1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kdc/tgs_policy.c */ 3 /* 4 * Copyright (C) 2012 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "k5-int.h" 34 #include "kdc_util.h" 35 36 /* 37 * Routines that validate a TGS request; checks a lot of things. :-) 38 * 39 * Returns a Kerberos protocol error number, which is _not_ the same 40 * as a com_err error number! 41 */ 42 43 struct tgsflagrule { 44 krb5_flags reqflags; /* Flag(s) in TGS-REQ */ 45 krb5_flags checkflag; /* Flags to check against */ 46 char *status; /* Status string */ 47 int err; /* Protocol error code */ 48 }; 49 50 /* Service principal TGS policy checking functions */ 51 typedef int (check_tgs_svc_pol_fn)(krb5_kdc_req *, krb5_db_entry *, 52 krb5_ticket *, krb5_timestamp, 53 const char **); 54 55 static check_tgs_svc_pol_fn check_tgs_svc_deny_opts; 56 static check_tgs_svc_pol_fn check_tgs_svc_deny_all; 57 static check_tgs_svc_pol_fn check_tgs_svc_reqd_flags; 58 static check_tgs_svc_pol_fn check_tgs_svc_time; 59 60 static check_tgs_svc_pol_fn * const svc_pol_fns[] = { 61 check_tgs_svc_deny_opts, check_tgs_svc_deny_all, check_tgs_svc_reqd_flags, 62 check_tgs_svc_time 63 }; 64 65 static const struct tgsflagrule tgsflagrules[] = { 66 { KDC_OPT_FORWARDED, TKT_FLG_FORWARDABLE, 67 "TGT NOT FORWARDABLE", KRB5KDC_ERR_BADOPTION }, 68 { KDC_OPT_PROXY, TKT_FLG_PROXIABLE, 69 "TGT NOT PROXIABLE", KRB5KDC_ERR_BADOPTION }, 70 { (KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED), TKT_FLG_MAY_POSTDATE, 71 "TGT NOT POSTDATABLE", KRB5KDC_ERR_BADOPTION }, 72 { KDC_OPT_VALIDATE, TKT_FLG_INVALID, 73 "VALIDATE VALID TICKET", KRB5KDC_ERR_BADOPTION }, 74 { KDC_OPT_RENEW, TKT_FLG_RENEWABLE, 75 "TICKET NOT RENEWABLE", KRB5KDC_ERR_BADOPTION } 76 }; 77 78 /* 79 * Some TGS-REQ options require that the ticket have corresponding flags set. 80 */ 81 static krb5_error_code 82 check_tgs_opts(krb5_kdc_req *req, krb5_ticket *tkt, const char **status) 83 { 84 size_t i; 85 size_t nrules = sizeof(tgsflagrules) / sizeof(tgsflagrules[0]); 86 const struct tgsflagrule *r; 87 88 for (i = 0; i < nrules; i++) { 89 r = &tgsflagrules[i]; 90 if (r->reqflags & req->kdc_options) { 91 if (!(r->checkflag & tkt->enc_part2->flags)) { 92 *status = r->status; 93 return r->err; 94 } 95 } 96 } 97 98 if (isflagset(tkt->enc_part2->flags, TKT_FLG_INVALID) && 99 !isflagset(req->kdc_options, KDC_OPT_VALIDATE)) { 100 *status = "TICKET NOT VALID"; 101 return KRB5KRB_AP_ERR_TKT_NYV; 102 } 103 104 return 0; 105 } 106 107 static const struct tgsflagrule svcdenyrules[] = { 108 { KDC_OPT_RENEWABLE, KRB5_KDB_DISALLOW_RENEWABLE, 109 "NON-RENEWABLE TICKET", KRB5KDC_ERR_POLICY }, 110 { KDC_OPT_ALLOW_POSTDATE, KRB5_KDB_DISALLOW_POSTDATED, 111 "NON-POSTDATABLE TICKET", KRB5KDC_ERR_CANNOT_POSTDATE }, 112 { KDC_OPT_ENC_TKT_IN_SKEY, KRB5_KDB_DISALLOW_DUP_SKEY, 113 "DUP_SKEY DISALLOWED", KRB5KDC_ERR_POLICY } 114 }; 115 116 /* 117 * A service principal can forbid some TGS-REQ options. 118 */ 119 static krb5_error_code 120 check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry *server, 121 krb5_ticket *tkt, krb5_timestamp kdc_time, 122 const char **status) 123 { 124 size_t i; 125 size_t nrules = sizeof(svcdenyrules) / sizeof(svcdenyrules[0]); 126 const struct tgsflagrule *r; 127 128 for (i = 0; i < nrules; i++) { 129 r = &svcdenyrules[i]; 130 if (!(r->reqflags & req->kdc_options)) 131 continue; 132 if (r->checkflag & server->attributes) { 133 *status = r->status; 134 return r->err; 135 } 136 } 137 return 0; 138 } 139 140 /* 141 * A service principal can deny all TGS-REQs for it. 142 */ 143 static krb5_error_code 144 check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry *server, 145 krb5_ticket *tkt, krb5_timestamp kdc_time, 146 const char **status) 147 { 148 if (server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) { 149 *status = "SERVER LOCKED OUT"; 150 return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 151 } 152 if ((server->attributes & KRB5_KDB_DISALLOW_SVR) && 153 !(req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)) { 154 *status = "SERVER NOT ALLOWED"; 155 return KRB5KDC_ERR_MUST_USE_USER2USER; 156 } 157 if (server->attributes & KRB5_KDB_DISALLOW_TGT_BASED) { 158 if (krb5_is_tgs_principal(tkt->server)) { 159 *status = "TGT BASED NOT ALLOWED"; 160 return KRB5KDC_ERR_POLICY; 161 } 162 } 163 return 0; 164 } 165 166 /* 167 * A service principal can require certain TGT flags. 168 */ 169 static krb5_error_code 170 check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry *server, 171 krb5_ticket *tkt, 172 krb5_timestamp kdc_time, const char **status) 173 { 174 if (server->attributes & KRB5_KDB_REQUIRES_HW_AUTH) { 175 if (!(tkt->enc_part2->flags & TKT_FLG_HW_AUTH)) { 176 *status = "NO HW PREAUTH"; 177 return KRB5KRB_ERR_GENERIC; 178 } 179 } 180 if (server->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) { 181 if (!(tkt->enc_part2->flags & TKT_FLG_PRE_AUTH)) { 182 *status = "NO PREAUTH"; 183 return KRB5KRB_ERR_GENERIC; 184 } 185 } 186 return 0; 187 } 188 189 static krb5_error_code 190 check_tgs_svc_time(krb5_kdc_req *req, krb5_db_entry *server, krb5_ticket *tkt, 191 krb5_timestamp kdc_time, const char **status) 192 { 193 if (server->expiration && ts_after(kdc_time, server->expiration)) { 194 *status = "SERVICE EXPIRED"; 195 return KRB5KDC_ERR_SERVICE_EXP; 196 } 197 return 0; 198 } 199 200 static krb5_error_code 201 check_tgs_svc_policy(krb5_kdc_req *req, krb5_db_entry *server, 202 krb5_ticket *tkt, krb5_timestamp kdc_time, 203 const char **status) 204 { 205 int errcode; 206 size_t i; 207 size_t nfns = sizeof(svc_pol_fns) / sizeof(svc_pol_fns[0]); 208 209 for (i = 0; i < nfns; i++) { 210 errcode = svc_pol_fns[i](req, server, tkt, kdc_time, status); 211 if (errcode != 0) 212 return errcode; 213 } 214 return 0; 215 } 216 217 /* 218 * Check header ticket timestamps against the current time. 219 */ 220 static krb5_error_code 221 check_tgs_times(krb5_kdc_req *req, krb5_ticket_times *times, 222 krb5_timestamp kdc_time, const char **status) 223 { 224 krb5_timestamp starttime; 225 226 /* For validating a postdated ticket, check the start time vs. the 227 KDC time. */ 228 if (req->kdc_options & KDC_OPT_VALIDATE) { 229 starttime = times->starttime ? times->starttime : times->authtime; 230 if (ts_after(starttime, kdc_time)) { 231 *status = "NOT_YET_VALID"; 232 return KRB5KRB_AP_ERR_TKT_NYV; 233 } 234 } 235 /* 236 * Check the renew_till time. The endtime was already 237 * been checked in the initial authentication check. 238 */ 239 if ((req->kdc_options & KDC_OPT_RENEW) && 240 ts_after(kdc_time, times->renew_till)) { 241 *status = "TKT_EXPIRED"; 242 return KRB5KRB_AP_ERR_TKT_EXPIRED; 243 } 244 return 0; 245 } 246 247 /* Check for local user tickets issued by foreign realms. This check is 248 * skipped for S4U2Self requests. */ 249 static krb5_error_code 250 check_tgs_lineage(krb5_db_entry *server, krb5_ticket *tkt, 251 krb5_boolean is_crossrealm, const char **status) 252 { 253 if (is_crossrealm && data_eq(tkt->enc_part2->client->realm, 254 server->princ->realm)) { 255 *status = "INVALID LINEAGE"; 256 return KRB5KDC_ERR_POLICY; 257 } 258 return 0; 259 } 260 261 static krb5_error_code 262 check_tgs_s4u2self(kdc_realm_t *realm, krb5_kdc_req *req, 263 krb5_db_entry *server, krb5_ticket *tkt, krb5_pac pac, 264 krb5_timestamp kdc_time, 265 krb5_pa_s4u_x509_user *s4u_x509_user, krb5_db_entry *client, 266 krb5_boolean is_crossrealm, krb5_boolean is_referral, 267 const char **status, krb5_pa_data ***e_data) 268 { 269 krb5_context context = realm->realm_context; 270 krb5_db_entry empty_server = { 0 }; 271 272 /* If the server is local, check that the request is for self. */ 273 if (!is_referral && 274 !is_client_db_alias(context, server, tkt->enc_part2->client)) { 275 *status = "INVALID_S4U2SELF_REQUEST_SERVER_MISMATCH"; 276 return KRB5KRB_AP_ERR_BADMATCH; 277 } 278 279 /* S4U2Self requests must use options valid for AS requests. */ 280 if (req->kdc_options & AS_INVALID_OPTIONS) { 281 *status = "INVALID S4U2SELF OPTIONS"; 282 return KRB5KDC_ERR_BADOPTION; 283 } 284 285 /* 286 * Valid S4U2Self requests can occur in the following combinations: 287 * 288 * (1) local TGT, local user, local server 289 * (2) cross TGT, local user, issuing referral 290 * (3) cross TGT, non-local user, issuing referral 291 * (4) cross TGT, non-local user, local server 292 * 293 * The first case is for a single-realm S4U2Self scenario; the second, 294 * third, and fourth cases are for the initial, intermediate (if any), and 295 * final cross-realm requests in a multi-realm scenario. 296 */ 297 298 if (!is_crossrealm && is_referral) { 299 /* This could happen if the requesting server no longer exists, and we 300 * found a referral instead. Treat this as a server lookup failure. */ 301 *status = "LOOKING_UP_SERVER"; 302 return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 303 } 304 if (client != NULL && is_crossrealm && !is_referral) { 305 /* A local server should not need a cross-realm TGT to impersonate 306 * a local principal. */ 307 *status = "NOT_CROSS_REALM_REQUEST"; 308 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error */ 309 } 310 if (client == NULL && !is_crossrealm) { 311 /* 312 * The server is asking to impersonate a principal from another realm, 313 * using a local TGT. It should instead ask that principal's realm and 314 * follow referrals back to us. 315 */ 316 *status = "S4U2SELF_CLIENT_NOT_OURS"; 317 return KRB5KDC_ERR_POLICY; /* match Windows error */ 318 } 319 if (client == NULL && s4u_x509_user->user_id.user->length == 0) { 320 /* 321 * Only a KDC in the client realm can handle a certificate-only 322 * S4U2Self request. Other KDCs require a principal name and ignore 323 * the subject-certificate field. 324 */ 325 *status = "INVALID_XREALM_S4U2SELF_REQUEST"; 326 return KRB5KDC_ERR_POLICY; /* match Windows error */ 327 } 328 329 /* The header ticket PAC must be present. */ 330 if (pac == NULL) { 331 *status = "S4U2SELF_NO_PAC"; 332 return KRB5KDC_ERR_TGT_REVOKED; 333 } 334 335 if (client != NULL) { 336 /* The header ticket PAC must be for the impersonator. */ 337 if (krb5_pac_verify(context, pac, tkt->enc_part2->times.authtime, 338 tkt->enc_part2->client, NULL, NULL) != 0) { 339 *status = "S4U2SELF_LOCAL_PAC_CLIENT"; 340 return KRB5KDC_ERR_BADOPTION; 341 } 342 343 /* Validate the client policy. Use an empty server principal to bypass 344 * server policy checks. */ 345 return validate_as_request(realm, req, client, &empty_server, kdc_time, 346 status, e_data); 347 } else { 348 /* The header ticket PAC must be for the subject, with realm. */ 349 if (krb5_pac_verify_ext(context, pac, tkt->enc_part2->times.authtime, 350 s4u_x509_user->user_id.user, NULL, NULL, 351 TRUE) != 0) { 352 *status = "S4U2SELF_FOREIGN_PAC_CLIENT"; 353 return KRB5KDC_ERR_BADOPTION; 354 } 355 } 356 357 return 0; 358 } 359 360 /* 361 * Validate pac as an S4U2Proxy subject PAC contained within a cross-realm TGT. 362 * If target_server is non-null, verify that it matches the PAC proxy target. 363 * Return 0 on success, non-zero on failure. 364 */ 365 static int 366 verify_deleg_pac(krb5_context context, krb5_pac pac, 367 krb5_enc_tkt_part *enc_tkt, 368 krb5_const_principal target_server) 369 { 370 krb5_timestamp pac_authtime; 371 krb5_data deleg_buf = empty_data(); 372 krb5_principal princ = NULL; 373 struct pac_s4u_delegation_info *di = NULL; 374 char *client_str = NULL, *target_str = NULL; 375 const char *last_transited; 376 int result = -1; 377 378 /* Make sure the PAC client string can be parsed as a principal with 379 * realm. */ 380 if (get_pac_princ_with_realm(context, pac, &princ, &pac_authtime) != 0) 381 goto cleanup; 382 if (pac_authtime != enc_tkt->times.authtime) 383 goto cleanup; 384 385 if (krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, 386 &deleg_buf) != 0) 387 goto cleanup; 388 389 if (ndr_dec_delegation_info(&deleg_buf, &di) != 0) 390 goto cleanup; 391 392 if (target_server != NULL) { 393 if (krb5_unparse_name_flags(context, target_server, 394 KRB5_PRINCIPAL_UNPARSE_DISPLAY | 395 KRB5_PRINCIPAL_UNPARSE_NO_REALM, 396 &target_str) != 0) 397 goto cleanup; 398 if (strcmp(target_str, di->proxy_target) != 0) 399 goto cleanup; 400 } 401 402 /* Check that the most recently added PAC transited service matches the 403 * requesting impersonator. */ 404 if (di->transited_services_length == 0) 405 goto cleanup; 406 if (krb5_unparse_name(context, enc_tkt->client, &client_str) != 0) 407 goto cleanup; 408 last_transited = di->transited_services[di->transited_services_length - 1]; 409 if (strcmp(last_transited, client_str) != 0) 410 goto cleanup; 411 412 result = 0; 413 414 cleanup: 415 free(target_str); 416 free(client_str); 417 ndr_free_delegation_info(di); 418 krb5_free_principal(context, princ); 419 krb5_free_data_contents(context, &deleg_buf); 420 return result; 421 } 422 423 static krb5_error_code 424 check_tgs_s4u2proxy(krb5_context context, krb5_kdc_req *req, 425 krb5_db_entry *server, krb5_ticket *tkt, krb5_pac pac, 426 const krb5_ticket *stkt, krb5_pac stkt_pac, 427 krb5_db_entry *stkt_server, krb5_boolean is_crossrealm, 428 krb5_boolean is_referral, const char **status) 429 { 430 /* A forwardable second ticket must be present in the request. */ 431 if (stkt == NULL) { 432 *status = "NO_2ND_TKT"; 433 return KRB5KDC_ERR_BADOPTION; 434 } 435 if (!(stkt->enc_part2->flags & TKT_FLG_FORWARDABLE)) { 436 *status = "EVIDENCE_TKT_NOT_FORWARDABLE"; 437 return KRB5KDC_ERR_BADOPTION; 438 } 439 440 /* Constrained delegation is mutually exclusive with renew/forward/etc. 441 * (and therefore requires the header ticket to be a TGT). */ 442 if (req->kdc_options & (NON_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) { 443 *status = "INVALID_S4U2PROXY_OPTIONS"; 444 return KRB5KDC_ERR_BADOPTION; 445 } 446 447 /* Can't get a TGT (otherwise it would be unconstrained delegation). */ 448 if (krb5_is_tgs_principal(req->server)) { 449 *status = "NOT_ALLOWED_TO_DELEGATE"; 450 return KRB5KDC_ERR_POLICY; 451 } 452 453 /* The header ticket PAC must be present and for the impersonator. */ 454 if (pac == NULL) { 455 *status = "S4U2PROXY_NO_HEADER_PAC"; 456 return KRB5KDC_ERR_TGT_REVOKED; 457 } 458 if (krb5_pac_verify(context, pac, tkt->enc_part2->times.authtime, 459 tkt->enc_part2->client, NULL, NULL) != 0) { 460 *status = "S4U2PROXY_HEADER_PAC"; 461 return KRB5KDC_ERR_BADOPTION; 462 } 463 464 /* 465 * An S4U2Proxy request must be an initial request to the impersonator's 466 * realm (possibly for a target resource in the same realm), or a final 467 * cross-realm RBCD request to the resource realm. Intermediate 468 * referral-chasing requests do not use the CNAME-IN-ADDL-TKT flag. 469 */ 470 471 if (stkt_pac == NULL) { 472 *status = "S4U2PROXY_NO_STKT_PAC"; 473 return KRB5KRB_AP_ERR_MODIFIED; 474 } 475 if (!is_crossrealm) { 476 /* For an initial or same-realm request, the second ticket server and 477 * header ticket client must be the same principal. */ 478 if (!is_client_db_alias(context, stkt_server, 479 tkt->enc_part2->client)) { 480 *status = "EVIDENCE_TICKET_MISMATCH"; 481 return KRB5KDC_ERR_SERVER_NOMATCH; 482 } 483 484 /* The second ticket client and PAC client are the subject, and must 485 * match. */ 486 if (krb5_pac_verify(context, stkt_pac, stkt->enc_part2->times.authtime, 487 stkt->enc_part2->client, NULL, NULL) != 0) { 488 *status = "S4U2PROXY_LOCAL_STKT_PAC"; 489 return KRB5KDC_ERR_BADOPTION; 490 } 491 492 } else { 493 494 /* 495 * For a cross-realm request, the second ticket must be a referral TGT 496 * to our realm with the impersonator as client. The target server 497 * must also be local, so we must not be issuing a referral. 498 */ 499 if (is_referral || !is_cross_tgs_principal(stkt_server->princ) || 500 !data_eq(stkt_server->princ->data[1], server->princ->realm) || 501 !krb5_principal_compare(context, stkt->enc_part2->client, 502 tkt->enc_part2->client)) { 503 *status = "XREALM_EVIDENCE_TICKET_MISMATCH"; 504 return KRB5KDC_ERR_BADOPTION; 505 } 506 507 /* The second ticket PAC must be present and for the impersonated 508 * client, with delegation info. */ 509 if (stkt_pac == NULL || 510 verify_deleg_pac(context, stkt_pac, stkt->enc_part2, 511 req->server) != 0) { 512 *status = "S4U2PROXY_CROSS_STKT_PAC"; 513 return KRB5KDC_ERR_BADOPTION; 514 } 515 } 516 517 return 0; 518 } 519 520 /* Check the KDB policy for a final RBCD request. */ 521 static krb5_error_code 522 check_s4u2proxy_policy(krb5_context context, krb5_kdc_req *req, 523 krb5_principal desired_client, 524 krb5_principal impersonator_name, 525 krb5_db_entry *impersonator, krb5_pac impersonator_pac, 526 krb5_principal resource_name, krb5_db_entry *resource, 527 krb5_boolean is_crossrealm, krb5_boolean is_referral, 528 const char **status) 529 { 530 krb5_error_code ret; 531 krb5_boolean support_rbcd, policy_denial = FALSE; 532 533 /* Check if the client supports resource-based constrained delegation. */ 534 ret = kdc_get_pa_pac_rbcd(context, req->padata, &support_rbcd); 535 if (ret) 536 return ret; 537 538 if (is_referral) { 539 if (!support_rbcd) { 540 /* The client must support RBCD for a referral to be useful. */ 541 *status = "UNSUPPORTED_S4U2PROXY_REQUEST"; 542 return KRB5KDC_ERR_BADOPTION; 543 } 544 /* Policy will be checked in the resource realm. */ 545 return 0; 546 } 547 548 /* Try resource-based authorization if the client supports RBCD. */ 549 if (support_rbcd) { 550 ret = krb5_db_allowed_to_delegate_from(context, desired_client, 551 impersonator_name, 552 impersonator_pac, resource); 553 if (ret == KRB5KDC_ERR_BADOPTION) 554 policy_denial = TRUE; 555 else if (ret != KRB5_PLUGIN_OP_NOTSUPP) 556 return ret; 557 } 558 559 /* Try traditional authorization if the requestor is in this realm. */ 560 if (!is_crossrealm) { 561 ret = krb5_db_check_allowed_to_delegate(context, desired_client, 562 impersonator, resource_name); 563 if (ret == KRB5KDC_ERR_BADOPTION) 564 policy_denial = TRUE; 565 else if (ret != KRB5_PLUGIN_OP_NOTSUPP) 566 return ret; 567 } 568 569 *status = policy_denial ? "NOT_ALLOWED_TO_DELEGATE" : 570 "UNSUPPORTED_S4U2PROXY_REQUEST"; 571 return KRB5KDC_ERR_BADOPTION; 572 } 573 574 static krb5_error_code 575 check_tgs_u2u(krb5_context context, krb5_kdc_req *req, const krb5_ticket *stkt, 576 krb5_db_entry *server, const char **status) 577 { 578 /* A second ticket must be present in the request. */ 579 if (stkt == NULL) { 580 *status = "NO_2ND_TKT"; 581 return KRB5KDC_ERR_BADOPTION; 582 } 583 584 /* The second ticket must be a TGT to the server realm. */ 585 if (!is_local_tgs_principal(stkt->server) || 586 !data_eq(stkt->server->data[1], server->princ->realm)) { 587 *status = "2ND_TKT_NOT_TGS"; 588 return KRB5KDC_ERR_POLICY; 589 } 590 591 /* The second ticket client must match the requested server. */ 592 if (!is_client_db_alias(context, server, stkt->enc_part2->client)) { 593 *status = "2ND_TKT_MISMATCH"; 594 return KRB5KDC_ERR_SERVER_NOMATCH; 595 } 596 597 return 0; 598 } 599 600 /* Validate the PAC of a non-S4U TGS request, if one is present. */ 601 static krb5_error_code 602 check_normal_tgs_pac(krb5_context context, krb5_enc_tkt_part *enc_tkt, 603 krb5_pac pac, krb5_db_entry *server, 604 krb5_boolean is_crossrealm, const char **status) 605 { 606 /* We don't require a PAC for regular TGS requests. */ 607 if (pac == NULL) 608 return 0; 609 610 /* For most requests the header ticket PAC will be for the ticket 611 * client. */ 612 if (krb5_pac_verify(context, pac, enc_tkt->times.authtime, enc_tkt->client, 613 NULL, NULL) == 0) 614 return 0; 615 616 /* For intermediate RBCD requests the header ticket PAC will be for the 617 * impersonated client. */ 618 if (is_crossrealm && is_cross_tgs_principal(server->princ) && 619 verify_deleg_pac(context, pac, enc_tkt, NULL) == 0) 620 return 0; 621 622 *status = "HEADER_PAC"; 623 return KRB5KDC_ERR_BADOPTION; 624 } 625 626 /* 627 * Some TGS-REQ options allow for a non-TGS principal in the ticket. Do some 628 * checks that are peculiar to these cases. (e.g., ticket service principal 629 * matches requested service principal) 630 */ 631 static krb5_error_code 632 check_tgs_nontgt(krb5_context context, krb5_kdc_req *req, krb5_ticket *tkt, 633 const char **status) 634 { 635 if (!krb5_principal_compare(context, tkt->server, req->server)) { 636 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; 637 return KRB5KDC_ERR_SERVER_NOMATCH; 638 } 639 /* Cannot proxy ticket granting tickets. */ 640 if ((req->kdc_options & KDC_OPT_PROXY) && 641 krb5_is_tgs_principal(req->server)) { 642 *status = "CAN'T PROXY TGT"; 643 return KRB5KDC_ERR_BADOPTION; 644 } 645 return 0; 646 } 647 648 /* 649 * Do some checks for a normal TGS-REQ (where the ticket service must be a TGS 650 * principal). 651 */ 652 static krb5_error_code 653 check_tgs_tgt(krb5_kdc_req *req, krb5_ticket *tkt, const char **status) 654 { 655 /* Make sure it's a TGS principal. */ 656 if (!krb5_is_tgs_principal(tkt->server)) { 657 *status = "BAD TGS SERVER NAME"; 658 return KRB5KRB_AP_ERR_NOT_US; 659 } 660 /* TGS principal second component must match service realm. */ 661 if (!data_eq(tkt->server->data[1], req->server->realm)) { 662 *status = "BAD TGS SERVER INSTANCE"; 663 return KRB5KRB_AP_ERR_NOT_US; 664 } 665 return 0; 666 } 667 668 krb5_error_code 669 check_tgs_constraints(kdc_realm_t *realm, krb5_kdc_req *request, 670 krb5_db_entry *server, krb5_ticket *ticket, krb5_pac pac, 671 const krb5_ticket *stkt, krb5_pac stkt_pac, 672 krb5_db_entry *stkt_server, krb5_timestamp kdc_time, 673 krb5_pa_s4u_x509_user *s4u_x509_user, 674 krb5_db_entry *s4u2self_client, 675 krb5_boolean is_crossrealm, krb5_boolean is_referral, 676 const char **status, krb5_pa_data ***e_data) 677 { 678 krb5_context context = realm->realm_context; 679 int errcode; 680 681 /* Depends only on request and ticket. */ 682 errcode = check_tgs_opts(request, ticket, status); 683 if (errcode != 0) 684 return errcode; 685 686 /* Depends only on request, ticket times, and current time. */ 687 errcode = check_tgs_times(request, &ticket->enc_part2->times, kdc_time, 688 status); 689 if (errcode != 0) 690 return errcode; 691 692 if (request->kdc_options & NON_TGT_OPTION) 693 errcode = check_tgs_nontgt(context, request, ticket, status); 694 else 695 errcode = check_tgs_tgt(request, ticket, status); 696 if (errcode != 0) 697 return errcode; 698 699 if (s4u_x509_user != NULL) { 700 errcode = check_tgs_s4u2self(realm, request, server, ticket, pac, 701 kdc_time, s4u_x509_user, s4u2self_client, 702 is_crossrealm, is_referral, status, 703 e_data); 704 } else { 705 errcode = check_tgs_lineage(server, ticket, is_crossrealm, status); 706 } 707 if (errcode != 0) 708 return errcode; 709 710 if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { 711 errcode = check_tgs_u2u(context, request, stkt, server, status); 712 if (errcode != 0) 713 return errcode; 714 } 715 716 if (request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) { 717 errcode = check_tgs_s4u2proxy(context, request, server, ticket, pac, 718 stkt, stkt_pac, stkt_server, 719 is_crossrealm, is_referral, status); 720 if (errcode != 0) 721 return errcode; 722 } else if (s4u_x509_user == NULL) { 723 errcode = check_normal_tgs_pac(context, ticket->enc_part2, pac, server, 724 is_crossrealm, status); 725 if (errcode != 0) 726 return errcode; 727 } 728 729 return 0; 730 } 731 732 krb5_error_code 733 check_tgs_policy(kdc_realm_t *realm, krb5_kdc_req *request, 734 krb5_db_entry *server, krb5_ticket *ticket, 735 krb5_pac pac, const krb5_ticket *stkt, krb5_pac stkt_pac, 736 krb5_principal stkt_pac_client, krb5_db_entry *stkt_server, 737 krb5_timestamp kdc_time, krb5_boolean is_crossrealm, 738 krb5_boolean is_referral, const char **status, 739 krb5_pa_data ***e_data) 740 { 741 krb5_context context = realm->realm_context; 742 int errcode; 743 krb5_error_code ret; 744 krb5_principal desired_client; 745 746 errcode = check_tgs_svc_policy(request, server, ticket, kdc_time, status); 747 if (errcode != 0) 748 return errcode; 749 750 if (request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) { 751 desired_client = (stkt_pac_client != NULL) ? stkt_pac_client : 752 stkt->enc_part2->client; 753 errcode = check_s4u2proxy_policy(context, request, desired_client, 754 ticket->enc_part2->client, 755 stkt_server, pac, request->server, 756 server, is_crossrealm, is_referral, 757 status); 758 if (errcode != 0) 759 return errcode; 760 } 761 762 if (check_anon(realm, ticket->enc_part2->client, request->server) != 0) { 763 *status = "ANONYMOUS NOT ALLOWED"; 764 return KRB5KDC_ERR_POLICY; 765 } 766 767 /* Perform KDB module policy checks. */ 768 ret = krb5_db_check_policy_tgs(context, request, server, ticket, status, 769 e_data); 770 return (ret == KRB5_PLUGIN_OP_NOTSUPP) ? 0 : ret; 771 } 772