-: 0:Source:pkinit_clnt.c -: 0:Graph:/var/tsitkova/Sources/v10/trunk/src/plugins/preauth/pkinit/pkinit_clnt.so.gcno -: 0:Data:/var/tsitkova/Sources/v10/trunk/src/plugins/preauth/pkinit/pkinit_clnt.so.gcda -: 0:Runs:291 -: 0:Programs:1 -: 1:/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -: 2:/* -: 3: * COPYRIGHT (C) 2006,2007 -: 4: * THE REGENTS OF THE UNIVERSITY OF MICHIGAN -: 5: * ALL RIGHTS RESERVED -: 6: * -: 7: * Permission is granted to use, copy, create derivative works -: 8: * and redistribute this software and such derivative works -: 9: * for any purpose, so long as the name of The University of -: 10: * Michigan is not used in any advertising or publicity -: 11: * pertaining to the use of distribution of this software -: 12: * without specific, written prior authorization. If the -: 13: * above copyright notice or any other identification of the -: 14: * University of Michigan is included in any copy of any -: 15: * portion of this software, then the disclaimer below must -: 16: * also be included. -: 17: * -: 18: * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION -: 19: * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY -: 20: * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF -: 21: * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING -: 22: * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF -: 23: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE -: 24: * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE -: 25: * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR -: 26: * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING -: 27: * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN -: 28: * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF -: 29: * SUCH DAMAGES. -: 30: */ -: 31: -: 32:#include -: 33:#include -: 34:#include -: 35:#include -: 36:#include -: 37:#include -: 38:#include -: 39:#include -: 40:#include -: 41: -: 42:#include "pkinit.h" -: 43: -: 44:/* -: 45: * It is anticipated that all the special checks currently -: 46: * required when talking to a Longhorn server will go away -: 47: * by the time it is officially released and all references -: 48: * to the longhorn global can be removed and any code -: 49: * #ifdef'd with LONGHORN_BETA_COMPAT can be removed. -: 50: * -: 51: * Current testing (20070620) is against a patched Beta 3 -: 52: * version of Longhorn. Most, if not all, problems should -: 53: * be fixed in SP1 of Longhorn. -: 54: */ -: 55:int longhorn = 0; /* Talking to a Longhorn server? */ -: 56: -: 57:/** -: 58: * Return true if we should use ContentInfo rather than SignedData. This -: 59: * happens if we are talking to what might be an old (pre-6112) MIT KDC and -: 60: * we're using anonymous. -: 61: */ -: 62:static int 2: 63:use_content_info(krb5_context context, pkinit_req_context req, -: 64: krb5_principal client) -: 65:{ 2: 66: if (req->rfc6112_kdc) 2: 67: return 0; #####: 68: if (krb5_principal_compare_any_realm(context, client, -: 69: krb5_anonymous_principal())) #####: 70: return 1; #####: 71: return 0; -: 72:} -: 73: -: 74:static krb5_error_code -: 75:pkinit_as_req_create(krb5_context context, pkinit_context plgctx, -: 76: pkinit_req_context reqctx, krb5_timestamp ctsec, -: 77: krb5_int32 cusec, krb5_ui_4 nonce, -: 78: const krb5_checksum *cksum, -: 79: krb5_principal client, krb5_principal server, -: 80: krb5_data **as_req); -: 81: -: 82:static krb5_error_code -: 83:pkinit_as_rep_parse(krb5_context context, pkinit_context plgctx, -: 84: pkinit_req_context reqctx, krb5_preauthtype pa_type, -: 85: krb5_kdc_req *request, const krb5_data *as_rep, -: 86: krb5_keyblock *key_block, krb5_enctype etype, krb5_data *); -: 87: -: 88:static void pkinit_client_plugin_fini(krb5_context context, -: 89: krb5_clpreauth_moddata moddata); -: 90: -: 91:static krb5_error_code 2: 92:pa_pkinit_gen_req(krb5_context context, -: 93: pkinit_context plgctx, -: 94: pkinit_req_context reqctx, -: 95: krb5_kdc_req * request, -: 96: krb5_pa_data * in_padata, -: 97: krb5_pa_data *** out_padata, -: 98: krb5_prompter_fct prompter, -: 99: void *prompter_data, -: 100: krb5_get_init_creds_opt *gic_opt) -: 101:{ -: 102: 2: 103: krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 2: 104: krb5_data *out_data = NULL; 2: 105: krb5_timestamp ctsec = 0; 2: 106: krb5_int32 cusec = 0; 2: 107: krb5_ui_4 nonce = 0; -: 108: krb5_checksum cksum; 2: 109: krb5_data *der_req = NULL; 2: 110: krb5_pa_data **return_pa_data = NULL; -: 111: 2: 112: cksum.contents = NULL; 2: 113: reqctx->pa_type = in_padata->pa_type; -: 114: 2: 115: pkiDebug("kdc_options = 0x%x till = %d\n", -: 116: request->kdc_options, request->till); -: 117: /* If we don't have a client, we're done */ 2: 118: if (request->client == NULL) { #####: 119: pkiDebug("No request->client; aborting PKINIT\n"); #####: 120: return KRB5KDC_ERR_PREAUTH_FAILED; -: 121: } -: 122: 2: 123: retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx, -: 124: reqctx->idctx, request->server); 2: 125: if (retval) { #####: 126: pkiDebug("pkinit_get_kdc_cert returned %d\n", retval); #####: 127: goto cleanup; -: 128: } -: 129: -: 130: /* checksum of the encoded KDC-REQ-BODY */ 2: 131: retval = k5int_encode_krb5_kdc_req_body(request, &der_req); 2: 132: if (retval) { #####: 133: pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); #####: 134: goto cleanup; -: 135: } -: 136: 2: 137: retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, -: 138: der_req, &cksum); 2: 139: if (retval) #####: 140: goto cleanup; -: 141:#ifdef DEBUG_CKSUM -: 142: pkiDebug("calculating checksum on buf size (%d)\n", der_req->length); -: 143: print_buffer(der_req->data, der_req->length); -: 144:#endif -: 145: 2: 146: retval = krb5_us_timeofday(context, &ctsec, &cusec); 2: 147: if (retval) #####: 148: goto cleanup; -: 149: -: 150: /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the -: 151: * same as in the AS_REQ. However, if we pick a different nonce, then we -: 152: * need to remember that info when AS_REP is returned. I'm choosing to -: 153: * reuse the AS_REQ nonce. -: 154: */ 2: 155: nonce = request->nonce; -: 156: 2: 157: retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec, -: 158: nonce, &cksum, request->client, request->server, &out_data); 2: 159: if (retval || !out_data->length) { #####: 160: pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", -: 161: (int) retval); #####: 162: goto cleanup; -: 163: } 2: 164: retval = ENOMEM; -: 165: /* -: 166: * The most we'll return is two pa_data, normally just one. -: 167: * We need to make room for the NULL terminator. -: 168: */ 2: 169: return_pa_data = malloc(3 * sizeof(krb5_pa_data *)); 2: 170: if (return_pa_data == NULL) #####: 171: goto cleanup; -: 172: 2: 173: return_pa_data[1] = NULL; /* in case of an early trip to cleanup */ 2: 174: return_pa_data[2] = NULL; /* Terminate the list */ -: 175: 2: 176: return_pa_data[0] = malloc(sizeof(krb5_pa_data)); 2: 177: if (return_pa_data[0] == NULL) #####: 178: goto cleanup; -: 179: 2: 180: return_pa_data[1] = malloc(sizeof(krb5_pa_data)); 2: 181: if (return_pa_data[1] == NULL) #####: 182: goto cleanup; -: 183: 2: 184: return_pa_data[0]->magic = KV5M_PA_DATA; -: 185: 2: 186: if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD) #####: 187: return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD; -: 188: else 2: 189: return_pa_data[0]->pa_type = in_padata->pa_type; 2: 190: return_pa_data[0]->length = out_data->length; 2: 191: return_pa_data[0]->contents = (krb5_octet *) out_data->data; -: 192: -: 193: /* -: 194: * LH Beta 3 requires the extra pa-data, even for RFC requests, -: 195: * in order to get the Checksum rather than a Nonce in the reply. -: 196: * This can be removed when LH SP1 is released. -: 197: */ 6: 198: if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD 4: 199: && reqctx->opts->win2k_require_cksum) || (longhorn == 1)) { #####: 200: return_pa_data[1]->pa_type = 132; #####: 201: return_pa_data[1]->length = 0; #####: 202: return_pa_data[1]->contents = NULL; -: 203: } else { 2: 204: free(return_pa_data[1]); 2: 205: return_pa_data[1] = NULL; /* Move the list terminator */ -: 206: } 2: 207: *out_padata = return_pa_data; 2: 208: retval = 0; -: 209: -: 210:cleanup: 2: 211: if (der_req != NULL) 2: 212: krb5_free_data(context, der_req); 2: 213: free(out_data); -: 214: 2: 215: if (retval) { #####: 216: if (return_pa_data) { #####: 217: free(return_pa_data[0]); #####: 218: free(return_pa_data[1]); #####: 219: free(return_pa_data); -: 220: } #####: 221: if (out_data) { #####: 222: free(out_data->data); #####: 223: free(out_data); -: 224: } -: 225: } 2: 226: return retval; -: 227:} -: 228: -: 229:static krb5_error_code 2: 230:pkinit_as_req_create(krb5_context context, -: 231: pkinit_context plgctx, -: 232: pkinit_req_context reqctx, -: 233: krb5_timestamp ctsec, -: 234: krb5_int32 cusec, -: 235: krb5_ui_4 nonce, -: 236: const krb5_checksum * cksum, -: 237: krb5_principal client, -: 238: krb5_principal server, -: 239: krb5_data ** as_req) -: 240:{ 2: 241: krb5_error_code retval = ENOMEM; 2: 242: krb5_subject_pk_info *info = NULL; 2: 243: krb5_data *coded_auth_pack = NULL; 2: 244: krb5_auth_pack *auth_pack = NULL; 2: 245: krb5_pa_pk_as_req *req = NULL; 2: 246: krb5_auth_pack_draft9 *auth_pack9 = NULL; 2: 247: krb5_pa_pk_as_req_draft9 *req9 = NULL; 2: 248: int protocol = reqctx->opts->dh_or_rsa; -: 249: 2: 250: pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type); -: 251: -: 252: /* Create the authpack */ 2: 253: switch((int)reqctx->pa_type) { -: 254: case KRB5_PADATA_PK_AS_REQ_OLD: #####: 255: protocol = RSA_PROTOCOL; #####: 256: init_krb5_auth_pack_draft9(&auth_pack9); #####: 257: if (auth_pack9 == NULL) #####: 258: goto cleanup; #####: 259: auth_pack9->pkAuthenticator.ctime = ctsec; #####: 260: auth_pack9->pkAuthenticator.cusec = cusec; #####: 261: auth_pack9->pkAuthenticator.nonce = nonce; #####: 262: auth_pack9->pkAuthenticator.kdcName = server; #####: 263: auth_pack9->pkAuthenticator.kdcRealm.magic = 0; #####: 264: auth_pack9->pkAuthenticator.kdcRealm.data = -: 265: (unsigned char *)server->realm.data; #####: 266: auth_pack9->pkAuthenticator.kdcRealm.length = server->realm.length; #####: 267: free(cksum->contents); #####: 268: break; -: 269: case KRB5_PADATA_PK_AS_REQ: 2: 270: init_krb5_subject_pk_info(&info); 2: 271: if (info == NULL) #####: 272: goto cleanup; 2: 273: init_krb5_auth_pack(&auth_pack); 2: 274: if (auth_pack == NULL) #####: 275: goto cleanup; 2: 276: auth_pack->pkAuthenticator.ctime = ctsec; 2: 277: auth_pack->pkAuthenticator.cusec = cusec; 2: 278: auth_pack->pkAuthenticator.nonce = nonce; 2: 279: auth_pack->pkAuthenticator.paChecksum = *cksum; 2: 280: auth_pack->clientDHNonce.length = 0; 2: 281: auth_pack->clientPublicValue = info; 2: 282: auth_pack->supportedKDFs = (krb5_octet_data **) supported_kdf_alg_ids; -: 283: -: 284: /* add List of CMS algorithms */ 2: 285: retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx, -: 286: reqctx->cryptoctx, reqctx->idctx, 2: 287: &auth_pack->supportedCMSTypes); 2: 288: if (retval) #####: 289: goto cleanup; 2: 290: break; -: 291: default: #####: 292: pkiDebug("as_req: unrecognized pa_type = %d\n", -: 293: (int)reqctx->pa_type); #####: 294: retval = -1; #####: 295: goto cleanup; -: 296: } -: 297: 2: 298: switch(protocol) { -: 299: case DH_PROTOCOL: 2: 300: pkiDebug("as_req: DH key transport algorithm\n"); 2: 301: retval = pkinit_copy_krb5_octet_data(&info->algorithm.algorithm, &dh_oid); 2: 302: if (retval) { #####: 303: pkiDebug("failed to copy dh_oid\n"); #####: 304: goto cleanup; -: 305: } -: 306: -: 307: /* create client-side DH keys */ 6: 308: if ((retval = client_create_dh(context, plgctx->cryptoctx, 2: 309: reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size, 2: 310: &info->algorithm.parameters.data, 2: 311: &info->algorithm.parameters.length, 2: 312: &info->subjectPublicKey.data, 2: 313: &info->subjectPublicKey.length)) != 0) { #####: 314: pkiDebug("failed to create dh parameters\n"); #####: 315: goto cleanup; -: 316: } 2: 317: break; -: 318: case RSA_PROTOCOL: #####: 319: pkiDebug("as_req: RSA key transport algorithm\n"); #####: 320: switch((int)reqctx->pa_type) { -: 321: case KRB5_PADATA_PK_AS_REQ_OLD: #####: 322: auth_pack9->clientPublicValue = NULL; #####: 323: break; -: 324: case KRB5_PADATA_PK_AS_REQ: #####: 325: free_krb5_subject_pk_info(&info); #####: 326: auth_pack->clientPublicValue = NULL; -: 327: break; -: 328: } #####: 329: break; -: 330: default: #####: 331: pkiDebug("as_req: unknown key transport protocol %d\n", -: 332: protocol); #####: 333: retval = -1; #####: 334: goto cleanup; -: 335: } -: 336: -: 337: /* Encode the authpack */ 2: 338: switch((int)reqctx->pa_type) { -: 339: case KRB5_PADATA_PK_AS_REQ: 2: 340: retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack); 2: 341: break; -: 342: case KRB5_PADATA_PK_AS_REQ_OLD: #####: 343: retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9, -: 344: &coded_auth_pack); -: 345: break; -: 346: } 2: 347: if (retval) { #####: 348: pkiDebug("failed to encode the AuthPack %d\n", retval); #####: 349: goto cleanup; -: 350: } -: 351:#ifdef DEBUG_ASN1 -: 352: print_buffer_bin((unsigned char *)coded_auth_pack->data, -: 353: coded_auth_pack->length, -: 354: "/tmp/client_auth_pack"); -: 355:#endif -: 356: -: 357: /* create PKCS7 object from authpack */ 2: 358: switch((int)reqctx->pa_type) { -: 359: case KRB5_PADATA_PK_AS_REQ: 2: 360: init_krb5_pa_pk_as_req(&req); 2: 361: if (req == NULL) { #####: 362: retval = ENOMEM; #####: 363: goto cleanup; -: 364: } 2: 365: if (use_content_info(context, reqctx, client)) { #####: 366: retval = cms_contentinfo_create(context, plgctx->cryptoctx, -: 367: reqctx->cryptoctx, reqctx->idctx, -: 368: CMS_SIGN_CLIENT, (unsigned char *) #####: 369: coded_auth_pack->data, #####: 370: coded_auth_pack->length, #####: 371: &req->signedAuthPack.data, #####: 372: &req->signedAuthPack.length); -: 373: } else { 6: 374: retval = cms_signeddata_create(context, plgctx->cryptoctx, -: 375: reqctx->cryptoctx, reqctx->idctx, -: 376: CMS_SIGN_CLIENT, 1, -: 377: (unsigned char *) 2: 378: coded_auth_pack->data, 2: 379: coded_auth_pack->length, 2: 380: &req->signedAuthPack.data, 2: 381: &req->signedAuthPack.length); -: 382: } -: 383:#ifdef DEBUG_ASN1 -: 384: print_buffer_bin((unsigned char *)req->signedAuthPack.data, -: 385: req->signedAuthPack.length, -: 386: "/tmp/client_signed_data"); -: 387:#endif 2: 388: break; -: 389: case KRB5_PADATA_PK_AS_REQ_OLD: #####: 390: init_krb5_pa_pk_as_req_draft9(&req9); #####: 391: if (req9 == NULL) { #####: 392: retval = ENOMEM; #####: 393: goto cleanup; -: 394: } #####: 395: retval = cms_signeddata_create(context, plgctx->cryptoctx, -: 396: reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1, #####: 397: (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, #####: 398: &req9->signedAuthPack.data, &req9->signedAuthPack.length); -: 399: break; -: 400:#ifdef DEBUG_ASN1 -: 401: print_buffer_bin((unsigned char *)req9->signedAuthPack.data, -: 402: req9->signedAuthPack.length, -: 403: "/tmp/client_signed_data_draft9"); -: 404:#endif -: 405: } 2: 406: krb5_free_data(context, coded_auth_pack); 2: 407: if (retval) { #####: 408: pkiDebug("failed to create pkcs7 signed data\n"); #####: 409: goto cleanup; -: 410: } -: 411: -: 412: /* create a list of trusted CAs */ 2: 413: switch((int)reqctx->pa_type) { -: 414: case KRB5_PADATA_PK_AS_REQ: 2: 415: retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx, 2: 416: reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers); 2: 417: if (retval) #####: 418: goto cleanup; 4: 419: retval = create_issuerAndSerial(context, plgctx->cryptoctx, 2: 420: reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data, 2: 421: &req->kdcPkId.length); 2: 422: if (retval) #####: 423: goto cleanup; -: 424: -: 425: /* Encode the as-req */ 2: 426: retval = k5int_encode_krb5_pa_pk_as_req(req, as_req); 2: 427: break; -: 428: case KRB5_PADATA_PK_AS_REQ_OLD: -: 429:#if 0 -: 430: /* W2K3 KDC doesn't like this */ -: 431: retval = create_krb5_trustedCas(context, plgctx->cryptoctx, -: 432: reqctx->cryptoctx, reqctx->idctx, 1, &req9->trustedCertifiers); -: 433: if (retval) -: 434: goto cleanup; -: 435: -: 436:#endif #####: 437: retval = create_issuerAndSerial(context, plgctx->cryptoctx, #####: 438: reqctx->cryptoctx, reqctx->idctx, &req9->kdcCert.data, #####: 439: &req9->kdcCert.length); #####: 440: if (retval) #####: 441: goto cleanup; -: 442: /* Encode the as-req */ #####: 443: retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req); -: 444: break; -: 445: } -: 446:#ifdef DEBUG_ASN1 -: 447: if (!retval) -: 448: print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length, -: 449: "/tmp/client_as_req"); -: 450:#endif -: 451: -: 452:cleanup: 2: 453: switch((int)reqctx->pa_type) { -: 454: case KRB5_PADATA_PK_AS_REQ: 2: 455: auth_pack->supportedKDFs = NULL; /*alias to global constant*/ 2: 456: free_krb5_auth_pack(&auth_pack); 2: 457: free_krb5_pa_pk_as_req(&req); 2: 458: break; -: 459: case KRB5_PADATA_PK_AS_REQ_OLD: #####: 460: free_krb5_pa_pk_as_req_draft9(&req9); #####: 461: free(auth_pack9); -: 462: break; -: 463: } -: 464: -: 465: 2: 466: pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval); -: 467: 2: 468: return retval; -: 469:} -: 470: -: 471:static krb5_error_code 2: 472:pa_pkinit_parse_rep(krb5_context context, -: 473: pkinit_context plgctx, -: 474: pkinit_req_context reqctx, -: 475: krb5_kdc_req * request, -: 476: krb5_pa_data * in_padata, -: 477: krb5_enctype etype, -: 478: krb5_keyblock * as_key, -: 479: krb5_data *encoded_request) -: 480:{ 2: 481: krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 2: 482: krb5_data asRep = { 0, 0, NULL}; -: 483: -: 484: /* -: 485: * One way or the other - success or failure - no other PA systems can -: 486: * work if the server sent us a PKINIT reply, since only we know how to -: 487: * decrypt the key. -: 488: */ 2: 489: if ((in_padata == NULL) || (in_padata->length == 0)) { #####: 490: pkiDebug("pa_pkinit_parse_rep: no in_padata\n"); #####: 491: return KRB5KDC_ERR_PREAUTH_FAILED; -: 492: } -: 493: 2: 494: asRep.data = (char *) in_padata->contents; 2: 495: asRep.length = in_padata->length; -: 496: 2: 497: retval = 2: 498: pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type, -: 499: request, &asRep, as_key, etype, encoded_request); 2: 500: if (retval) { #####: 501: pkiDebug("pkinit_as_rep_parse returned %d (%s)\n", -: 502: retval, error_message(retval)); #####: 503: goto cleanup; -: 504: } -: 505: 2: 506: retval = 0; -: 507: -: 508:cleanup: -: 509: 2: 510: return retval; -: 511:} -: 512: -: 513:static krb5_error_code 2: 514:verify_kdc_san(krb5_context context, -: 515: pkinit_context plgctx, -: 516: pkinit_req_context reqctx, -: 517: krb5_principal kdcprinc, -: 518: int *valid_san, -: 519: int *need_eku_checking) -: 520:{ -: 521: krb5_error_code retval; 2: 522: char **certhosts = NULL, **cfghosts = NULL; 2: 523: krb5_principal *princs = NULL; -: 524: unsigned char ***get_dns; -: 525: int i, j; -: 526: 2: 527: *valid_san = 0; 2: 528: *need_eku_checking = 1; -: 529: 2: 530: retval = pkinit_libdefault_strings(context, 2: 531: krb5_princ_realm(context, kdcprinc), -: 532: KRB5_CONF_PKINIT_KDC_HOSTNAME, -: 533: &cfghosts); 4: 534: if (retval || cfghosts == NULL) { 2: 535: pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n", -: 536: __FUNCTION__); 2: 537: get_dns = NULL; -: 538: } else { #####: 539: pkiDebug("%s: pkinit_kdc_hostname values found in config file\n", -: 540: __FUNCTION__); #####: 541: get_dns = (unsigned char ***)&certhosts; -: 542: } -: 543: 2: 544: retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, -: 545: reqctx->cryptoctx, reqctx->idctx, -: 546: &princs, NULL, get_dns); 2: 547: if (retval) { #####: 548: pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); #####: 549: retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; #####: 550: goto out; -: 551: } -: 552:#if 0 -: 553: retval = call_san_checking_plugins(context, plgctx, reqctx, idctx, -: 554: princs, hosts, &plugin_decision, -: 555: need_eku_checking); -: 556: pkiDebug("%s: call_san_checking_plugins() returned retval %d\n", -: 557: __FUNCTION__); -: 558: if (retval) { -: 559: retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; -: 560: goto out; -: 561: } -: 562: pkiDebug("%s: call_san_checking_plugins() returned decision %d and " -: 563: "need_eku_checking %d\n", -: 564: __FUNCTION__, plugin_decision, *need_eku_checking); -: 565: if (plugin_decision != NO_DECISION) { -: 566: retval = plugin_decision; -: 567: goto out; -: 568: } -: 569:#endif -: 570: 2: 571: pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); 2: 572: for (i = 0; princs != NULL && princs[i] != NULL; i++) { 2: 573: if (krb5_principal_compare(context, princs[i], kdcprinc)) { 2: 574: pkiDebug("%s: pkinit san match found\n", __FUNCTION__); 2: 575: *valid_san = 1; 2: 576: *need_eku_checking = 0; 2: 577: retval = 0; 2: 578: goto out; -: 579: } -: 580: } #####: 581: pkiDebug("%s: no pkinit san match found\n", __FUNCTION__); -: 582: #####: 583: if (certhosts == NULL) { #####: 584: pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n", -: 585: __FUNCTION__); #####: 586: retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; #####: 587: goto out; -: 588: } -: 589: #####: 590: for (i = 0; certhosts[i] != NULL; i++) { #####: 591: for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) { #####: 592: pkiDebug("%s: comparing cert name '%s' with config name '%s'\n", #####: 593: __FUNCTION__, certhosts[i], cfghosts[j]); #####: 594: if (strcmp(certhosts[i], cfghosts[j]) == 0) { #####: 595: pkiDebug("%s: we have a dnsName match\n", __FUNCTION__); #####: 596: *valid_san = 1; #####: 597: retval = 0; #####: 598: goto out; -: 599: } -: 600: } -: 601: } #####: 602: pkiDebug("%s: no dnsName san match found\n", __FUNCTION__); -: 603: -: 604: /* We found no match */ #####: 605: retval = 0; -: 606: -: 607:out: 2: 608: if (princs != NULL) { 4: 609: for (i = 0; princs[i] != NULL; i++) 2: 610: krb5_free_principal(context, princs[i]); 2: 611: free(princs); -: 612: } 2: 613: if (certhosts != NULL) { #####: 614: for (i = 0; certhosts[i] != NULL; i++) #####: 615: free(certhosts[i]); #####: 616: free(certhosts); -: 617: } 2: 618: if (cfghosts != NULL) #####: 619: profile_free_list(cfghosts); -: 620: 2: 621: pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n", -: 622: __FUNCTION__, retval, *valid_san, *need_eku_checking); 2: 623: return retval; -: 624:} -: 625: -: 626:static krb5_error_code #####: 627:verify_kdc_eku(krb5_context context, -: 628: pkinit_context plgctx, -: 629: pkinit_req_context reqctx, -: 630: int *eku_accepted) -: 631:{ -: 632: krb5_error_code retval; -: 633: #####: 634: *eku_accepted = 0; -: 635: #####: 636: if (reqctx->opts->require_eku == 0) { #####: 637: pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__); #####: 638: *eku_accepted = 1; #####: 639: retval = 0; #####: 640: goto out; -: 641: } #####: 642: retval = crypto_check_cert_eku(context, plgctx->cryptoctx, -: 643: reqctx->cryptoctx, reqctx->idctx, -: 644: 1, /* kdc cert */ #####: 645: reqctx->opts->accept_secondary_eku, -: 646: eku_accepted); #####: 647: if (retval) { #####: 648: pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n", -: 649: __FUNCTION__, retval, error_message(retval)); -: 650: goto out; -: 651: } -: 652: -: 653:out: #####: 654: pkiDebug("%s: returning retval %d, eku_accepted %d\n", -: 655: __FUNCTION__, retval, *eku_accepted); #####: 656: return retval; -: 657:} -: 658: -: 659:/* -: 660: * Parse PA-PK-AS-REP message. Optionally evaluates the message's -: 661: * certificate chain. -: 662: * Optionally returns various components. -: 663: */ -: 664:static krb5_error_code 2: 665:pkinit_as_rep_parse(krb5_context context, -: 666: pkinit_context plgctx, -: 667: pkinit_req_context reqctx, -: 668: krb5_preauthtype pa_type, -: 669: krb5_kdc_req *request, -: 670: const krb5_data *as_rep, -: 671: krb5_keyblock *key_block, -: 672: krb5_enctype etype, -: 673: krb5_data *encoded_request) -: 674:{ 2: 675: krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 2: 676: krb5_principal kdc_princ = NULL; 2: 677: krb5_pa_pk_as_rep *kdc_reply = NULL; 2: 678: krb5_kdc_dh_key_info *kdc_dh = NULL; 2: 679: krb5_reply_key_pack *key_pack = NULL; 2: 680: krb5_reply_key_pack_draft9 *key_pack9 = NULL; 2: 681: krb5_octet_data dh_data = { 0, 0, NULL }; 2: 682: unsigned char *client_key = NULL, *kdc_hostname = NULL; 2: 683: unsigned int client_key_len = 0; 2: 684: krb5_checksum cksum = {0, 0, 0, NULL}; -: 685: krb5_data k5data; -: 686: krb5_octet_data secret; 2: 687: int valid_san = 0; 2: 688: int valid_eku = 0; 2: 689: int need_eku_checking = 1; -: 690: 2: 691: assert((as_rep != NULL) && (key_block != NULL)); -: 692: -: 693:#ifdef DEBUG_ASN1 -: 694: print_buffer_bin((unsigned char *)as_rep->data, as_rep->length, -: 695: "/tmp/client_as_rep"); -: 696:#endif -: 697: 2: 698: if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) { #####: 699: pkiDebug("decode_krb5_as_rep failed %d\n", retval); #####: 700: return retval; -: 701: } -: 702: 2: 703: switch(kdc_reply->choice) { -: 704: case choice_pa_pk_as_rep_dhInfo: 2: 705: pkiDebug("as_rep: DH key transport algorithm\n"); -: 706:#ifdef DEBUG_ASN1 -: 707: print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data, -: 708: kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata"); -: 709:#endif 4: 710: if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx, -: 711: reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER, 2: 712: reqctx->opts->require_crl_checking, 2: 713: kdc_reply->u.dh_Info.dhSignedData.data, 2: 714: kdc_reply->u.dh_Info.dhSignedData.length, -: 715: &dh_data.data, &dh_data.length, -: 716: NULL, NULL, NULL)) != 0) { #####: 717: pkiDebug("failed to verify pkcs7 signed data\n"); #####: 718: goto cleanup; -: 719: } -: 720: 2: 721: break; -: 722: case choice_pa_pk_as_rep_encKeyPack: #####: 723: pkiDebug("as_rep: RSA key transport algorithm\n"); #####: 724: if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx, -: 725: reqctx->cryptoctx, reqctx->idctx, pa_type, #####: 726: reqctx->opts->require_crl_checking, #####: 727: kdc_reply->u.encKeyPack.data, #####: 728: kdc_reply->u.encKeyPack.length, -: 729: &dh_data.data, &dh_data.length)) != 0) { #####: 730: pkiDebug("failed to verify pkcs7 enveloped data\n"); #####: 731: goto cleanup; -: 732: } #####: 733: break; -: 734: default: #####: 735: pkiDebug("unknown as_rep type %d\n", kdc_reply->choice); #####: 736: retval = -1; #####: 737: goto cleanup; -: 738: } 6: 739: retval = krb5_build_principal_ext(context, &kdc_princ, 2: 740: request->server->realm.length, 2: 741: request->server->realm.data, -: 742: strlen(KRB5_TGS_NAME), KRB5_TGS_NAME, 2: 743: request->server->realm.length, 2: 744: request->server->realm.data, -: 745: 0); 2: 746: if (retval) #####: 747: goto cleanup; 2: 748: retval = verify_kdc_san(context, plgctx, reqctx, kdc_princ, -: 749: &valid_san, &need_eku_checking); 2: 750: if (retval) #####: 751: goto cleanup; 2: 752: if (!valid_san) { #####: 753: pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n", -: 754: __FUNCTION__); #####: 755: retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; #####: 756: goto cleanup; -: 757: } -: 758: 2: 759: if (need_eku_checking) { #####: 760: retval = verify_kdc_eku(context, plgctx, reqctx, -: 761: &valid_eku); #####: 762: if (retval) #####: 763: goto cleanup; #####: 764: if (!valid_eku) { #####: 765: pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n", -: 766: __FUNCTION__); #####: 767: retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; #####: 768: goto cleanup; -: 769: } -: 770: } else 2: 771: pkiDebug("%s: skipping EKU check\n", __FUNCTION__); -: 772: 2: 773: OCTETDATA_TO_KRB5DATA(&dh_data, &k5data); -: 774: 2: 775: switch(kdc_reply->choice) { -: 776: case choice_pa_pk_as_rep_dhInfo: -: 777:#ifdef DEBUG_ASN1 -: 778: print_buffer_bin(dh_data.data, dh_data.length, -: 779: "/tmp/client_dh_key"); -: 780:#endif 2: 781: if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data, -: 782: &kdc_dh)) != 0) { #####: 783: pkiDebug("failed to decode kdc_dh_key_info\n"); #####: 784: goto cleanup; -: 785: } -: 786: -: 787: /* client after KDC reply */ 4: 788: if ((retval = client_process_dh(context, plgctx->cryptoctx, -: 789: reqctx->cryptoctx, reqctx->idctx, 2: 790: kdc_dh->subjectPublicKey.data, 2: 791: kdc_dh->subjectPublicKey.length, -: 792: &client_key, &client_key_len)) != 0) { #####: 793: pkiDebug("failed to process dh params\n"); #####: 794: goto cleanup; -: 795: } -: 796: -: 797: /* If we have a KDF algorithm ID, call the algorithm agility KDF... */ 2: 798: if (kdc_reply->u.dh_Info.kdfID) { 2: 799: secret.length = client_key_len; 2: 800: secret.data = client_key; -: 801: 4: 802: retval = pkinit_alg_agility_kdf(context, &secret, 2: 803: kdc_reply->u.dh_Info.kdfID, 2: 804: request->client, 2: 805: request->server, etype, -: 806: (krb5_octet_data *)encoded_request, -: 807: (krb5_octet_data *)as_rep, -: 808: key_block); -: 809: 2: 810: if (retval) { #####: 811: pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n", -: 812: error_message(retval)); #####: 813: goto cleanup; -: 814: } -: 815: -: 816: /* ...otherwise, use the older octetstring2key function. */ -: 817: } else { -: 818: #####: 819: retval = pkinit_octetstring2key(context, etype, client_key, -: 820: client_key_len, key_block); #####: 821: if (retval) { #####: 822: pkiDebug("failed to create key pkinit_octetstring2key %s\n", -: 823: error_message(retval)); #####: 824: goto cleanup; -: 825: } -: 826: } -: 827: 2: 828: break; -: 829: case choice_pa_pk_as_rep_encKeyPack: -: 830:#ifdef DEBUG_ASN1 -: 831: print_buffer_bin(dh_data.data, dh_data.length, -: 832: "/tmp/client_key_pack"); -: 833:#endif #####: 834: if ((retval = k5int_decode_krb5_reply_key_pack(&k5data, -: 835: &key_pack)) != 0) { #####: 836: pkiDebug("failed to decode reply_key_pack\n"); -: 837:#ifdef LONGHORN_BETA_COMPAT -: 838: /* -: 839: * LH Beta 3 requires the extra pa-data, even for RFC requests, -: 840: * in order to get the Checksum rather than a Nonce in the reply. -: 841: * This can be removed when LH SP1 is released. -: 842: */ #####: 843: if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0) -: 844:#else -: 845: if (pa_type == KRB5_PADATA_PK_AS_REP) -: 846:#endif -: 847: goto cleanup; -: 848: else { #####: 849: if ((retval = #####: 850: k5int_decode_krb5_reply_key_pack_draft9(&k5data, -: 851: &key_pack9)) != 0) { #####: 852: pkiDebug("failed to decode reply_key_pack_draft9\n"); #####: 853: goto cleanup; -: 854: } #####: 855: pkiDebug("decode reply_key_pack_draft9\n"); #####: 856: if (key_pack9->nonce != request->nonce) { #####: 857: pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n", key_pack9->nonce, request->nonce); #####: 858: retval = -1; #####: 859: goto cleanup; -: 860: } #####: 861: krb5_copy_keyblock_contents(context, &key_pack9->replyKey, -: 862: key_block); #####: 863: break; -: 864: } -: 865: } -: 866: /* -: 867: * This is hack but Windows sends back SHA1 checksum -: 868: * with checksum type of 14. There is currently no -: 869: * checksum type of 14 defined. -: 870: */ #####: 871: if (key_pack->asChecksum.checksum_type == 14) #####: 872: key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA; #####: 873: retval = krb5_c_make_checksum(context, #####: 874: key_pack->asChecksum.checksum_type, #####: 875: &key_pack->replyKey, -: 876: KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, -: 877: encoded_request, &cksum); #####: 878: if (retval) { #####: 879: pkiDebug("failed to make a checksum\n"); #####: 880: goto cleanup; -: 881: } -: 882: #####: 883: if ((cksum.length != key_pack->asChecksum.length) || #####: 884: memcmp(cksum.contents, key_pack->asChecksum.contents, #####: 885: cksum.length)) { #####: 886: pkiDebug("failed to match the checksums\n"); -: 887:#ifdef DEBUG_CKSUM -: 888: pkiDebug("calculating checksum on buf size (%d)\n", -: 889: encoded_request->length); -: 890: print_buffer(encoded_request->data, encoded_request->length); -: 891: pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length); -: 892: print_buffer(key_pack->replyKey.contents, -: 893: key_pack->replyKey.length); -: 894: pkiDebug("received checksum type=%d size=%d ", -: 895: key_pack->asChecksum.checksum_type, -: 896: key_pack->asChecksum.length); -: 897: print_buffer(key_pack->asChecksum.contents, -: 898: key_pack->asChecksum.length); -: 899: pkiDebug("expected checksum type=%d size=%d ", -: 900: cksum.checksum_type, cksum.length); -: 901: print_buffer(cksum.contents, cksum.length); -: 902:#endif #####: 903: goto cleanup; -: 904: } else #####: 905: pkiDebug("checksums match\n"); -: 906: #####: 907: krb5_copy_keyblock_contents(context, &key_pack->replyKey, -: 908: key_block); -: 909: #####: 910: break; -: 911: default: #####: 912: pkiDebug("unknow as_rep type %d\n", kdc_reply->choice); #####: 913: goto cleanup; -: 914: } -: 915: 2: 916: retval = 0; -: 917: -: 918:cleanup: 2: 919: free(dh_data.data); 2: 920: krb5_free_principal(context, kdc_princ); 2: 921: free(client_key); 2: 922: free_krb5_kdc_dh_key_info(&kdc_dh); 2: 923: free_krb5_pa_pk_as_rep(&kdc_reply); -: 924: 2: 925: if (key_pack != NULL) { #####: 926: free_krb5_reply_key_pack(&key_pack); #####: 927: free(cksum.contents); -: 928: } 2: 929: if (key_pack9 != NULL) #####: 930: free_krb5_reply_key_pack_draft9(&key_pack9); -: 931: 2: 932: free(kdc_hostname); -: 933: 2: 934: pkiDebug("pkinit_as_rep_parse returning %d (%s)\n", -: 935: retval, error_message(retval)); 2: 936: return retval; -: 937:} -: 938: -: 939:static void 2: 940:pkinit_client_profile(krb5_context context, -: 941: pkinit_context plgctx, -: 942: pkinit_req_context reqctx, -: 943: const krb5_data *realm) -: 944:{ 2: 945: char *eku_string = NULL; -: 946: 2: 947: pkiDebug("pkinit_client_profile %p %p %p %p\n", -: 948: context, plgctx, reqctx, realm); -: 949: 4: 950: pkinit_libdefault_boolean(context, realm, -: 951: KRB5_CONF_PKINIT_WIN2K, 2: 952: reqctx->opts->win2k_target, 2: 953: &reqctx->opts->win2k_target); 4: 954: pkinit_libdefault_boolean(context, realm, -: 955: KRB5_CONF_PKINIT_WIN2K_REQUIRE_BINDING, 2: 956: reqctx->opts->win2k_require_cksum, 2: 957: &reqctx->opts->win2k_require_cksum); 4: 958: pkinit_libdefault_boolean(context, realm, -: 959: KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING, 2: 960: reqctx->opts->require_crl_checking, 2: 961: &reqctx->opts->require_crl_checking); 4: 962: pkinit_libdefault_integer(context, realm, -: 963: KRB5_CONF_PKINIT_DH_MIN_BITS, 2: 964: reqctx->opts->dh_size, 2: 965: &reqctx->opts->dh_size); 4: 966: if (reqctx->opts->dh_size != 1024 && reqctx->opts->dh_size != 2048 2: 967: && reqctx->opts->dh_size != 4096) { #####: 968: pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " -: 969: "using default value (%d) instead\n", __FUNCTION__, #####: 970: reqctx->opts->dh_size, PKINIT_DEFAULT_DH_MIN_BITS); #####: 971: reqctx->opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS; -: 972: } 2: 973: pkinit_libdefault_string(context, realm, -: 974: KRB5_CONF_PKINIT_EKU_CHECKING, -: 975: &eku_string); 2: 976: if (eku_string != NULL) { #####: 977: if (strcasecmp(eku_string, "kpKDC") == 0) { #####: 978: reqctx->opts->require_eku = 1; #####: 979: reqctx->opts->accept_secondary_eku = 0; #####: 980: } else if (strcasecmp(eku_string, "kpServerAuth") == 0) { #####: 981: reqctx->opts->require_eku = 1; #####: 982: reqctx->opts->accept_secondary_eku = 1; #####: 983: } else if (strcasecmp(eku_string, "none") == 0) { #####: 984: reqctx->opts->require_eku = 0; #####: 985: reqctx->opts->accept_secondary_eku = 0; -: 986: } else { #####: 987: pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", -: 988: __FUNCTION__, eku_string); -: 989: } #####: 990: free(eku_string); -: 991: } -: 992:#ifdef LONGHORN_BETA_COMPAT -: 993: /* Temporarily just set global flag from config file */ 2: 994: pkinit_libdefault_boolean(context, realm, -: 995: KRB5_CONF_PKINIT_LONGHORN, -: 996: 0, -: 997: &longhorn); -: 998:#endif -: 999: -: 1000: /* Only process anchors here if they were not specified on command line */ 2: 1001: if (reqctx->idopts->anchors == NULL) 2: 1002: pkinit_libdefault_strings(context, realm, -: 1003: KRB5_CONF_PKINIT_ANCHORS, 2: 1004: &reqctx->idopts->anchors); 2: 1005: pkinit_libdefault_strings(context, realm, -: 1006: KRB5_CONF_PKINIT_POOL, 2: 1007: &reqctx->idopts->intermediates); 2: 1008: pkinit_libdefault_strings(context, realm, -: 1009: KRB5_CONF_PKINIT_REVOKE, 2: 1010: &reqctx->idopts->crls); 2: 1011: pkinit_libdefault_strings(context, realm, -: 1012: KRB5_CONF_PKINIT_IDENTITIES, 2: 1013: &reqctx->idopts->identity_alt); 2: 1014:} -: 1015: -: 1016:static krb5_error_code 8: 1017:pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata, -: 1018: krb5_clpreauth_modreq modreq, -: 1019: krb5_get_init_creds_opt *gic_opt, -: 1020: krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, -: 1021: krb5_kdc_req *request, krb5_data *encoded_request_body, -: 1022: krb5_data *encoded_previous_request, -: 1023: krb5_pa_data *in_padata, -: 1024: krb5_prompter_fct prompter, void *prompter_data, -: 1025: krb5_pa_data ***out_padata) -: 1026:{ 8: 1027: krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 8: 1028: krb5_enctype enctype = -1; 8: 1029: int processing_request = 0; 8: 1030: pkinit_context plgctx = (pkinit_context)moddata; 8: 1031: pkinit_req_context reqctx = (pkinit_req_context)modreq; 8: 1032: krb5_keyblock *armor_key = cb->fast_armor(context, rock), as_key; -: 1033: 8: 1034: pkiDebug("pkinit_client_process %p %p %p %p\n", -: 1035: context, plgctx, reqctx, request); -: 1036: -: 1037: /* Remove (along with armor_key) when FAST PKINIT is settled. */ -: 1038: /* Don't use PKINIT if also using FAST. */ 8: 1039: if (armor_key != NULL) #####: 1040: return EINVAL; -: 1041: 8: 1042: if (plgctx == NULL || reqctx == NULL) #####: 1043: return EINVAL; -: 1044: 8: 1045: switch ((int) in_padata->pa_type) { -: 1046: case KRB5_PADATA_PKINIT_KX: 4: 1047: reqctx->rfc6112_kdc = 1; 4: 1048: return 0; -: 1049: case KRB5_PADATA_PK_AS_REQ: 2: 1050: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); 2: 1051: processing_request = 1; 2: 1052: break; -: 1053: -: 1054: case KRB5_PADATA_PK_AS_REP: 2: 1055: pkiDebug("processing KRB5_PADATA_PK_AS_REP\n"); 2: 1056: break; -: 1057: case KRB5_PADATA_PK_AS_REP_OLD: -: 1058: case KRB5_PADATA_PK_AS_REQ_OLD: #####: 1059: if (in_padata->length == 0) { #####: 1060: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); #####: 1061: in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD; #####: 1062: processing_request = 1; -: 1063: } else { #####: 1064: pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n"); #####: 1065: in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD; -: 1066: } #####: 1067: break; -: 1068: default: #####: 1069: pkiDebug("unrecognized patype = %d for PKINIT\n", -: 1070: in_padata->pa_type); #####: 1071: return EINVAL; -: 1072: } -: 1073: 4: 1074: if (processing_request) { 2: 1075: pkinit_client_profile(context, plgctx, reqctx, 2: 1076: &request->server->realm); 2: 1077: pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data); 2: 1078: retval = pkinit_identity_initialize(context, plgctx->cryptoctx, -: 1079: reqctx->cryptoctx, reqctx->idopts, -: 1080: reqctx->idctx, 1, request->client); 2: 1081: if (retval) { #####: 1082: pkiDebug("pkinit_identity_initialize returned %d (%s)\n", -: 1083: retval, error_message(retval)); #####: 1084: return retval; -: 1085: } 2: 1086: retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, -: 1087: in_padata, out_padata, prompter, -: 1088: prompter_data, gic_opt); -: 1089: } else { -: 1090: /* -: 1091: * Get the enctype of the reply. -: 1092: */ 2: 1093: enctype = cb->get_etype(context, rock); 2: 1094: retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request, -: 1095: in_padata, enctype, &as_key, -: 1096: encoded_previous_request); 2: 1097: if (retval == 0) 2: 1098: retval = cb->set_as_key(context, rock, &as_key); -: 1099: } -: 1100: 4: 1101: pkiDebug("pkinit_client_process: returning %d (%s)\n", -: 1102: retval, error_message(retval)); 4: 1103: return retval; -: 1104:} -: 1105: -: 1106:static krb5_error_code #####: 1107:pkinit_client_tryagain(krb5_context context, krb5_clpreauth_moddata moddata, -: 1108: krb5_clpreauth_modreq modreq, -: 1109: krb5_get_init_creds_opt *gic_opt, -: 1110: krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, -: 1111: krb5_kdc_req *request, krb5_data *encoded_request_body, -: 1112: krb5_data *encoded_previous_request, -: 1113: krb5_pa_data *in_padata, krb5_error *err_reply, -: 1114: krb5_prompter_fct prompter, void *prompter_data, -: 1115: krb5_pa_data ***out_padata) -: 1116:{ #####: 1117: krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; #####: 1118: pkinit_context plgctx = (pkinit_context)moddata; #####: 1119: pkinit_req_context reqctx = (pkinit_req_context)modreq; #####: 1120: krb5_typed_data **typed_data = NULL; -: 1121: krb5_data scratch; #####: 1122: krb5_external_principal_identifier **krb5_trusted_certifiers = NULL; #####: 1123: krb5_algorithm_identifier **algId = NULL; #####: 1124: int do_again = 0; -: 1125: #####: 1126: pkiDebug("pkinit_client_tryagain %p %p %p %p\n", -: 1127: context, plgctx, reqctx, request); -: 1128: #####: 1129: if (reqctx->pa_type != in_padata->pa_type) #####: 1130: return retval; -: 1131: -: 1132:#ifdef DEBUG_ASN1 -: 1133: print_buffer_bin((unsigned char *)err_reply->e_data.data, -: 1134: err_reply->e_data.length, "/tmp/client_edata"); -: 1135:#endif #####: 1136: retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data); #####: 1137: if (retval) { #####: 1138: pkiDebug("decode_krb5_typed_data failed\n"); #####: 1139: goto cleanup; -: 1140: } -: 1141:#ifdef DEBUG_ASN1 -: 1142: print_buffer_bin(typed_data[0]->data, typed_data[0]->length, -: 1143: "/tmp/client_typed_data"); -: 1144:#endif #####: 1145: OCTETDATA_TO_KRB5DATA(typed_data[0], &scratch); -: 1146: #####: 1147: switch(typed_data[0]->type) { -: 1148: case TD_TRUSTED_CERTIFIERS: -: 1149: case TD_INVALID_CERTIFICATES: #####: 1150: retval = k5int_decode_krb5_td_trusted_certifiers(&scratch, -: 1151: &krb5_trusted_certifiers); #####: 1152: if (retval) { #####: 1153: pkiDebug("failed to decode sequence of trusted certifiers\n"); #####: 1154: goto cleanup; -: 1155: } #####: 1156: retval = pkinit_process_td_trusted_certifiers(context, -: 1157: plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, #####: 1158: krb5_trusted_certifiers, typed_data[0]->type); #####: 1159: if (!retval) #####: 1160: do_again = 1; #####: 1161: break; -: 1162: case TD_DH_PARAMETERS: #####: 1163: retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId); #####: 1164: if (retval) { #####: 1165: pkiDebug("failed to decode td_dh_parameters\n"); #####: 1166: goto cleanup; -: 1167: } #####: 1168: retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx, -: 1169: reqctx->cryptoctx, reqctx->idctx, algId, #####: 1170: &reqctx->opts->dh_size); #####: 1171: if (!retval) #####: 1172: do_again = 1; -: 1173: break; -: 1174: default: -: 1175: break; -: 1176: } -: 1177: #####: 1178: if (do_again) { #####: 1179: retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata, -: 1180: out_padata, prompter, prompter_data, gic_opt); #####: 1181: if (retval) #####: 1182: goto cleanup; -: 1183: } -: 1184: #####: 1185: retval = 0; -: 1186:cleanup: #####: 1187: if (krb5_trusted_certifiers != NULL) #####: 1188: free_krb5_external_principal_identifier(&krb5_trusted_certifiers); -: 1189: #####: 1190: if (typed_data != NULL) #####: 1191: free_krb5_typed_data(&typed_data); -: 1192: #####: 1193: if (algId != NULL) #####: 1194: free_krb5_algorithm_identifiers(&algId); -: 1195: #####: 1196: pkiDebug("pkinit_client_tryagain: returning %d (%s)\n", -: 1197: retval, error_message(retval)); #####: 1198: return retval; -: 1199:} -: 1200: -: 1201:static int 965: 1202:pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype) -: 1203:{ 965: 1204: if (patype == KRB5_PADATA_PKINIT_KX) 193: 1205: return PA_INFO|PA_PSEUDO; 772: 1206: return PA_REAL; -: 1207:} -: 1208: -: 1209:/* -: 1210: * We want to be notified about KRB5_PADATA_PKINIT_KX in addition to the actual -: 1211: * pkinit patypes because RFC 6112 requires anonymous KDCs to send it. We use -: 1212: * that to determine whether to use the broken MIT 1.9 behavior of sending -: 1213: * ContentInfo rather than SignedData or the RFC 6112 behavior -: 1214: */ -: 1215:static krb5_preauthtype supported_client_pa_types[] = { -: 1216: KRB5_PADATA_PK_AS_REP, -: 1217: KRB5_PADATA_PK_AS_REQ, -: 1218: KRB5_PADATA_PK_AS_REP_OLD, -: 1219: KRB5_PADATA_PK_AS_REQ_OLD, -: 1220: KRB5_PADATA_PKINIT_KX, -: 1221: 0 -: 1222:}; -: 1223: -: 1224:static void 203: 1225:pkinit_client_req_init(krb5_context context, -: 1226: krb5_clpreauth_moddata moddata, -: 1227: krb5_clpreauth_modreq *modreq_out) -: 1228:{ 203: 1229: krb5_error_code retval = ENOMEM; 203: 1230: pkinit_req_context reqctx = NULL; 203: 1231: pkinit_context plgctx = (pkinit_context)moddata; -: 1232: 203: 1233: *modreq_out = NULL; -: 1234: 203: 1235: reqctx = malloc(sizeof(*reqctx)); 203: 1236: if (reqctx == NULL) #####: 1237: return; 203: 1238: memset(reqctx, 0, sizeof(*reqctx)); -: 1239: 203: 1240: reqctx->magic = PKINIT_REQ_CTX_MAGIC; 203: 1241: reqctx->cryptoctx = NULL; 203: 1242: reqctx->opts = NULL; 203: 1243: reqctx->idctx = NULL; 203: 1244: reqctx->idopts = NULL; -: 1245: 203: 1246: retval = pkinit_init_req_opts(&reqctx->opts); 203: 1247: if (retval) #####: 1248: goto cleanup; -: 1249: 203: 1250: reqctx->opts->require_eku = plgctx->opts->require_eku; 203: 1251: reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku; 203: 1252: reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa; 203: 1253: reqctx->opts->allow_upn = plgctx->opts->allow_upn; 203: 1254: reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking; -: 1255: 203: 1256: retval = pkinit_init_req_crypto(&reqctx->cryptoctx); 203: 1257: if (retval) #####: 1258: goto cleanup; -: 1259: 203: 1260: retval = pkinit_init_identity_crypto(&reqctx->idctx); 203: 1261: if (retval) #####: 1262: goto cleanup; -: 1263: 203: 1264: retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts); 203: 1265: if (retval) #####: 1266: goto cleanup; -: 1267: 203: 1268: *modreq_out = (krb5_clpreauth_modreq)reqctx; 203: 1269: pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); -: 1270: -: 1271:cleanup: 203: 1272: if (retval) { #####: 1273: if (reqctx->idctx != NULL) #####: 1274: pkinit_fini_identity_crypto(reqctx->idctx); #####: 1275: if (reqctx->cryptoctx != NULL) #####: 1276: pkinit_fini_req_crypto(reqctx->cryptoctx); #####: 1277: if (reqctx->opts != NULL) #####: 1278: pkinit_fini_req_opts(reqctx->opts); #####: 1279: if (reqctx->idopts != NULL) #####: 1280: pkinit_fini_identity_opts(reqctx->idopts); #####: 1281: free(reqctx); -: 1282: } -: 1283: 203: 1284: return; -: 1285:} -: 1286: -: 1287:static void 185: 1288:pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata, -: 1289: krb5_clpreauth_modreq modreq) -: 1290:{ 185: 1291: pkinit_req_context reqctx = (pkinit_req_context)modreq; -: 1292: 185: 1293: pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx); 185: 1294: if (reqctx == NULL) #####: 1295: return; 185: 1296: if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) { #####: 1297: pkiDebug("%s: Bad magic value (%x) in req ctx\n", -: 1298: __FUNCTION__, reqctx->magic); #####: 1299: return; -: 1300: } 185: 1301: if (reqctx->opts != NULL) 185: 1302: pkinit_fini_req_opts(reqctx->opts); -: 1303: 185: 1304: if (reqctx->cryptoctx != NULL) 185: 1305: pkinit_fini_req_crypto(reqctx->cryptoctx); -: 1306: 185: 1307: if (reqctx->idctx != NULL) 185: 1308: pkinit_fini_identity_crypto(reqctx->idctx); -: 1309: 185: 1310: if (reqctx->idopts != NULL) 185: 1311: pkinit_fini_identity_opts(reqctx->idopts); -: 1312: 185: 1313: free(reqctx); 185: 1314: return; -: 1315:} -: 1316: -: 1317:static int 193: 1318:pkinit_client_plugin_init(krb5_context context, -: 1319: krb5_clpreauth_moddata *moddata_out) -: 1320:{ 193: 1321: krb5_error_code retval = ENOMEM; 193: 1322: pkinit_context ctx = NULL; -: 1323: 193: 1324: ctx = calloc(1, sizeof(*ctx)); 193: 1325: if (ctx == NULL) #####: 1326: return ENOMEM; 193: 1327: memset(ctx, 0, sizeof(*ctx)); 193: 1328: ctx->magic = PKINIT_CTX_MAGIC; 193: 1329: ctx->opts = NULL; 193: 1330: ctx->cryptoctx = NULL; 193: 1331: ctx->idopts = NULL; -: 1332: 193: 1333: retval = pkinit_accessor_init(); 193: 1334: if (retval) #####: 1335: goto errout; -: 1336: 193: 1337: retval = pkinit_init_plg_opts(&ctx->opts); 193: 1338: if (retval) #####: 1339: goto errout; -: 1340: 193: 1341: retval = pkinit_init_plg_crypto(&ctx->cryptoctx); 193: 1342: if (retval) #####: 1343: goto errout; -: 1344: 193: 1345: retval = pkinit_init_identity_opts(&ctx->idopts); 193: 1346: if (retval) #####: 1347: goto errout; -: 1348: 193: 1349: *moddata_out = (krb5_clpreauth_moddata)ctx; -: 1350: 193: 1351: pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx); -: 1352: -: 1353:errout: 193: 1354: if (retval) #####: 1355: pkinit_client_plugin_fini(context, (krb5_clpreauth_moddata)ctx); -: 1356: 193: 1357: return retval; -: 1358:} -: 1359: -: 1360:static void 193: 1361:pkinit_client_plugin_fini(krb5_context context, krb5_clpreauth_moddata moddata) -: 1362:{ 193: 1363: pkinit_context ctx = (pkinit_context)moddata; -: 1364: 193: 1365: if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) { #####: 1366: pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx); #####: 1367: return; -: 1368: } 193: 1369: pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx); -: 1370: 193: 1371: pkinit_fini_identity_opts(ctx->idopts); 193: 1372: pkinit_fini_plg_crypto(ctx->cryptoctx); 193: 1373: pkinit_fini_plg_opts(ctx->opts); 193: 1374: free(ctx); -: 1375: -: 1376:} -: 1377: -: 1378:static krb5_error_code #####: 1379:add_string_to_array(krb5_context context, char ***array, const char *addition) -: 1380:{ #####: 1381: char **out = NULL; -: 1382: #####: 1383: if (*array == NULL) { #####: 1384: out = malloc(2 * sizeof(char *)); #####: 1385: if (out == NULL) #####: 1386: return ENOMEM; #####: 1387: out[1] = NULL; #####: 1388: out[0] = strdup(addition); #####: 1389: if (out[0] == NULL) { #####: 1390: free(out); #####: 1391: return ENOMEM; -: 1392: } -: 1393: } else { -: 1394: int i; #####: 1395: char **a = *array; #####: 1396: for (i = 0; a[i] != NULL; i++); #####: 1397: out = malloc( (i + 2) * sizeof(char *)); #####: 1398: if (out == NULL) #####: 1399: return ENOMEM; #####: 1400: for (i = 0; a[i] != NULL; i++) { #####: 1401: out[i] = a[i]; -: 1402: } #####: 1403: out[i++] = strdup(addition); #####: 1404: if (out == NULL) { #####: 1405: free(out); #####: 1406: return ENOMEM; -: 1407: } #####: 1408: out[i] = NULL; #####: 1409: free(*array); -: 1410: } #####: 1411: *array = out; -: 1412: #####: 1413: return 0; -: 1414:} -: 1415:static krb5_error_code #####: 1416:handle_gic_opt(krb5_context context, -: 1417: pkinit_context plgctx, -: 1418: const char *attr, -: 1419: const char *value) -: 1420:{ -: 1421: krb5_error_code retval; -: 1422: #####: 1423: if (strcmp(attr, "X509_user_identity") == 0) { #####: 1424: if (plgctx->idopts->identity != NULL) { #####: 1425: krb5_set_error_message(context, KRB5_PREAUTH_FAILED, -: 1426: "X509_user_identity can not be given twice\n"); #####: 1427: return KRB5_PREAUTH_FAILED; -: 1428: } #####: 1429: plgctx->idopts->identity = strdup(value); #####: 1430: if (plgctx->idopts->identity == NULL) { #####: 1431: krb5_set_error_message(context, ENOMEM, -: 1432: "Could not duplicate X509_user_identity value\n"); #####: 1433: return ENOMEM; -: 1434: } #####: 1435: } else if (strcmp(attr, "X509_anchors") == 0) { #####: 1436: retval = add_string_to_array(context, &plgctx->idopts->anchors, value); #####: 1437: if (retval) #####: 1438: return retval; #####: 1439: } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) { #####: 1440: if (strcmp(value, "yes") == 0) { #####: 1441: pkiDebug("Setting flag to use RSA_PROTOCOL\n"); #####: 1442: plgctx->opts->dh_or_rsa = RSA_PROTOCOL; -: 1443: } -: 1444: } #####: 1445: return 0; -: 1446:} -: 1447: -: 1448:static krb5_error_code #####: 1449:pkinit_client_gic_opt(krb5_context context, krb5_clpreauth_moddata moddata, -: 1450: krb5_get_init_creds_opt *gic_opt, -: 1451: const char *attr, -: 1452: const char *value) -: 1453:{ -: 1454: krb5_error_code retval; #####: 1455: pkinit_context plgctx = (pkinit_context)moddata; -: 1456: #####: 1457: pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value); #####: 1458: retval = handle_gic_opt(context, plgctx, attr, value); #####: 1459: if (retval) #####: 1460: return retval; -: 1461: #####: 1462: return 0; -: 1463:} -: 1464: -: 1465:krb5_error_code -: 1466:clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, -: 1467: krb5_plugin_vtable vtable); -: 1468: -: 1469:krb5_error_code 193: 1470:clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, -: 1471: krb5_plugin_vtable vtable) -: 1472:{ -: 1473: krb5_clpreauth_vtable vt; -: 1474: 193: 1475: if (maj_ver != 1) #####: 1476: return KRB5_PLUGIN_VER_NOTSUPP; 193: 1477: vt = (krb5_clpreauth_vtable)vtable; 193: 1478: vt->name = "pkinit"; 193: 1479: vt->pa_type_list = supported_client_pa_types; 193: 1480: vt->init = pkinit_client_plugin_init; 193: 1481: vt->fini = pkinit_client_plugin_fini; 193: 1482: vt->flags = pkinit_client_get_flags; 193: 1483: vt->request_init = pkinit_client_req_init; 193: 1484: vt->request_fini = pkinit_client_req_fini; 193: 1485: vt->process = pkinit_client_process; 193: 1486: vt->tryagain = pkinit_client_tryagain; 193: 1487: vt->gic_opts = pkinit_client_gic_opt; 193: 1488: return 0; -: 1489:}