/* * Create a IAKERB-FINISHED structure containing a checksum of * the entire IAKERB exchange. */ krb5_error_code iakerb_make_finished(krb5_context context, krb5_key key, const krb5_data *conv, krb5_data **finished) { krb5_error_code code; krb5_iakerb_finished iaf; *finished = NULL; memset(&iaf, 0, sizeof(iaf)); if (key == NULL) return KRB5KDC_ERR_NULL_KEY; code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED, conv, &iaf.checksum); if (code != 0) return code; code = encode_krb5_iakerb_finished(&iaf, finished); krb5_free_checksum_contents(context, &iaf.checksum); return code; }
/* Return a four-byte hex string from the first two bytes of a SHA-1 hash of a * byte array. Return NULL on failure. */ static char * hash_bytes(krb5_context context, const void *ptr, size_t len) { krb5_checksum cksum; krb5_data d = make_data((void *) ptr, len); char *s = NULL; if (krb5_k_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, &d, &cksum) != 0) return NULL; if (cksum.length >= 2) (void) asprintf(&s, "%02X%02X", cksum.contents[0], cksum.contents[1]); krb5_free_checksum_contents(context, &cksum); return s; }
krb5_error_code KRB5_CALLCONV krb5_c_make_checksum(krb5_context context, krb5_cksumtype cksumtype, const krb5_keyblock *keyblock, krb5_keyusage usage, const krb5_data *input, krb5_checksum *cksum) { krb5_key key = NULL; krb5_error_code ret; if (keyblock != NULL) { ret = krb5_k_create_key(context, keyblock, &key); if (ret != 0) return ret; } ret = krb5_k_make_checksum(context, cksumtype, key, usage, input, cksum); krb5_k_free_key(context, key); return ret; }
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_key key; krb5_cksumtype cksumtype; 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->keyblock.enctype); /* Allocate space for header plus encrypted data. */ outbuf = gssalloc_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); if (ec != 0) 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->keyblock.enctype; err = krb5_k_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 = gssalloc_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_k_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: gssalloc_free(outbuf); token->value = NULL; token->length = 0; return err; }
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 = krb5int_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)->key != NULL) { krb5_k_free_key(context, (*auth_context)->key); (*auth_context)->key = NULL; } /* set auth context keyblock */ if ((retval = krb5_k_create_key(context, &in_creds->keyblock, &((*auth_context)->key)))) 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 ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) { retval = k5_generate_and_save_subkey(context, *auth_context, &in_creds->keyblock, in_creds->keyblock.enctype); if (retval) goto cleanup; } 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 (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_enctype enctype = krb5_k_key_enctype(context, (*auth_context)->key); krb5_cksumtype cksumtype = ap_req_cksum(context, *auth_context, enctype); if ((retval = krb5_k_make_checksum(context, cksumtype, (*auth_context)->key, 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; } TRACE_MK_REQ(context, in_creds, (*auth_context)->local_seq_number, (*auth_context)->send_subkey, &in_creds->keyblock); if ((retval = 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; }
/* Formats a KRB_SAFE message into outbuf. userdata is formatted as the user data in the message. sumtype specifies the encryption type; key specifies the key which might be used to seed the checksum; sender_addr and recv_addr specify the full addresses (host and port) of the sender and receiver. The host portion of sender_addr is used to form the addresses used in the KRB_SAFE message. The outbuf buffer storage is allocated, and should be freed by the caller when finished. returns system errors */ static krb5_error_code krb5_mk_safe_basic(krb5_context context, const krb5_data *userdata, krb5_key key, krb5_replay_data *replaydata, krb5_address *local_addr, krb5_address *remote_addr, krb5_cksumtype sumtype, krb5_data *outbuf) { krb5_error_code retval; krb5_safe safemsg; krb5_octet zero_octet = 0; krb5_checksum safe_checksum; krb5_data *scratch1, *scratch2; if (!krb5_c_valid_cksumtype(sumtype)) return KRB5_PROG_SUMTYPE_NOSUPP; if (!krb5_c_is_coll_proof_cksum(sumtype) || !krb5_c_is_keyed_cksum(sumtype)) return KRB5KRB_AP_ERR_INAPP_CKSUM; safemsg.user_data = *userdata; safemsg.s_address = (krb5_address *) local_addr; safemsg.r_address = (krb5_address *) remote_addr; /* We should check too make sure one exists. */ safemsg.timestamp = replaydata->timestamp; safemsg.usec = replaydata->usec; safemsg.seq_number = replaydata->seq; /* * To do the checksum stuff, we need to encode the message with a * zero-length zero-type checksum, then checksum the encoding, then * re-encode with the checksum. */ safe_checksum.length = 0; safe_checksum.checksum_type = 0; safe_checksum.contents = &zero_octet; safemsg.checksum = &safe_checksum; if ((retval = encode_krb5_safe(&safemsg, &scratch1))) return retval; if ((retval = krb5_k_make_checksum(context, sumtype, key, KRB5_KEYUSAGE_KRB_SAFE_CKSUM, scratch1, &safe_checksum))) goto cleanup_checksum; safemsg.checksum = &safe_checksum; if ((retval = encode_krb5_safe(&safemsg, &scratch2))) { goto cleanup_checksum; } *outbuf = *scratch2; free(scratch2); retval = 0; cleanup_checksum: free(safe_checksum.contents); memset(scratch1->data, 0, scratch1->length); krb5_free_data(context, scratch1); return retval; }