krb5_error_code KRB5_CALLCONV krb5_calculate_checksum(krb5_context context, krb5_cksumtype ctype, krb5_const_pointer in, size_t in_length, krb5_const_pointer seed, size_t seed_length, krb5_checksum *outcksum) { krb5_data input; krb5_keyblock key; krb5_error_code ret; krb5_checksum cksum; input.data = in; input.length = in_length; key.length = seed_length; key.contents = seed; if ((ret = krb5_c_make_checksum(context, ctype, &key, 0, &input, &cksum))) return(ret); if (outcksum->length < cksum.length) { memset(cksum.contents, 0, cksum.length); free(cksum.contents); return(KRB5_BAD_MSIZE); } outcksum->magic = cksum.magic; outcksum->checksum_type = cksum.checksum_type; memcpy(outcksum->contents, cksum.contents, cksum.length); outcksum->length = cksum.length; free(cksum.contents); return(0); }
/* Generate a response authenticator field. */ static krb5_error_code auth_generate_response(krb5_context ctx, const char *secret, const krad_packet *response, const uchar *auth, uchar *rauth) { krb5_error_code retval; krb5_checksum hash; krb5_data data; /* Allocate the temporary buffer. */ retval = alloc_data(&data, response->pkt.length + strlen(secret)); if (retval != 0) return retval; /* Encoded RADIUS packet with the request's * authenticator and the secret at the end. */ memcpy(data.data, response->pkt.data, response->pkt.length); memcpy(data.data + OFFSET_AUTH, auth, AUTH_FIELD_SIZE); memcpy(data.data + response->pkt.length, secret, strlen(secret)); /* Hash it. */ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data, &hash); free(data.data); if (retval != 0) return retval; memcpy(rauth, hash.contents, AUTH_FIELD_SIZE); krb5_free_checksum_contents(ctx, &hash); return 0; }
krb5_error_code securid_make_sam_challenge_2_and_cksum(krb5_context context, krb5_sam_challenge_2 *sc2, krb5_sam_challenge_2_body *sc2b, krb5_keyblock *cksum_key) { krb5_error_code retval; krb5_checksum **cksum_array = NULL; krb5_checksum *cksum = NULL; krb5_cksumtype cksumtype; krb5_data *encoded_challenge_body = NULL; if (!cksum_key) return KRB5_PREAUTH_NO_KEY; if (!sc2 || !sc2b) return KRB5KDC_ERR_PREAUTH_FAILED; retval = encode_krb5_sam_challenge_2_body(sc2b, &encoded_challenge_body); if (retval || !encoded_challenge_body) { encoded_challenge_body = NULL; goto cksum_cleanup; } cksum_array = calloc(2, sizeof(krb5_checksum *)); if (!cksum_array) { retval = ENOMEM; goto cksum_cleanup; } cksum = (krb5_checksum *)k5alloc(sizeof(krb5_checksum), &retval); if (retval) goto cksum_cleanup; cksum_array[0] = cksum; cksum_array[1] = NULL; retval = krb5int_c_mandatory_cksumtype(context, cksum_key->enctype, &cksumtype); if (retval) goto cksum_cleanup; retval = krb5_c_make_checksum(context, cksumtype, cksum_key, KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, encoded_challenge_body, cksum); if (retval) goto cksum_cleanup; sc2->sam_cksum = cksum_array; sc2->sam_challenge_2_body = *encoded_challenge_body; return 0; cksum_cleanup: krb5_free_data(context, encoded_challenge_body); free(cksum_array); free(cksum); return retval; }
krb5_error_code KRB5_CALLCONV krb5_c_verify_checksum(krb5_context context, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *data, const krb5_checksum *cksum, krb5_boolean *valid) { int i; size_t hashsize; krb5_error_code ret; krb5_data indata; krb5_checksum computed; for (i=0; i<krb5_cksumtypes_length; i++) { if (krb5_cksumtypes_list[i].ctype == cksum->checksum_type) break; } if (i == krb5_cksumtypes_length) return(KRB5_BAD_ENCTYPE); /* if there's actually a verify function, call it */ indata.length = cksum->length; indata.data = (char *) cksum->contents; *valid = 0; if (krb5_cksumtypes_list[i].keyhash && krb5_cksumtypes_list[i].keyhash->verify) return((*(krb5_cksumtypes_list[i].keyhash->verify))( context, key, usage, 0, data, &indata, valid)); /* otherwise, make the checksum again, and compare */ if ((ret = krb5_c_checksum_length(context, cksum->checksum_type, &hashsize))) return(ret); if (cksum->length != hashsize) return(KRB5_BAD_MSIZE); computed.length = hashsize; if ((ret = krb5_c_make_checksum(context, cksum->checksum_type, key, usage, data, &computed))) { FREE(computed.contents, computed.length); return(ret); } *valid = (memcmp(computed.contents, cksum->contents, hashsize) == 0); FREE(computed.contents, computed.length); return(0); }
static krb5_error_code make_ad_signedpath_checksum(krb5_context context, krb5_const_principal for_user_princ, const krb5_db_entry *krbtgt, krb5_keyblock *krbtgt_key, krb5_enc_tkt_part *enc_tkt_part, krb5_principal *deleg_path, krb5_pa_data **method_data, krb5_checksum *cksum) { krb5_error_code code; krb5_data *data; krb5_cksumtype cksumtype; krb5_const_principal client; if (for_user_princ != NULL) client = for_user_princ; else client = enc_tkt_part->client; code = make_ad_signedpath_data(context, client, enc_tkt_part->times.authtime, deleg_path, method_data, enc_tkt_part->authorization_data, &data); if (code != 0) return code; code = krb5int_c_mandatory_cksumtype(context, krbtgt_key->enctype, &cksumtype); if (code != 0) { krb5_free_data(context, data); return code; } if (!krb5_c_is_keyed_cksum(cksumtype)) { krb5_free_data(context, data); return KRB5KRB_AP_ERR_INAPP_CKSUM; } code = krb5_c_make_checksum(context, cksumtype, krbtgt_key, KRB5_KEYUSAGE_AD_SIGNEDPATH, data, cksum); krb5_free_data(context, data); return code; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_c_verify_checksum(krb5_context context, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *data, const krb5_checksum *cksum, krb5_boolean *valid) { krb5_error_code ret; krb5_checksum data_cksum; *valid = 0; ret = krb5_c_make_checksum(context, cksum->cksumtype, key, usage, data, &data_cksum); if (ret) return ret; if (data_cksum.cksumtype == cksum->cksumtype && krb5_data_ct_cmp(&data_cksum.checksum, &cksum->checksum) == 0) *valid = 1; krb5_free_checksum_contents(context, &data_cksum); return 0; }
/* Construct an AP-REQ message for a TGS request. */ static krb5_error_code tgs_construct_ap_req(krb5_context context, krb5_data *checksum_data, krb5_creds *tgt, krb5_keyblock *subkey, krb5_data **ap_req_asn1_out) { krb5_cksumtype cksumtype; krb5_error_code ret; krb5_checksum checksum; krb5_authenticator authent; krb5_ap_req ap_req; krb5_data *authent_asn1 = NULL; krb5_ticket *ticket = NULL; krb5_enc_data authent_enc; *ap_req_asn1_out = NULL; memset(&checksum, 0, sizeof(checksum)); memset(&ap_req, 0, sizeof(ap_req)); memset(&authent_enc, 0, sizeof(authent_enc)); /* Determine the authenticator checksum type. */ switch (tgt->keyblock.enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: case ENCTYPE_ARCFOUR_HMAC: case ENCTYPE_ARCFOUR_HMAC_EXP: cksumtype = context->kdc_req_sumtype; break; default: ret = krb5int_c_mandatory_cksumtype(context, tgt->keyblock.enctype, &cksumtype); if (ret) goto cleanup; } /* Generate checksum. */ ret = krb5_c_make_checksum(context, cksumtype, &tgt->keyblock, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, checksum_data, &checksum); if (ret) goto cleanup; /* Construct, encode, and encrypt an authenticator. */ authent.subkey = subkey; authent.seq_number = 0; authent.checksum = &checksum; authent.client = tgt->client; authent.authorization_data = tgt->authdata; ret = krb5_us_timeofday(context, &authent.ctime, &authent.cusec); if (ret) goto cleanup; ret = encode_krb5_authenticator(&authent, &authent_asn1); if (ret) goto cleanup; ret = krb5_encrypt_helper(context, &tgt->keyblock, KRB5_KEYUSAGE_TGS_REQ_AUTH, authent_asn1, &authent_enc); if (ret) goto cleanup; ret = decode_krb5_ticket(&tgt->ticket, &ticket); if (ret) goto cleanup; /* Encode the AP-REQ. */ ap_req.authenticator = authent_enc; ap_req.ticket = ticket; ret = encode_krb5_ap_req(&ap_req, ap_req_asn1_out); cleanup: free(checksum.contents); krb5_free_ticket(context, ticket); krb5_free_data_contents(context, &authent_enc.ciphertext); if (authent_asn1 != NULL) zapfree(authent_asn1->data, authent_asn1->length); free(authent_asn1); return ret; }
krb5_error_code krb5int_fast_prep_req(krb5_context context, struct krb5int_fast_request_state *state, krb5_kdc_req *request, const krb5_data *to_be_checksummed, kdc_req_encoder_proc encoder, krb5_data **encoded_request) { krb5_error_code retval = 0; krb5_pa_data *pa_array[2]; krb5_pa_data pa[2]; krb5_fast_req fast_req; krb5_fast_armored_req *armored_req = NULL; krb5_data *encoded_fast_req = NULL; krb5_data *encoded_armored_req = NULL; krb5_data *local_encoded_result = NULL; krb5_data random_data; char random_buf[4]; assert(state != NULL); assert(state->fast_outer_request.padata == NULL); memset(pa_array, 0, sizeof pa_array); if (state->armor_key == NULL) { return encoder(request, encoded_request); } TRACE_FAST_ENCODE(context); /* Fill in a fresh random nonce for each inner request*/ random_data.length = 4; random_data.data = (char *)random_buf; retval = krb5_c_random_make_octets(context, &random_data); if (retval == 0) { request->nonce = 0x7fffffff & load_32_n(random_buf); state->nonce = request->nonce; } fast_req.req_body = request; if (fast_req.req_body->padata == NULL) { fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *)); if (fast_req.req_body->padata == NULL) retval = ENOMEM; } fast_req.fast_options = state->fast_options; if (retval == 0) retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req); if (retval == 0) { armored_req = calloc(1, sizeof(krb5_fast_armored_req)); if (armored_req == NULL) retval = ENOMEM; } if (retval == 0) armored_req->armor = state->armor; if (retval ==0) retval = krb5_c_make_checksum(context, 0, state->armor_key, KRB5_KEYUSAGE_FAST_REQ_CHKSUM, to_be_checksummed, &armored_req->req_checksum); if (retval == 0) retval = krb5_encrypt_helper(context, state->armor_key, KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req, &armored_req->enc_part); if (retval == 0) retval = encode_krb5_pa_fx_fast_request(armored_req, &encoded_armored_req); if (retval==0) { pa[0].pa_type = KRB5_PADATA_FX_FAST; pa[0].contents = (unsigned char *) encoded_armored_req->data; pa[0].length = encoded_armored_req->length; pa_array[0] = &pa[0]; } state->fast_outer_request.padata = pa_array; if(retval == 0) retval = encoder(&state->fast_outer_request, &local_encoded_result); if (retval == 0) { *encoded_request = local_encoded_result; local_encoded_result = NULL; } if (encoded_armored_req) krb5_free_data(context, encoded_armored_req); if (armored_req) { armored_req->armor = NULL; /*owned by state*/ krb5_free_fast_armored_req(context, armored_req); } if (encoded_fast_req) krb5_free_data(context, encoded_fast_req); if (local_encoded_result) krb5_free_data(context, local_encoded_result); state->fast_outer_request.padata = NULL; return retval; }
krb5_error_code gss_krb5int_make_seal_token_v3 (krb5_context context, krb5_gss_ctx_id_rec *ctx, const gss_buffer_desc * message, gss_buffer_t token, int conf_req_flag, int toktype) { size_t bufsize = 16; unsigned char *outbuf = 0; krb5_error_code err; int key_usage; unsigned char acceptor_flag; const gss_buffer_desc *message2 = message; #ifdef CFX_EXERCISE size_t rrc; #endif size_t ec; unsigned short tok_id; krb5_checksum sum; krb5_keyblock *key; krb5_cksumtype cksumtype; assert(ctx->big_endian == 0); acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; key_usage = (toktype == KG_TOK_WRAP_MSG ? (ctx->initiate ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL) : (ctx->initiate ? KG_USAGE_INITIATOR_SIGN : KG_USAGE_ACCEPTOR_SIGN)); if (ctx->have_acceptor_subkey) { key = ctx->acceptor_subkey; cksumtype = ctx->acceptor_subkey_cksumtype; } else { key = ctx->subkey; cksumtype = ctx->cksumtype; } assert(key != NULL); #ifdef CFX_EXERCISE { static int initialized = 0; if (!initialized) { srand(time(0)); initialized = 1; } } #endif if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { krb5_data plain; krb5_enc_data cipher; size_t ec_max; /* 300: Adds some slop. */ if (SIZE_MAX - 300 < message->length) return ENOMEM; ec_max = SIZE_MAX - message->length - 300; if (ec_max > 0xffff) ec_max = 0xffff; #ifdef CFX_EXERCISE /* For testing only. For performance, always set ec = 0. */ ec = ec_max & rand(); #else ec = 0; #endif plain.length = message->length + 16 + ec; plain.data = malloc(message->length + 16 + ec); if (plain.data == NULL) return ENOMEM; /* Get size of ciphertext. */ bufsize = 16 + krb5_encrypt_size (plain.length, key->enctype); /* Allocate space for header plus encrypted data. */ outbuf = malloc(bufsize); if (outbuf == NULL) { free(plain.data); return ENOMEM; } /* TOK_ID */ store_16_be(KG2_TOK_WRAP_MSG, outbuf); /* flags */ outbuf[2] = (acceptor_flag | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); /* filler */ outbuf[3] = 0xff; /* EC */ store_16_be(ec, outbuf+4); /* RRC */ store_16_be(0, outbuf+6); store_64_be(ctx->seq_send, outbuf+8); memcpy(plain.data, message->value, message->length); memset(plain.data + message->length, 'x', ec); memcpy(plain.data + message->length + ec, outbuf, 16); cipher.ciphertext.data = (char *)outbuf + 16; cipher.ciphertext.length = bufsize - 16; cipher.enctype = key->enctype; err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher); zap(plain.data, plain.length); free(plain.data); plain.data = 0; if (err) goto error; /* Now that we know we're returning a valid token.... */ ctx->seq_send++; #ifdef CFX_EXERCISE rrc = rand() & 0xffff; if (gss_krb5int_rotate_left(outbuf+16, bufsize-16, (bufsize-16) - (rrc % (bufsize - 16)))) store_16_be(rrc, outbuf+6); /* If the rotate fails, don't worry about it. */ #endif } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { krb5_data plain; size_t cksumsize; /* Here, message is the application-supplied data; message2 is what goes into the output token. They may be the same, or message2 may be empty (for MIC). */ tok_id = KG2_TOK_WRAP_MSG; wrap_with_checksum: plain.length = message->length + 16; plain.data = malloc(message->length + 16); if (plain.data == NULL) return ENOMEM; err = krb5_c_checksum_length(context, cksumtype, &cksumsize); if (err) goto error; assert(cksumsize <= 0xffff); bufsize = 16 + message2->length + cksumsize; outbuf = malloc(bufsize); if (outbuf == NULL) { free(plain.data); plain.data = 0; err = ENOMEM; goto error; } /* TOK_ID */ store_16_be(tok_id, outbuf); /* flags */ outbuf[2] = (acceptor_flag | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); /* filler */ outbuf[3] = 0xff; if (toktype == KG_TOK_WRAP_MSG) { /* Use 0 for checksum calculation, substitute checksum length later. */ /* EC */ store_16_be(0, outbuf+4); /* RRC */ store_16_be(0, outbuf+6); } else { /* MIC and DEL store 0xFF in EC and RRC. */ store_16_be(0xffff, outbuf+4); store_16_be(0xffff, outbuf+6); } store_64_be(ctx->seq_send, outbuf+8); memcpy(plain.data, message->value, message->length); memcpy(plain.data + message->length, outbuf, 16); /* Fill in the output token -- data contents, if any, and space for the checksum. */ if (message2->length) memcpy(outbuf + 16, message2->value, message2->length); sum.contents = outbuf + 16 + message2->length; sum.length = cksumsize; err = krb5_c_make_checksum(context, cksumtype, key, key_usage, &plain, &sum); zap(plain.data, plain.length); free(plain.data); plain.data = 0; if (err) { zap(outbuf,bufsize); goto error; } if (sum.length != cksumsize) abort(); memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize); krb5_free_checksum_contents(context, &sum); sum.contents = 0; /* Now that we know we're actually generating the token... */ ctx->seq_send++; if (toktype == KG_TOK_WRAP_MSG) { #ifdef CFX_EXERCISE rrc = rand() & 0xffff; /* If the rotate fails, don't worry about it. */ if (gss_krb5int_rotate_left(outbuf+16, bufsize-16, (bufsize-16) - (rrc % (bufsize - 16)))) store_16_be(rrc, outbuf+6); #endif /* Fix up EC field. */ store_16_be(cksumsize, outbuf+4); } else { store_16_be(0xffff, outbuf+6); } } else if (toktype == KG_TOK_MIC_MSG) { tok_id = KG2_TOK_MIC_MSG; message2 = &empty_message; goto wrap_with_checksum; } else if (toktype == KG_TOK_DEL_CTX) { tok_id = KG2_TOK_DEL_CTX; message = message2 = &empty_message; goto wrap_with_checksum; } else abort(); token->value = outbuf; token->length = bufsize; return 0; error: free(outbuf); token->value = NULL; token->length = 0; return err; }
int ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms) { krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_ccache cc = NULL; int retval = SNMPERR_SUCCESS; krb5_data outdata, ivector; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input; krb5_enc_data output; unsigned int numcksumtypes; krb5_cksumtype *cksumtype_array; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ size_t blocksize, encrypted_length; unsigned char *encrypted_data = NULL; int zero = 0, i; u_char *cksum_pointer, *endp = *parms->wholeMsg; krb5_cksumtype cksumtype; krb5_checksum pdu_checksum; u_char **wholeMsg = parms->wholeMsg; size_t *offset = parms->wholeMsgOffset, seq_offset; struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *) parms->secStateRef; int rc; DEBUGMSGTL(("ksm", "Starting KSM processing\n")); outdata.length = 0; outdata.data = NULL; ivector.length = 0; ivector.data = NULL; pdu_checksum.contents = NULL; if (!ksm_state) { /* * If we don't have a ksm_state, then we're a request. Get a * credential cache and build a ap_req. */ retcode = krb5_cc_default(kcontext, &cc); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n")); /* * This seems odd, since we don't need this until later (or earlier, * depending on how you look at it), but because the most likely * errors are Kerberos at this point, I'll get this now to save * time not encoding the rest of the packet. * * Also, we need the subkey to encrypt the PDU (if required). */ retcode = krb5_mk_req(kcontext, &auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, (char *) service_name, parms->session->peername, NULL, cc, &outdata); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" " "(may not be actual ticket sname)\n", service_name, parms->session->peername)); } else { /* * Grab the auth_context from our security state reference */ auth_context = ksm_state->auth_context; /* * Bundle up an AP_REP. Note that we do this only when we * have a security state reference (which means we're in an agent * and we're sending a response). */ DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n")); retcode = krb5_mk_rep(kcontext, auth_context, &outdata); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n")); } /* * If we have to encrypt the PDU, do that now */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n")); /* * It's weird - * * If we're on the manager, it's a local subkey (because that's in * our AP_REQ) * * If we're on the agent, it's a remote subkey (because that comes * FROM the received AP_REQ). */ if (ksm_state) retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); else retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_auth_con_getlocalsubkey failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } /* * Note that here we need to handle different things between the * old and new crypto APIs. First, we need to get the final encrypted * length of the PDU. */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_encrypt_length(kcontext, subkey->enctype, parms->scopedPduLen, &encrypted_length); if (retcode) { DEBUGMSGTL(("ksm", "Encryption length calculation failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } encrypted_length = krb5_encrypt_size(parms->scopedPduLen, eblock.crypto_entry); #endif /* MIT_NEW_CRYPTO */ encrypted_data = malloc(encrypted_length); if (!encrypted_data) { DEBUGMSGTL(("ksm", "KSM: Unable to malloc %d bytes for encrypt " "buffer: %s\n", parms->scopedPduLen, strerror(errno))); retval = SNMPERR_MALLOC; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } /* * We need to set up a blank initialization vector for the encryption. * Use a block of all zero's (which is dependent on the block size * of the encryption method). */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine crypto block size: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ blocksize = krb5_enctype_array[subkey->enctype]->system->block_length; #endif /* MIT_NEW_CRYPTO */ ivector.data = malloc(blocksize); if (!ivector.data) { DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", blocksize)); retval = SNMPERR_MALLOC; goto error; } ivector.length = blocksize; memset(ivector.data, 0, blocksize); /* * Finally! Do the encryption! */ #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->scopedPdu; input.length = parms->scopedPduLen; output.ciphertext.data = (char *) encrypted_data; output.ciphertext.length = encrypted_length; retcode = krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &input, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu, (krb5_pointer) encrypted_data, parms->scopedPduLen, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } *offset = 0; rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), encrypted_data, encrypted_length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n")); } else { /* * Plaintext PDU (not encrypted) */ if (*parms->wholeMsgLen < parms->scopedPduLen) { DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n")); retval = SNMPERR_TOO_LONG; goto error; } } /* * Start encoding the msgSecurityParameters * * For now, use 0 for the response hint */ DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n")); seq_offset = *offset; rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), (long *) &zero, sizeof(zero)); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), (u_char *) outdata.data, outdata.length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } /* * Now, we need to pick the "right" checksum algorithm. For old * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick * one of the "approved" ones. */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype, &numcksumtypes, &cksumtype_array); if (retcode) { DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (numcksumtypes <= 0) { DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this " "enctype (%d)\n", subkey->enctype)); snmp_set_detail("No valid checksum type for this encryption type"); retval = SNMPERR_KRB5; goto error; } /* * It's not clear to me from the API which checksum you're supposed * to support, so I'm taking a guess at the first one */ cksumtype = cksumtype_array[0]; krb5_free_cksumtypes(kcontext, cksumtype_array); DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type " "of %d)\n", cksumtype, subkey->enctype)); retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } pdu_checksum.length = blocksize; #else /* MIT_NEW_CRYPTO */ if (ksm_state) cksumtype = ksm_state->cksumtype; else cksumtype = CKSUMTYPE_RSA_MD5_DES; if (!is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } pdu_checksum.length = krb5_checksum_size(kcontext, cksumtype); pdu_checksum.checksum_type = cksumtype; #endif /* MIT_NEW_CRYPTO */ /* * Note that here, we're just leaving blank space for the checksum; * we remember where that is, and we'll fill it in later. */ *offset += pdu_checksum.length; memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, pdu_checksum.length); cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset; rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), pdu_checksum.length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), (long *) &cksumtype, sizeof(cksumtype)); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), *offset - seq_offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), *offset - seq_offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n")); /* * We're done with the KSM security parameters - now we do the global * header and wrap up the whole PDU. */ if (*parms->wholeMsgLen < parms->globalDataLen) { DEBUGMSGTL(("ksm", "Building global data failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } *offset += parms->globalDataLen; memcpy(*wholeMsg + *parms->wholeMsgLen - *offset, parms->globalData, parms->globalDataLen); rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), *offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building master packet sequence.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n")); /* * Now we need to checksum the entire PDU (since it's built). */ pdu_checksum.contents = malloc(pdu_checksum.length); if (!pdu_checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n", pdu_checksum.length)); retval = SNMPERR_MALLOC; goto error; } /* * If we didn't encrypt the packet, we haven't yet got the subkey. * Get that now. */ if (!subkey) { if (ksm_state) retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); else retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } } #ifdef MIT_NEW_CRYPTO input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset); input.length = *offset; retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &pdu_checksum); #else /* MIT_NEW_CRYPTO */ retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg + *parms->wholeMsgLen - *offset, *offset, (krb5_pointer) subkey->contents, subkey->length, &pdu_checksum); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n")); memcpy(cksum_pointer, pdu_checksum.contents, pdu_checksum.length); DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n", pdu_checksum.length, cksum_pointer - (*wholeMsg + 1))); DEBUGMSGTL(("ksm", "KSM: Checksum:")); for (i = 0; i < pdu_checksum.length; i++) DEBUGMSG(("ksm", " %02x", (unsigned int) pdu_checksum.contents[i])); DEBUGMSG(("ksm", "\n")); /* * If we're _not_ called as part of a response (null ksm_state), * then save the auth_context for later using our cache routines. */ if (!ksm_state) { if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context, (u_char *) parms->secName, parms->secNameLen)) != SNMPERR_SUCCESS) goto error; auth_context = NULL; } DEBUGMSGTL(("ksm", "KSM processing complete!\n")); error: if (pdu_checksum.contents) #ifdef MIT_NEW_CRYPTO krb5_free_checksum_contents(kcontext, &pdu_checksum); #else /* MIT_NEW_CRYPTO */ free(pdu_checksum.contents); #endif /* MIT_NEW_CRYPTO */ if (ivector.data) free(ivector.data); if (subkey) krb5_free_keyblock(kcontext, subkey); if (encrypted_data) free(encrypted_data); if (cc) krb5_cc_close(kcontext, cc); if (auth_context && !ksm_state) krb5_auth_con_free(kcontext, auth_context); return retval; }
static krb5_error_code pkinit_server_verify_padata(krb5_context context, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data * data, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval = 0; krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; krb5_data *encoded_pkinit_authz_data = NULL; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; krb5_auth_pack *auth_pack = NULL; krb5_auth_pack_draft9 *auth_pack9 = NULL; pkinit_kdc_context plgctx = NULL; pkinit_kdc_req_context reqctx; krb5_preauthtype pa_type; krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data *der_req = NULL; int valid_eku = 0, valid_san = 0; krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL; krb5_kdc_req *tmp_as_req = NULL; krb5_data k5data; pkiDebug("pkinit_verify_padata: entered!\n"); if (data == NULL || data->length <= 0 || data->contents == NULL) return 0; if (pa_plugin_context == NULL || e_data == NULL) return EINVAL; plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) return 0; #ifdef DEBUG_ASN1 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); #endif /* create a per-request context */ retval = pkinit_init_kdc_req_context(context, (void **)&reqctx); if (retval) goto cleanup; reqctx->pa_type = data->pa_type; PADATA_TO_KRB5DATA(data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); pa_type = (int)data->pa_type; retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, "/tmp/kdc_signed_data"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, plgctx->opts->require_crl_checking, reqp->signedAuthPack.data, reqp->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); pa_type = KRB5_PADATA_PK_AS_REQ_OLD; retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, "/tmp/kdc_signed_data_draft9"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, plgctx->opts->require_crl_checking, reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length); break; default: pkiDebug("unrecognized pa_type = %d\n", data->pa_type); retval = EINVAL; goto cleanup; } if (retval) { pkiDebug("pkcs7_signeddata_verify failed\n"); goto cleanup; } retval = verify_client_san(context, plgctx, reqctx, request->client, &valid_san); if (retval) goto cleanup; if (!valid_san) { pkiDebug("%s: did not find an acceptable SAN in user certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto cleanup; } retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); if (retval) goto cleanup; if (!valid_eku) { pkiDebug("%s: did not find an acceptable EKU in user certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); #endif OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); if (retval) { pkiDebug("failed to decode krb5_auth_pack\n"); goto cleanup; } /* check dh parameters */ if (auth_pack->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } /* * The KDC may have modified the request after decoding it. * We need to compute the checksum on the data that * came from the client. Therefore, we use the original * packet contents. */ retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); if (retval) { pkiDebug("decode_krb5_as_req returned %d\n", (int)retval); goto cleanup; } retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; } retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || memcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, cksum.length)) { pkiDebug("failed to match the checksum\n"); #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("received checksum type=%d size=%d ", auth_pack->pkAuthenticator.paChecksum.checksum_type, auth_pack->pkAuthenticator.paChecksum.length); print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, auth_pack->pkAuthenticator.paChecksum.length); pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length); print_buffer(cksum.contents, cksum.length); #endif retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; goto cleanup; } /* check if kdcPkId present and match KDC's subjectIdentifier */ if (reqp->kdcPkId.data != NULL) { int valid_kdcPkId = 0; retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId); if (retval) goto cleanup; if (!valid_kdcPkId) pkiDebug("kdcPkId in AS_REQ does not match KDC's cert" "RFC says to ignore and proceed\n"); } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack = auth_pack; auth_pack = NULL; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9); if (retval) { pkiDebug("failed to decode krb5_auth_pack_draft9\n"); goto cleanup; } if (auth_pack9->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack9->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack9 = auth_pack9; auth_pack9 = NULL; break; } /* return authorization data to be included in the ticket */ switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: my_authz_data = malloc(2 * sizeof(*my_authz_data)); if (my_authz_data == NULL) { retval = ENOMEM; pkiDebug("Couldn't allocate krb5_authdata ptr array\n"); goto cleanup; } my_authz_data[1] = NULL; my_authz_data[0] = malloc(sizeof(krb5_authdata)); if (my_authz_data[0] == NULL) { retval = ENOMEM; pkiDebug("Couldn't allocate krb5_authdata\n"); free(my_authz_data); goto cleanup; } /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */ my_authz_data[0]->magic = KV5M_AUTHDATA; my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT; /* create an internal AD-INITIAL-VERIFIED-CAS data */ pkinit_authz_data = malloc(sizeof(krb5_authdata)); if (pkinit_authz_data == NULL) { retval = ENOMEM; pkiDebug("Couldn't allocate krb5_authdata\n"); free(my_authz_data[0]); free(my_authz_data); goto cleanup; } pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS; /* content of this ad-type contains the certification path with which the client certificate was validated */ pkinit_authz_data->contents = krb5_authz.data; pkinit_authz_data->length = krb5_authz.length; retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data, &encoded_pkinit_authz_data); #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data, encoded_pkinit_authz_data->length, "/tmp/kdc_pkinit_authz_data"); #endif free(pkinit_authz_data); if (retval) { pkiDebug("k5int_encode_krb5_authdata_elt failed\n"); free(my_authz_data[0]); free(my_authz_data); goto cleanup; } my_authz_data[0]->contents = (krb5_octet *) encoded_pkinit_authz_data->data; my_authz_data[0]->length = encoded_pkinit_authz_data->length; *authz_data = my_authz_data; pkiDebug("Returning %d bytes of authorization data\n", krb5_authz.length); encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/ free(encoded_pkinit_authz_data); break; default: *authz_data = NULL; } /* remember to set the PREAUTH flag in the reply */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; *pa_request_context = reqctx; reqctx = NULL; cleanup: if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { pkiDebug("pkinit_verify_padata failed: creating e-data\n"); if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, plgctx->opts, retval, e_data)) pkiDebug("pkinit_create_edata failed\n"); } switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); if (cksum.contents != NULL) free(cksum.contents); if (der_req != NULL) krb5_free_data(context, der_req); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); } if (tmp_as_req != NULL) k5int_krb5_free_kdc_req(context, tmp_as_req); if (authp_data.data != NULL) free(authp_data.data); if (krb5_authz.data != NULL) free(krb5_authz.data); if (reqctx != NULL) pkinit_fini_kdc_req_context(context, reqctx); if (auth_pack != NULL) free_krb5_auth_pack(&auth_pack); if (auth_pack9 != NULL) free_krb5_auth_pack_draft9(context, &auth_pack9); return retval; }
krb5_error_code KRB5_CALLCONV krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_data *outbuf) { krb5_error_code retval; krb5_checksum checksum; krb5_checksum *checksump = 0; krb5_auth_context new_auth_context; krb5_enctype *desired_etypes = NULL; krb5_ap_req request; krb5_data *scratch = 0; krb5_data *toutbuf; request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK; request.authenticator.ciphertext.data = NULL; request.ticket = 0; if (!in_creds->ticket.length) return(KRB5_NO_TKT_SUPPLIED); if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) && !(ap_req_options & AP_OPTS_MUTUAL_REQUIRED)) return(EINVAL); /* we need a native ticket */ if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket))) return(retval); /* verify that the ticket is not expired */ if ((retval = krb5_validate_times(context, &in_creds->times)) != 0) goto cleanup; /* generate auth_context if needed */ if (*auth_context == NULL) { if ((retval = krb5_auth_con_init(context, &new_auth_context))) goto cleanup; *auth_context = new_auth_context; } if ((*auth_context)->keyblock != NULL) { krb5_free_keyblock(context, (*auth_context)->keyblock); (*auth_context)->keyblock = NULL; } /* set auth context keyblock */ if ((retval = krb5_copy_keyblock(context, &in_creds->keyblock, &((*auth_context)->keyblock)))) goto cleanup; /* generate seq number if needed */ if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && ((*auth_context)->local_seq_number == 0)) if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock, &(*auth_context)->local_seq_number))) goto cleanup; /* generate subkey if needed */ if (!in_data &&(*auth_context)->checksum_func) { retval = (*auth_context)->checksum_func( context, *auth_context, (*auth_context)->checksum_func_data, &in_data); if (retval) goto cleanup; } if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) { retval = krb5int_generate_and_save_subkey (context, *auth_context, &in_creds->keyblock, in_creds->keyblock.enctype); if (retval) goto cleanup; } if (in_data) { if ((*auth_context)->req_cksumtype == 0x8003) { /* XXX Special hack for GSSAPI */ checksum.checksum_type = 0x8003; checksum.length = in_data->length; checksum.contents = (krb5_octet *) in_data->data; } else { krb5_cksumtype cksumtype; retval = krb5int_c_mandatory_cksumtype(context, (*auth_context)->keyblock->enctype, &cksumtype); if (retval) goto cleanup_cksum; if ((*auth_context)->req_cksumtype) cksumtype = (*auth_context)->req_cksumtype; if ((retval = krb5_c_make_checksum(context, cksumtype, (*auth_context)->keyblock, KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, in_data, &checksum))) goto cleanup_cksum; } checksump = &checksum; } /* Generate authenticator */ if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof( krb5_authenticator))) == NULL) { retval = ENOMEM; goto cleanup_cksum; } if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) { if ((*auth_context)->permitted_etypes == NULL) { retval = krb5_get_tgs_ktypes(context, in_creds->server, &desired_etypes); if (retval) goto cleanup_cksum; } else desired_etypes = (*auth_context)->permitted_etypes; } if ((retval = krb5_generate_authenticator(context, (*auth_context)->authentp, in_creds->client, checksump, (*auth_context)->send_subkey, (*auth_context)->local_seq_number, in_creds->authdata, (*auth_context)->ad_context, desired_etypes, in_creds->keyblock.enctype))) goto cleanup_cksum; /* encode the authenticator */ if ((retval = encode_krb5_authenticator((*auth_context)->authentp, &scratch))) goto cleanup_cksum; /* call the encryption routine */ if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock, KRB5_KEYUSAGE_AP_REQ_AUTH, scratch, &request.authenticator))) goto cleanup_cksum; if ((retval = encode_krb5_ap_req(&request, &toutbuf))) goto cleanup_cksum; *outbuf = *toutbuf; free(toutbuf); cleanup_cksum: /* Null out these fields, to prevent pointer sharing problems; * they were supplied by the caller */ if ((*auth_context)->authentp != NULL) { (*auth_context)->authentp->client = NULL; (*auth_context)->authentp->checksum = NULL; } if (checksump && checksump->checksum_type != 0x8003) free(checksump->contents); cleanup: if (desired_etypes && desired_etypes != (*auth_context)->permitted_etypes) free(desired_etypes); if (request.ticket) krb5_free_ticket(context, request.ticket); if (request.authenticator.ciphertext.data) { (void) memset(request.authenticator.ciphertext.data, 0, request.authenticator.ciphertext.length); free(request.authenticator.ciphertext.data); } if (scratch) { memset(scratch->data, 0, scratch->length); free(scratch->data); free(scratch); } return retval; }
static krb5_error_code make_seal_token_v1 (krb5_context context, krb5_keyblock *enc, krb5_keyblock *seq, gssint_uint64 *seqnum, int direction, gss_buffer_t text, gss_buffer_t token, int signalg, size_t cksum_size, int sealalg, int do_encrypt, int toktype, int bigend, gss_OID oid) { krb5_error_code code; size_t sumlen; char *data_ptr; krb5_data plaind; krb5_checksum md5cksum; krb5_checksum cksum; /* msglen contains the message length * we are signing/encrypting. tmsglen * contains the length of the message * we plan to write out to the token. * tlen is the length of the token * including header. */ unsigned int conflen=0, tmsglen, tlen, msglen; unsigned char *t, *ptr; unsigned char *plain; unsigned char pad; krb5_keyusage sign_usage = KG_USAGE_SIGN; assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG)); /* create the token buffer */ /* Do we need confounder? */ if (do_encrypt || (!bigend && (toktype == KG_TOK_SEAL_MSG))) conflen = kg_confounder_size(context, enc); else conflen = 0; if (toktype == KG_TOK_SEAL_MSG) { switch (sealalg) { case SEAL_ALG_MICROSOFT_RC4: msglen = conflen + text->length+1; pad = 1; break; default: /* XXX knows that des block size is 8 */ msglen = (conflen+text->length+8)&(~7); pad = 8-(text->length%8); } tmsglen = msglen; } else { tmsglen = 0; msglen = text->length; pad = 0; } tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen); if ((t = (unsigned char *) xmalloc(tlen)) == NULL) return(ENOMEM); /*** fill in the token */ ptr = t; g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype); /* 0..1 SIGN_ALG */ store_16_le(signalg, &ptr[0]); /* 2..3 SEAL_ALG or Filler */ if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) { store_16_le(sealalg, &ptr[2]); } else { /* No seal */ ptr[2] = 0xff; ptr[3] = 0xff; } /* 4..5 Filler */ ptr[4] = 0xff; ptr[5] = 0xff; /* pad the plaintext, encrypt if needed, and stick it in the token */ /* initialize the the cksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; if (toktype != KG_TOK_SEAL_MSG) sign_usage = 15; break; default: case SGN_ALG_DES_MAC: abort (); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); if (code) { xfree(t); return(code); } md5cksum.length = sumlen; if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) { xfree(t); return(ENOMEM); } if (conflen) { if ((code = kg_make_confounder(context, enc, plain))) { xfree(plain); xfree(t); return(code); } } memcpy(plain+conflen, text->value, text->length); if (pad) memset(plain+conflen+text->length, pad, pad); /* compute the checksum */ /* 8 = head of token body as specified by mech spec */ if (! (data_ptr = (char *) xmalloc(8 + (bigend ? text->length : msglen)))) { xfree(plain); xfree(t); return(ENOMEM); } (void) memcpy(data_ptr, ptr-2, 8); if (bigend) (void) memcpy(data_ptr+8, text->value, text->length); else (void) memcpy(data_ptr+8, plain, msglen); plaind.length = 8 + (bigend ? text->length : msglen); plaind.data = data_ptr; code = krb5_c_make_checksum(context, md5cksum.checksum_type, seq, sign_usage, &plaind, &md5cksum); xfree(data_ptr); if (code) { xfree(plain); xfree(t); return(code); } switch(signalg) { case SGN_ALG_DES_MAC_MD5: case 3: if ((code = kg_encrypt(context, seq, KG_USAGE_SEAL, (g_OID_equal(oid, gss_mech_krb5_old) ? seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16))) { krb5_free_checksum_contents(context, &md5cksum); xfree (plain); xfree(t); return code; } cksum.length = cksum_size; cksum.contents = md5cksum.contents + 16 - cksum.length; memcpy(ptr+14, cksum.contents, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: /* * Using key derivation, the call to krb5_c_make_checksum * already dealt with encrypting. */ if (md5cksum.length != cksum_size) abort (); memcpy (ptr+14, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: memcpy (ptr+14, md5cksum.contents, cksum_size); break; } krb5_free_checksum_contents(context, &md5cksum); /* create the seq_num */ if ((code = kg_make_seq_num(context, seq, direction?0:0xff, (krb5_ui_4)*seqnum, ptr+14, ptr+6))) { xfree (plain); xfree(t); return(code); } if (do_encrypt) { switch(sealalg) { case SEAL_ALG_MICROSOFT_RC4: { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; int i; store_32_be(*seqnum, bigend_seqnum); code = krb5_copy_keyblock (context, enc, &enc_key); if (code) { xfree(plain); xfree(t); return(code); } assert (enc_key->length == 16); for (i = 0; i <= 15; i++) ((char *) enc_key->contents)[i] ^=0xf0; code = kg_arcfour_docrypt (enc_key, 0, bigend_seqnum, 4, plain, tmsglen, ptr+14+cksum_size); krb5_free_keyblock (context, enc_key); if (code) { xfree(plain); xfree(t); return(code); } } break; default: if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, (krb5_pointer) plain, (krb5_pointer) (ptr+cksum_size+14), tmsglen))) { xfree(plain); xfree(t); return(code); } } }else { if (tmsglen) memcpy(ptr+14+cksum_size, plain, tmsglen); } xfree(plain); /* that's it. return the token */ (*seqnum)++; *seqnum &= 0xffffffffL; token->length = tlen; token->value = (void *) t; return(0); }
/* * Create a CAMMAC for contents, using enc_tkt and the first key from krbtgt * for the KDC verifier. Set *cammac_out to a single-element authdata list * containing the CAMMAC inside an IF-RELEVANT container. */ krb5_error_code cammac_create(krb5_context context, krb5_enc_tkt_part *enc_tkt, krb5_keyblock *server_key, krb5_db_entry *krbtgt, krb5_authdata **contents, krb5_authdata ***cammac_out) { krb5_error_code ret; krb5_data *der_authdata = NULL, *der_enctkt = NULL, *der_cammac = NULL; krb5_authdata ad, *list[2]; krb5_cammac cammac; krb5_verifier_mac kdc_verifier, svc_verifier; krb5_key_data *kd; krb5_keyblock tgtkey; krb5_checksum kdc_cksum, svc_cksum; *cammac_out = NULL; memset(&tgtkey, 0, sizeof(tgtkey)); memset(&kdc_cksum, 0, sizeof(kdc_cksum)); memset(&svc_cksum, 0, sizeof(svc_cksum)); /* Fetch the first krbtgt key for the KDC verifier. */ ret = krb5_dbe_find_enctype(context, krbtgt, -1, -1, 0, &kd); if (ret) goto cleanup; ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &tgtkey, NULL); if (ret) goto cleanup; /* Checksum the reply with contents as authdata for the KDC verifier. */ ret = encode_kdcver_encpart(enc_tkt, contents, &der_enctkt); if (ret) goto cleanup; ret = krb5_c_make_checksum(context, 0, &tgtkey, KRB5_KEYUSAGE_CAMMAC, der_enctkt, &kdc_cksum); if (ret) goto cleanup; kdc_verifier.princ = NULL; kdc_verifier.kvno = kd->key_data_kvno; kdc_verifier.enctype = ENCTYPE_NULL; kdc_verifier.checksum = kdc_cksum; /* Encode the authdata and checksum it for the service verifier. */ ret = encode_krb5_authdata(contents, &der_authdata); if (ret) goto cleanup; ret = krb5_c_make_checksum(context, 0, server_key, KRB5_KEYUSAGE_CAMMAC, der_authdata, &svc_cksum); if (ret) goto cleanup; svc_verifier.princ = NULL; svc_verifier.kvno = 0; svc_verifier.enctype = ENCTYPE_NULL; svc_verifier.checksum = svc_cksum; cammac.elements = contents; cammac.kdc_verifier = &kdc_verifier; cammac.svc_verifier = &svc_verifier; cammac.other_verifiers = NULL; ret = encode_krb5_cammac(&cammac, &der_cammac); if (ret) goto cleanup; /* Wrap the encoded CAMMAC in an IF-RELEVANT container and return it as a * single-element authdata list. */ ad.ad_type = KRB5_AUTHDATA_CAMMAC; ad.length = der_cammac->length; ad.contents = (uint8_t *)der_cammac->data; list[0] = &ad; list[1] = NULL; ret = krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT, list, cammac_out); cleanup: krb5_free_data(context, der_enctkt); krb5_free_data(context, der_authdata); krb5_free_data(context, der_cammac); krb5_free_keyblock_contents(context, &tgtkey); krb5_free_checksum_contents(context, &kdc_cksum); krb5_free_checksum_contents(context, &svc_cksum); return ret; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context = NULL; size_t i; struct test *test; krb5_keyblock kb, *kbp; krb5_checksum cksum; krb5_cksumtype mtype; krb5_boolean valid, verbose = FALSE; int status = 0; if (argc >= 2 && strcmp(argv[1], "-v") == 0) verbose = TRUE; for (i = 0; i < sizeof(test_cases) / sizeof(*test_cases); i++) { test = &test_cases[i]; if (test->enctype != 0) { kb.magic = KV5M_KEYBLOCK; kb.enctype = test->enctype; kb.length = test->keybits.length; kb.contents = (unsigned char *)test->keybits.data; kbp = &kb; } else kbp = NULL; ret = krb5_c_make_checksum(context, test->sumtype, kbp, test->usage, &test->plaintext, &cksum); assert(!ret); if (verbose) { char buf[64]; krb5_cksumtype_to_string(test->sumtype, buf, sizeof(buf)); printf("\nTest %d:\n", (int)i); printf("Plaintext: %.*s\n", (int)test->plaintext.length, test->plaintext.data); printf("Checksum type: %s\n", buf); if (test->enctype != 0) { krb5_enctype_to_name(test->enctype, FALSE, buf, sizeof(buf)); printf("Enctype: %s\n", buf); printhex("Key: ", test->keybits.data, test->keybits.length); printf("Key usage: %d\n", (int)test->usage); } printhex("Checksum: ", cksum.contents, cksum.length); } if (test->cksum.length != cksum.length || memcmp(test->cksum.data, cksum.contents, cksum.length) != 0) { printf("derive test %d failed\n", (int)i); status = 1; if (!verbose) break; } /* Test that the checksum verifies successfully. */ ret = krb5_c_verify_checksum(context, kbp, test->usage, &test->plaintext, &cksum, &valid); assert(!ret); if (!valid) { printf("test %d verify failed\n", (int)i); status = 1; if (!verbose) break; } if (kbp != NULL) { ret = krb5int_c_mandatory_cksumtype(context, kbp->enctype, &mtype); assert(!ret); if (test->sumtype == mtype) { /* Test that a checksum type of 0 uses the mandatory checksum * type for the key. */ cksum.checksum_type = 0; ret = krb5_c_verify_checksum(context, kbp, test->usage, &test->plaintext, &cksum, &valid); assert(!ret && valid); } } krb5_free_checksum_contents(context, &cksum); } return status; }
static krb5_error_code pkinit_server_verify_padata(krb5_context context, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data * data, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval = 0; krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; krb5_auth_pack *auth_pack = NULL; krb5_auth_pack_draft9 *auth_pack9 = NULL; pkinit_kdc_context plgctx = NULL; pkinit_kdc_req_context reqctx; krb5_preauthtype pa_type; krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data *der_req = NULL; int valid_eku = 0, valid_san = 0; krb5_kdc_req *tmp_as_req = NULL; krb5_data k5data; int is_signed = 1; krb5_keyblock *armor_key; pkiDebug("pkinit_verify_padata: entered!\n"); if (data == NULL || data->length <= 0 || data->contents == NULL) return 0; /* Remove (along with armor_key) when FAST PKINIT is settled. */ retval = fast_kdc_get_armor_key(context, server_get_entry_data, request, client, &armor_key); if (retval == 0 && armor_key != NULL) { /* Don't allow PKINIT if the client used FAST. */ krb5_free_keyblock(context, armor_key); return EINVAL; } if (pa_plugin_context == NULL || e_data == NULL) return EINVAL; plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) return 0; #ifdef DEBUG_ASN1 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); #endif /* create a per-request context */ retval = pkinit_init_kdc_req_context(context, (void **)&reqctx); if (retval) goto cleanup; reqctx->pa_type = data->pa_type; PADATA_TO_KRB5DATA(data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); pa_type = (int)data->pa_type; retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, "/tmp/kdc_signed_data"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, plgctx->opts->require_crl_checking, reqp->signedAuthPack.data, reqp->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length, &is_signed); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); pa_type = KRB5_PADATA_PK_AS_REQ_OLD; retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, "/tmp/kdc_signed_data_draft9"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, plgctx->opts->require_crl_checking, reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length, NULL); break; default: pkiDebug("unrecognized pa_type = %d\n", data->pa_type); retval = EINVAL; goto cleanup; } if (retval) { pkiDebug("pkcs7_signeddata_verify failed\n"); goto cleanup; } if (is_signed) { retval = verify_client_san(context, plgctx, reqctx, request->client, &valid_san); if (retval) goto cleanup; if (!valid_san) { pkiDebug("%s: did not find an acceptable SAN in user " "certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto cleanup; } retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); if (retval) goto cleanup; if (!valid_eku) { pkiDebug("%s: did not find an acceptable EKU in user " "certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; goto cleanup; } } else { /* !is_signed */ if (!krb5_principal_compare(context, request->client, krb5_anonymous_principal())) { retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "Pkinit request not " "signed, but client not anonymous."); goto cleanup; } } #ifdef DEBUG_ASN1 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); #endif OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); if (retval) { pkiDebug("failed to decode krb5_auth_pack\n"); goto cleanup; } /* check dh parameters */ if (auth_pack->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } else if (!is_signed) { /*Anonymous pkinit requires DH*/ retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "Anonymous pkinit without DH public value not supported."); goto cleanup; } /* * The KDC may have modified the request after decoding it. * We need to compute the checksum on the data that * came from the client. Therefore, we use the original * packet contents. */ retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); if (retval) { pkiDebug("decode_krb5_as_req returned %d\n", (int)retval); goto cleanup; } retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; } retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || memcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, cksum.length)) { pkiDebug("failed to match the checksum\n"); #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("received checksum type=%d size=%d ", auth_pack->pkAuthenticator.paChecksum.checksum_type, auth_pack->pkAuthenticator.paChecksum.length); print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, auth_pack->pkAuthenticator.paChecksum.length); pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length); print_buffer(cksum.contents, cksum.length); #endif retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; goto cleanup; } /* check if kdcPkId present and match KDC's subjectIdentifier */ if (reqp->kdcPkId.data != NULL) { int valid_kdcPkId = 0; retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId); if (retval) goto cleanup; if (!valid_kdcPkId) pkiDebug("kdcPkId in AS_REQ does not match KDC's cert" "RFC says to ignore and proceed\n"); } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack = auth_pack; auth_pack = NULL; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9); if (retval) { pkiDebug("failed to decode krb5_auth_pack_draft9\n"); goto cleanup; } if (auth_pack9->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack9->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack9 = auth_pack9; auth_pack9 = NULL; break; } /* * This code used to generate ad-initial-verified-cas authorization data. * However that has been removed until the ad-kdc-issued discussion can * happen in the working group. Dec 2009 */ /* return authorization data to be included in the ticket */ switch ((int)data->pa_type) { default: *authz_data = NULL; } /* remember to set the PREAUTH flag in the reply */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; *pa_request_context = reqctx; reqctx = NULL; cleanup: if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { pkiDebug("pkinit_verify_padata failed: creating e-data\n"); if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, plgctx->opts, retval, e_data)) pkiDebug("pkinit_create_edata failed\n"); } switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); free(cksum.contents); if (der_req != NULL) krb5_free_data(context, der_req); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); } if (tmp_as_req != NULL) k5int_krb5_free_kdc_req(context, tmp_as_req); free(authp_data.data); free(krb5_authz.data); if (reqctx != NULL) pkinit_fini_kdc_req_context(context, reqctx); if (auth_pack != NULL) free_krb5_auth_pack(&auth_pack); if (auth_pack9 != NULL) free_krb5_auth_pack_draft9(context, &auth_pack9); return retval; }
krb5_error_code krb5int_fast_prep_req(krb5_context context, struct krb5int_fast_request_state *state, krb5_kdc_req *request, const krb5_data *to_be_checksummed, kdc_req_encoder_proc encoder, krb5_data **encoded_request) { krb5_error_code retval = 0; krb5_pa_data *pa_array[2], **pa_tgs_array = NULL; krb5_pa_data pa[2]; krb5_fast_req fast_req; krb5_pa_data *tgs = NULL; krb5_fast_armored_req *armored_req = NULL; krb5_data *encoded_fast_req = NULL; krb5_data *encoded_armored_req = NULL; krb5_data *local_encoded_result = NULL; int i, j; assert(state != NULL); assert(state->fast_outer_request.padata == NULL); memset(pa_array, 0, sizeof(pa_array)); if (state->armor_key == NULL) { return encoder(request, encoded_request); } TRACE_FAST_ENCODE(context); state->nonce = request->nonce; fast_req.req_body = request; if (fast_req.req_body->padata == NULL) { fast_req.req_body->padata = calloc(1, sizeof(krb5_pa_data *)); if (fast_req.req_body->padata == NULL) retval = ENOMEM; } fast_req.fast_options = state->fast_options; if (retval == 0 && (tgs = krb5int_find_pa_data(context, fast_req.req_body->padata, KRB5_PADATA_AP_REQ)) != NULL) { krb5_pa_data **paptr = &fast_req.req_body->padata[0]; for (i = 0, j = 0; paptr[j] != NULL; j++) { if (paptr[j]->pa_type == KRB5_PADATA_AP_REQ) paptr[j] = NULL; else paptr[i++] = paptr[j]; } paptr[i] = NULL; } if (retval == 0) retval = encode_krb5_fast_req(&fast_req, &encoded_fast_req); if (retval == 0) { armored_req = calloc(1, sizeof(krb5_fast_armored_req)); if (armored_req == NULL) retval = ENOMEM; } if (retval == 0) armored_req->armor = state->armor; if (retval ==0) retval = krb5_c_make_checksum(context, 0, state->armor_key, KRB5_KEYUSAGE_FAST_REQ_CHKSUM, to_be_checksummed, &armored_req->req_checksum); if (retval == 0) retval = krb5_encrypt_helper(context, state->armor_key, KRB5_KEYUSAGE_FAST_ENC, encoded_fast_req, &armored_req->enc_part); if (retval == 0) retval = encode_krb5_pa_fx_fast_request(armored_req, &encoded_armored_req); if (retval == 0) { pa[0].pa_type = KRB5_PADATA_FX_FAST; pa[0].contents = (unsigned char *) encoded_armored_req->data; pa[0].length = encoded_armored_req->length; if (tgs) { retval = make_tgs_outer_padata(tgs, pa, request->padata, &pa_tgs_array); state->fast_outer_request.padata = pa_tgs_array; } else { pa_array[0] = &pa[0]; state->fast_outer_request.padata = pa_array; } } if (retval == 0) retval = encoder(&state->fast_outer_request, &local_encoded_result); if (retval == 0) { *encoded_request = local_encoded_result; local_encoded_result = NULL; } if (encoded_armored_req) krb5_free_data(context, encoded_armored_req); if (armored_req) { armored_req->armor = NULL; /*owned by state*/ krb5_free_fast_armored_req(context, armored_req); } if (encoded_fast_req) krb5_free_data(context, encoded_fast_req); if (local_encoded_result) krb5_free_data(context, local_encoded_result); if (tgs) { free(tgs->contents); free(tgs); } state->fast_outer_request.padata = NULL; free(pa_tgs_array); return retval; }
static krb5_error_code client_process(krb5_context kcontext, void *client_plugin_context, void *client_request_context, krb5_get_init_creds_opt *opt, preauth_get_client_data_proc client_get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *pa_data, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data **out_pa_data) { krb5_pa_data *send_pa; krb5_checksum checksum; krb5_enctype enctype; krb5_cksumtype *cksumtypes; krb5_error_code status = 0; krb5_int32 cksumtype, *enctypes; unsigned int i, n_enctypes, cksumtype_count; int num_gic_info = 0; krb5_gic_opt_pa_data *gic_info; status = krb5_get_init_creds_opt_get_pa(kcontext, opt, &num_gic_info, &gic_info); if (status && status != ENOENT) { #ifdef DEBUG fprintf(stderr, "Error from krb5_get_init_creds_opt_get_pa: %s\n", error_message(status)); #endif return status; } #ifdef DEBUG fprintf(stderr, "(cksum_body) Got the following gic options:\n"); #endif for (i = 0; i < num_gic_info; i++) { #ifdef DEBUG fprintf(stderr, " '%s' = '%s'\n", gic_info[i].attr, gic_info[i].value); #endif } krb5_get_init_creds_opt_free_pa(kcontext, num_gic_info, gic_info); memset(&checksum, 0, sizeof(checksum)); /* Get the user's long-term key if we haven't asked for it yet. Try * all of the encryption types which the server supports. */ if (as_key->length == 0) { if ((pa_data != NULL) && (pa_data->length >= 4)) { #ifdef DEBUG fprintf(stderr, "%d bytes of preauth data.\n", pa_data->length); #endif n_enctypes = pa_data->length / 4; enctypes = (krb5_int32*) pa_data->contents; } else { n_enctypes = request->nktypes; } for (i = 0; i < n_enctypes; i++) { if ((pa_data != NULL) && (pa_data->length >= 4)) { memcpy(&enctype, pa_data->contents + 4 * i, 4); enctype = ntohl(enctype); } else { enctype = request->ktype[i]; } #ifdef DEBUG fprintf(stderr, "Asking for AS key (type = %d).\n", enctype); #endif status = (*gak_fct)(kcontext, request->client, enctype, prompter, prompter_data, salt, s2kparams, as_key, gak_data); if (status == 0) break; } if (status != 0) return status; } #ifdef DEBUG fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype); #endif /* Determine an appropriate checksum type for this key. */ cksumtype_count = 0; cksumtypes = NULL; status = krb5_c_keyed_checksum_types(kcontext, as_key->enctype, &cksumtype_count, &cksumtypes); if (status != 0) return status; /* Generate the checksum. */ for (i = 0; i < cksumtype_count; i++) { status = krb5_c_make_checksum(kcontext, cksumtypes[i], as_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, encoded_request_body, &checksum); if (status == 0) { #ifdef DEBUG fprintf(stderr, "Made checksum (type = %d, %d bytes).\n", checksum.checksum_type, encoded_request_body->length); #endif break; } } cksumtype = htonl(cksumtypes[i]); krb5_free_cksumtypes(kcontext, cksumtypes); if (status != 0) { if (checksum.length > 0) krb5_free_checksum_contents(kcontext, &checksum); return status; } /* Allocate the preauth data structure. */ send_pa = malloc(sizeof(krb5_pa_data)); if (send_pa == NULL) { krb5_free_checksum_contents(kcontext, &checksum); return ENOMEM; } send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ; send_pa->length = 4 + checksum.length; send_pa->contents = malloc(4 + checksum.length); if (send_pa->contents == NULL) { krb5_free_checksum_contents(kcontext, &checksum); free(send_pa); return ENOMEM; } /* Store the checksum. */ memcpy(send_pa->contents, &cksumtype, 4); memcpy(send_pa->contents + 4, checksum.contents, checksum.length); *out_pa_data = send_pa; /* Clean up. */ krb5_free_checksum_contents(kcontext, &checksum); return 0; }
krb5_error_code gss_krb5int_make_seal_token_v3 (krb5_context context, krb5_gss_ctx_id_rec *ctx, const gss_buffer_desc * message, gss_buffer_t token, int conf_req_flag, int toktype) { size_t bufsize = 16; unsigned char *outbuf = 0; krb5_error_code err; int key_usage; unsigned char acceptor_flag; const gss_buffer_desc *message2 = message; #ifdef CFX_EXERCISE size_t rrc; #endif size_t ec; unsigned short tok_id; krb5_checksum sum; krb5_keyblock *key; ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0); ASSERT(ctx->big_endian == 0); acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; key_usage = (toktype == KG_TOK_WRAP_MSG ? (ctx->initiate ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL) : (ctx->initiate ? KG_USAGE_INITIATOR_SIGN : KG_USAGE_ACCEPTOR_SIGN)); if (ctx->have_acceptor_subkey) { key = ctx->acceptor_subkey; } else { key = ctx->enc; } #ifdef _KERNEL context->kef_cipher_mt = get_cipher_mech_type(context, key); context->kef_hash_mt = get_hash_mech_type(context, key); if ((err = init_key_kef(context->kef_cipher_mt, key))) { return (GSS_S_FAILURE); } #endif /* _KERNEL */ #ifdef CFX_EXERCISE { static int initialized = 0; if (!initialized) { srand(time(0)); initialized = 1; } } #endif if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { krb5_data plain; krb5_enc_data cipher; size_t ec_max; size_t tlen; /* 300: Adds some slop. */ if (SIZE_MAX - 300 < message->length) return ENOMEM; ec_max = SIZE_MAX - message->length - 300; if (ec_max > 0xffff) ec_max = 0xffff; /* * EC should really be a multiple (1) of the number of octets that * the cryptosystem would pad by if we didn't have the filler. * * For AES-CTS this will always be 0 and we expect no further * enctypes, so there should be no issue here. */ ec = 0; plain.length = message->length + 16 + ec; plain.data = MALLOC(plain.length); if (plain.data == NULL) return ENOMEM; /* Get size of ciphertext. */ if ((err = krb5_c_encrypt_length(context, ctx->enc->enctype, plain.length, &tlen))) { FREE(plain.data, plain.length); return (err); } bufsize = 16 + tlen; /* Allocate space for header plus encrypted data. */ outbuf = MALLOC(bufsize); if (outbuf == NULL) { FREE(plain.data, plain.length); return ENOMEM; } /* TOK_ID */ store_16_be(0x0504, outbuf); /* flags */ outbuf[2] = (acceptor_flag | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); /* filler */ outbuf[3] = 0xff; /* EC */ store_16_be(ec, outbuf+4); /* RRC */ store_16_be(0, outbuf+6); store_64_be(ctx->seq_send, outbuf+8); (void) memcpy(plain.data, message->value, message->length); (void) memset(plain.data + message->length, 'x', ec); (void) memcpy(plain.data + message->length + ec, outbuf, 16); /* Should really use scatter/gather crypto interfaces */ cipher.ciphertext.data = (char *)outbuf + 16; cipher.ciphertext.length = bufsize - 16; cipher.enctype = key->enctype; err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher); (void) bzero(plain.data, plain.length); FREE(plain.data, plain.length); plain.data = 0; if (err) goto error; /* Now that we know we're returning a valid token.... */ ctx->seq_send++; #ifdef CFX_EXERCISE rrc = rand() & 0xffff; if (rotate_left(outbuf+16, bufsize-16, (bufsize-16) - (rrc % (bufsize - 16)))) store_16_be(rrc, outbuf+6); /* If the rotate fails, don't worry about it. */ #endif } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { krb5_data plain; /* Here, message is the application-supplied data; message2 is what goes into the output token. They may be the same, or message2 may be empty (for MIC). */ tok_id = 0x0504; wrap_with_checksum: plain.length = message->length + 16; plain.data = MALLOC(message->length + 16); if (plain.data == NULL) return ENOMEM; if (ctx->cksum_size > 0xffff) { FREE(plain.data, plain.length); return EINVAL; } bufsize = 16 + message2->length + ctx->cksum_size; outbuf = MALLOC(bufsize); if (outbuf == NULL) { FREE(plain.data, plain.length); plain.data = 0; err = ENOMEM; goto error; } /* TOK_ID */ store_16_be(tok_id, outbuf); /* flags */ outbuf[2] = (acceptor_flag | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); /* filler */ outbuf[3] = 0xff; if (toktype == KG_TOK_WRAP_MSG) { /* Use 0 for checksum calculation, substitute checksum length later. */ /* EC */ store_16_be(0, outbuf+4); /* RRC */ store_16_be(0, outbuf+6); } else { /* MIC and DEL store 0xFF in EC and RRC. */ store_16_be(0xffff, outbuf+4); store_16_be(0xffff, outbuf+6); } store_64_be(ctx->seq_send, outbuf+8); (void) memcpy(plain.data, message->value, message->length); (void) memcpy(plain.data + message->length, outbuf, 16); /* Fill in the output token -- data contents, if any, and space for the checksum. */ if (message2->length) (void) memcpy(outbuf + 16, message2->value, message2->length); sum.contents = outbuf + 16 + message2->length; sum.length = ctx->cksum_size; err = krb5_c_make_checksum(context, ctx->cksumtype, key, key_usage, &plain, &sum); bzero(plain.data, plain.length); FREE(plain.data, plain.length); plain.data = 0; if (err) { bzero(outbuf,bufsize); err = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto error; } if (sum.length != ctx->cksum_size) { err = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto error; } (void) memcpy(outbuf + 16 + message2->length, sum.contents, ctx->cksum_size); krb5_free_checksum_contents(context, &sum); sum.contents = 0; /* Now that we know we're actually generating the token... */ ctx->seq_send++; if (toktype == KG_TOK_WRAP_MSG) { #ifdef CFX_EXERCISE rrc = rand() & 0xffff; /* If the rotate fails, don't worry about it. */ if (rotate_left(outbuf+16, bufsize-16, (bufsize-16) - (rrc % (bufsize - 16)))) store_16_be(rrc, outbuf+6); #endif /* Fix up EC field. */ store_16_be(ctx->cksum_size, outbuf+4); } else { store_16_be(0xffff, outbuf+6); } } else if (toktype == KG_TOK_MIC_MSG) { tok_id = 0x0404; message2 = &empty_message; goto wrap_with_checksum; } else if (toktype == KG_TOK_DEL_CTX) { /* * Solaris Kerberos: * No token should be generated for context deletion. Just * return. */ return 0; } else { err = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto error; } token->value = outbuf; token->length = bufsize; return 0; error: FREE(outbuf, bufsize); token->value = NULL; token->length = 0; return err; }
/* Sends a request to the TGS and waits for a response. options is used for the options in the KRB_TGS_REQ. timestruct values are used for from, till, rtime " " " enctype is used for enctype " " ", and to encrypt the authorization data, sname is used for sname " " " addrs, if non-NULL, is used for addresses " " " authorization_dat, if non-NULL, is used for authorization_dat " " " second_ticket, if required by options, is used for the 2nd ticket in the req. in_cred is used for the ticket & session key in the KRB_AP_REQ header " " " (the KDC realm is extracted from in_cred->server's realm) The response is placed into *rep. rep->response.data is set to point at allocated storage which should be freed by the caller when finished. returns system errors */ static krb5_error_code krb5_send_tgs_basic(krb5_context context, krb5_data *in_data, krb5_creds *in_cred, krb5_data *outbuf) { krb5_error_code retval; krb5_checksum checksum; krb5_authenticator authent; krb5_ap_req request; krb5_data * scratch; krb5_data * toutbuf; /* Generate checksum */ if ((retval = krb5_c_make_checksum(context, context->kdc_req_sumtype, &in_cred->keyblock, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, in_data, &checksum))) { free(checksum.contents); return(retval); } /* gen authenticator */ authent.subkey = 0; authent.seq_number = 0; authent.checksum = &checksum; authent.client = in_cred->client; authent.authorization_data = in_cred->authdata; if ((retval = krb5_us_timeofday(context, &authent.ctime, &authent.cusec))) { free(checksum.contents); return(retval); } /* encode the authenticator */ if ((retval = encode_krb5_authenticator(&authent, &scratch))) { free(checksum.contents); return(retval); } free(checksum.contents); request.authenticator.ciphertext.data = 0; request.authenticator.kvno = 0; request.ap_options = 0; request.ticket = 0; if ((retval = decode_krb5_ticket(&(in_cred)->ticket, &request.ticket))) /* Cleanup scratch and scratch data */ goto cleanup_data; /* call the encryption routine */ if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock, KRB5_KEYUSAGE_TGS_REQ_AUTH, scratch, &request.authenticator))) goto cleanup_ticket; retval = encode_krb5_ap_req(&request, &toutbuf); /* Solaris Kerberos */ if (retval == 0) { *outbuf = *toutbuf; krb5_xfree(toutbuf); } memset(request.authenticator.ciphertext.data, 0, request.authenticator.ciphertext.length); free(request.authenticator.ciphertext.data); cleanup_ticket: krb5_free_ticket(context, request.ticket); cleanup_data: memset(scratch->data, 0, scratch->length); free(scratch->data); free(scratch); return retval; }
static krb5_error_code pkinit_server_return_padata(krb5_context context, krb5_pa_data * padata, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_kdc_rep * reply, struct _krb5_key_data * client_key, krb5_keyblock * encrypting_key, krb5_pa_data ** send_pa, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context) { krb5_error_code retval = 0; krb5_data scratch = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; int i = 0; unsigned char *subjectPublicKey = NULL; unsigned char *dh_pubkey = NULL, *server_key = NULL; unsigned int subjectPublicKey_len = 0; unsigned int server_key_len = 0, dh_pubkey_len = 0; krb5_kdc_dh_key_info dhkey_info; krb5_data *encoded_dhkey_info = NULL; krb5_pa_pk_as_rep *rep = NULL; krb5_pa_pk_as_rep_draft9 *rep9 = NULL; krb5_data *out_data = NULL; krb5_enctype enctype = -1; krb5_reply_key_pack *key_pack = NULL; krb5_reply_key_pack_draft9 *key_pack9 = NULL; krb5_data *encoded_key_pack = NULL; unsigned int num_types; krb5_cksumtype *cksum_types = NULL; pkinit_kdc_context plgctx; pkinit_kdc_req_context reqctx; int fixed_keypack = 0; *send_pa = NULL; if (padata == NULL || padata->length <= 0 || padata->contents == NULL) return 0; if (pa_request_context == NULL || *pa_request_context == NULL) { pkiDebug("missing request context \n"); return EINVAL; } plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) { pkiDebug("Unable to locate correct realm context\n"); return ENOENT; } pkiDebug("pkinit_return_padata: entered!\n"); reqctx = (pkinit_kdc_req_context)*pa_request_context; if (encrypting_key->contents) { free(encrypting_key->contents); encrypting_key->length = 0; encrypting_key->contents = NULL; } for(i = 0; i < request->nktypes; i++) { enctype = request->ktype[i]; if (!krb5_c_valid_enctype(enctype)) continue; else { pkiDebug("KDC picked etype = %d\n", enctype); break; } } if (i == request->nktypes) { retval = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: init_krb5_pa_pk_as_rep(&rep); if (rep == NULL) { retval = ENOMEM; goto cleanup; } /* let's assume it's RSA. we'll reset it to DH if needed */ rep->choice = choice_pa_pk_as_rep_encKeyPack; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: init_krb5_pa_pk_as_rep_draft9(&rep9); if (rep9 == NULL) { retval = ENOMEM; goto cleanup; } rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; break; default: retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } if (reqctx->rcv_auth_pack != NULL && reqctx->rcv_auth_pack->clientPublicValue != NULL) { subjectPublicKey = reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data; subjectPublicKey_len = reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length; rep->choice = choice_pa_pk_as_rep_dhInfo; } else if (reqctx->rcv_auth_pack9 != NULL && reqctx->rcv_auth_pack9->clientPublicValue != NULL) { subjectPublicKey = reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data; subjectPublicKey_len = reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length; rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData; } /* if this DH, then process finish computing DH key */ if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo || rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) { pkiDebug("received DH key delivery AS REQ\n"); retval = server_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, subjectPublicKey, subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, &server_key, &server_key_len); if (retval) { pkiDebug("failed to process/create dh paramters\n"); goto cleanup; } } if ((rep9 != NULL && rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { retval = pkinit_octetstring2key(context, enctype, server_key, server_key_len, encrypting_key); if (retval) { pkiDebug("pkinit_octetstring2key failed: %s\n", error_message(retval)); goto cleanup; } dhkey_info.subjectPublicKey.length = dh_pubkey_len; dhkey_info.subjectPublicKey.data = dh_pubkey; dhkey_info.nonce = request->nonce; dhkey_info.dhKeyExpiration = 0; retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, &encoded_dhkey_info); if (retval) { pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, "/tmp/kdc_dh_key_info"); #endif switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1, (unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, &rep->u.dh_Info.dhSignedData.data, &rep->u.dh_Info.dhSignedData.length); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1, (unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, &rep9->u.dhSignedData.data, &rep9->u.dhSignedData.length); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } break; } } else { pkiDebug("received RSA key delivery AS REQ\n"); retval = krb5_c_make_random_key(context, enctype, encrypting_key); if (retval) { pkiDebug("unable to make a session key\n"); goto cleanup; } /* check if PA_TYPE of 132 is present which means the client is * requesting that a checksum is send back instead of the nonce */ for (i = 0; request->padata[i] != NULL; i++) { pkiDebug("%s: Checking pa_type 0x%08x\n", __FUNCTION__, request->padata[i]->pa_type); if (request->padata[i]->pa_type == 132) fixed_keypack = 1; } pkiDebug("%s: return checksum instead of nonce = %d\n", __FUNCTION__, fixed_keypack); /* if this is an RFC reply or draft9 client requested a checksum * in the reply instead of the nonce, create an RFC-style keypack */ if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) { init_krb5_reply_key_pack(&key_pack); if (key_pack == NULL) { retval = ENOMEM; goto cleanup; } /* retrieve checksums for a given enctype of the reply key */ retval = krb5_c_keyed_checksum_types(context, encrypting_key->enctype, &num_types, &cksum_types); if (retval) goto cleanup; /* pick the first of acceptable enctypes for the checksum */ retval = krb5_c_make_checksum(context, cksum_types[0], encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, req_pkt, &key_pack->asChecksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); print_buffer(key_pack->asChecksum.contents, key_pack->asChecksum.length); pkiDebug("encrypting key (%d)\n", encrypting_key->length); print_buffer(encrypting_key->contents, encrypting_key->length); #endif krb5_copy_keyblock_contents(context, encrypting_key, &key_pack->replyKey); retval = k5int_encode_krb5_reply_key_pack(key_pack, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); goto cleanup; } } switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: rep->choice = choice_pa_pk_as_rep_encKeyPack; retval = cms_envelopeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, (unsigned char *)encoded_key_pack->data, encoded_key_pack->length, &rep->u.encKeyPack.data, &rep->u.encKeyPack.length); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: /* if the request is from the broken draft9 client that * expects back a nonce, create it now */ if (!fixed_keypack) { init_krb5_reply_key_pack_draft9(&key_pack9); if (key_pack9 == NULL) { retval = ENOMEM; goto cleanup; } key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce; krb5_copy_keyblock_contents(context, encrypting_key, &key_pack9->replyKey); retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); goto cleanup; } } rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; retval = cms_envelopeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, (unsigned char *)encoded_key_pack->data, encoded_key_pack->length, &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length); break; } if (retval) { pkiDebug("failed to create pkcs7 enveloped data: %s\n", error_message(retval)); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_key_pack->data, encoded_key_pack->length, "/tmp/kdc_key_pack"); switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: print_buffer_bin(rep->u.encKeyPack.data, rep->u.encKeyPack.length, "/tmp/kdc_enc_key_pack"); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: print_buffer_bin(rep9->u.encKeyPack.data, rep9->u.encKeyPack.length, "/tmp/kdc_enc_key_pack"); break; } #endif } switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); break; } if (retval) { pkiDebug("failed to encode AS_REP\n"); goto cleanup; } #ifdef DEBUG_ASN1 if (out_data != NULL) print_buffer_bin((unsigned char *)out_data->data, out_data->length, "/tmp/kdc_as_rep"); #endif *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); if (*send_pa == NULL) { retval = ENOMEM; free(out_data->data); free(out_data); out_data = NULL; goto cleanup; } (*send_pa)->magic = KV5M_PA_DATA; switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; break; case KRB5_PADATA_PK_AS_REQ_OLD: case KRB5_PADATA_PK_AS_REP_OLD: (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD; break; } (*send_pa)->length = out_data->length; (*send_pa)->contents = (krb5_octet *) out_data->data; cleanup: pkinit_fini_kdc_req_context(context, reqctx); if (scratch.data != NULL) free(scratch.data); if (out_data != NULL) free(out_data); if (encoded_dhkey_info != NULL) krb5_free_data(context, encoded_dhkey_info); if (encoded_key_pack != NULL) krb5_free_data(context, encoded_key_pack); if (dh_pubkey != NULL) free(dh_pubkey); if (server_key != NULL) free(server_key); if (cksum_types != NULL) free(cksum_types); switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); free_krb5_pa_pk_as_rep(&rep); free_krb5_reply_key_pack(&key_pack); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); free_krb5_pa_pk_as_rep_draft9(&rep9); if (!fixed_keypack) free_krb5_reply_key_pack_draft9(&key_pack9); else free_krb5_reply_key_pack(&key_pack); break; } if (retval) pkiDebug("pkinit_verify_padata failure"); return retval; }
krb5_error_code kdc_fast_response_handle_padata(struct kdc_request_state *state, krb5_kdc_req *request, krb5_kdc_rep *rep, krb5_enctype enctype) { krb5_error_code retval = 0; krb5_fast_finished finish; krb5_fast_response fast_response; krb5_data *encoded_ticket = NULL; krb5_data *encrypted_reply = NULL; krb5_pa_data *pa = NULL, **pa_array = NULL; krb5_cksumtype cksumtype = CKSUMTYPE_RSA_MD5; krb5_pa_data *empty_padata[] = {NULL}; krb5_keyblock *strengthen_key = NULL; kdc_realm_t *kdc_active_realm = state->realm_data; if (!state->armor_key) return 0; memset(&finish, 0, sizeof(finish)); retval = krb5_init_keyblock(kdc_context, enctype, 0, &strengthen_key); if (retval == 0) retval = krb5_c_make_random_key(kdc_context, enctype, strengthen_key); if (retval == 0) { state->strengthen_key = strengthen_key; strengthen_key = NULL; } fast_response.padata = rep->padata; if (fast_response.padata == NULL) fast_response.padata = &empty_padata[0]; fast_response.strengthen_key = state->strengthen_key; fast_response.nonce = request->nonce; fast_response.finished = &finish; finish.client = rep->client; pa_array = calloc(3, sizeof(*pa_array)); if (pa_array == NULL) retval = ENOMEM; pa = calloc(1, sizeof(krb5_pa_data)); if (retval == 0 && pa == NULL) retval = ENOMEM; if (retval == 0) retval = krb5_us_timeofday(kdc_context, &finish.timestamp, &finish.usec); if (retval == 0) retval = encode_krb5_ticket(rep->ticket, &encoded_ticket); if (retval == 0) retval = krb5int_c_mandatory_cksumtype(kdc_context, state->armor_key->enctype, &cksumtype); if (retval == 0) retval = krb5_c_make_checksum(kdc_context, cksumtype, state->armor_key, KRB5_KEYUSAGE_FAST_FINISHED, encoded_ticket, &finish.ticket_checksum); if (retval == 0) retval = encrypt_fast_reply(state, &fast_response, &encrypted_reply); if (retval == 0) { pa[0].pa_type = KRB5_PADATA_FX_FAST; pa[0].length = encrypted_reply->length; pa[0].contents = (unsigned char *) encrypted_reply->data; pa_array[0] = &pa[0]; krb5_free_pa_data(kdc_context, rep->padata); rep->padata = pa_array; pa_array = NULL; free(encrypted_reply); encrypted_reply = NULL; pa = NULL; } if (pa) free(pa); if (pa_array) free(pa_array); if (encrypted_reply) krb5_free_data(kdc_context, encrypted_reply); if (encoded_ticket) krb5_free_data(kdc_context, encoded_ticket); if (strengthen_key != NULL) krb5_free_keyblock(kdc_context, strengthen_key); if (finish.ticket_checksum.contents) krb5_free_checksum_contents(kdc_context, &finish.ticket_checksum); return retval; }