/* Derive encryption and integrity keys for CMAC-using enctypes. */ static krb5_error_code derive_keys(const struct krb5_enc_provider *enc, krb5_key key, krb5_keyusage usage, krb5_key *ke_out, krb5_key *ki_out) { krb5_error_code ret; unsigned char buf[K5CLENGTH]; krb5_data constant = make_data(buf, K5CLENGTH); krb5_key ke, ki; *ke_out = *ki_out = NULL; /* Derive the encryption key. */ store_32_be(usage, buf); buf[4] = 0xAA; ret = krb5int_derive_key(enc, key, &ke, &constant, DERIVE_SP800_108_CMAC); if (ret != 0) return ret; /* Derive the integrity key. */ buf[4] = 0x55; ret = krb5int_derive_key(enc, key, &ki, &constant, DERIVE_SP800_108_CMAC); if (ret != 0) { krb5_k_free_key(NULL, ke); return ret; } *ke_out = ke; *ki_out = ki; return 0; }
static krb5_error_code nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce_out) { krb5_data nonce; krb5_error_code retval; krb5_timestamp now; retval = krb5_timeofday(ctx, &now); if (retval != 0) return retval; retval = alloc_data(&nonce, sizeof(now) + length); if (retval != 0) return retval; retval = krb5_c_random_make_octets(ctx, &nonce); if (retval != 0) { free(nonce.data); return retval; } store_32_be(now, nonce.data); *nonce_out = nonce; return 0; }
krb5_error_code KRB5_CALLCONV krb5_c_random_make_octets(krb5_context context, krb5_data *outdata) { #ifdef _WIN32 DWORD pid = GetCurrentProcessId(); #else pid_t pid = getpid(); #endif unsigned char pidbuf[4]; k5_mutex_lock(&fortuna_lock); if (!have_entropy) { k5_mutex_unlock(&fortuna_lock); return KRB5_CRYPTO_INTERNAL; } if (pid != last_pid) { /* We forked; make sure child's PRNG stream differs from parent's. */ store_32_be(pid, pidbuf); generator_reseed(&main_state, pidbuf, 4); last_pid = pid; } accumulator_output(&main_state, (unsigned char *)outdata->data, outdata->length); k5_mutex_unlock(&fortuna_lock); return 0; }
/* Set up the actual message we will send across the underlying transport to * communicate the payload message, using one or both of state->out.sgbuf. */ static krb5_error_code set_transport_message(struct conn_state *state, const krb5_data *realm, const krb5_data *message) { struct outgoing_message *out = &state->out; char *req = NULL; size_t reqlen; krb5_error_code ret; if (message == NULL || message->length == 0) return 0; if (state->addr.transport == TCP) { store_32_be(message->length, out->msg_len_buf); SG_SET(&out->sgbuf[0], out->msg_len_buf, 4); SG_SET(&out->sgbuf[1], message->data, message->length); out->sg_count = 2; return 0; } else if (state->addr.transport == HTTPS) { ret = make_proxy_request(state, realm, message, &req, &reqlen); if (ret != 0) return ret; SG_SET(&state->out.sgbuf[0], req, reqlen); SG_SET(&state->out.sgbuf[1], 0, 0); state->out.sg_count = 1; free(state->http.https_request); state->http.https_request = req; return 0; } else { SG_SET(&out->sgbuf[0], message->data, message->length); SG_SET(&out->sgbuf[1], NULL, 0); out->sg_count = 1; return 0; } }
/* * krb5_ser_pack_int32() - Pack a 4-byte integer if space is available. * Update buffer pointer and remaining space. */ krb5_error_code KRB5_CALLCONV krb5_ser_pack_int32(krb5_int32 iarg, krb5_octet **bufp, size_t *remainp) { if (*remainp >= sizeof(krb5_int32)) { store_32_be(iarg, *bufp); *bufp += sizeof(krb5_int32); *remainp -= sizeof(krb5_int32); return(0); } else return(ENOMEM); }
static krb5_error_code make_proxy_request(struct conn_state *state, const krb5_data *realm, const krb5_data *message, char **req_out, size_t *len_out) { krb5_kkdcp_message pm; krb5_data *encoded_pm = NULL; struct k5buf buf; const char *uri_path; krb5_error_code ret; *req_out = NULL; *len_out = 0; /* * Stuff the message length in at the front of the kerb_message field * before encoding. The proxied messages are actually the payload we'd * be sending and receiving if we were using plain TCP. */ memset(&pm, 0, sizeof(pm)); ret = alloc_data(&pm.kerb_message, message->length + 4); if (ret != 0) goto cleanup; store_32_be(message->length, pm.kerb_message.data); memcpy(pm.kerb_message.data + 4, message->data, message->length); pm.target_domain = *realm; ret = encode_krb5_kkdcp_message(&pm, &encoded_pm); if (ret != 0) goto cleanup; /* Build the request to transmit: the headers + the proxy message. */ k5_buf_init_dynamic(&buf); uri_path = (state->http.uri_path != NULL) ? state->http.uri_path : ""; k5_buf_add_fmt(&buf, "POST /%s HTTP/1.0\r\n", uri_path); k5_buf_add(&buf, "Cache-Control: no-cache\r\n"); k5_buf_add(&buf, "Pragma: no-cache\r\n"); k5_buf_add(&buf, "User-Agent: kerberos/1.0\r\n"); k5_buf_add(&buf, "Content-type: application/kerberos\r\n"); k5_buf_add_fmt(&buf, "Content-Length: %d\r\n\r\n", encoded_pm->length); k5_buf_add_len(&buf, encoded_pm->data, encoded_pm->length); if (k5_buf_status(&buf) != 0) { ret = ENOMEM; goto cleanup; } *req_out = buf.data; *len_out = buf.len; cleanup: krb5_free_data_contents(NULL, &pm.kerb_message); krb5_free_data(NULL, encoded_pm); return ret; }
/* Store a 32-bit integer into the cache file according to the file format. */ static krb5_error_code store32(krb5_context context, krb5_ccache id, uint32_t i) { unsigned char buf[4]; k5_cc_mutex_assert_locked(context, &((fcc_data *)id->data)->lock); if (version(id) < 3) store_32_n(i, buf); else store_32_be(i, buf); return write_bytes(context, id, buf, 4); }
int main () { /* Test some low-level assumptions the Kerberos code depends on. */ union { uint64_t n64; uint32_t n32; uint16_t n16; unsigned char b[9]; } u; static unsigned char buf[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; assert(load_64_be(buf+1) == 0x0102030405060708LL); assert(load_64_le(buf+1) == 0x0807060504030201LL); assert(load_32_le(buf+2) == 0x05040302); assert(load_32_be(buf+2) == 0x02030405); assert(load_16_be(buf+3) == 0x0304); assert(load_16_le(buf+3) == 0x0403); u.b[0] = 0; assert((store_64_be(0x0102030405060708LL, u.b+1), !memcmp(buf, u.b, 9))); u.b[1] = 9; assert((store_64_le(0x0807060504030201LL, u.b+1), !memcmp(buf, u.b, 9))); u.b[2] = 10; assert((store_32_be(0x02030405, u.b+2), !memcmp(buf, u.b, 9))); u.b[3] = 11; assert((store_32_le(0x05040302, u.b+2), !memcmp(buf, u.b, 9))); u.b[4] = 12; assert((store_16_be(0x0304, u.b+3), !memcmp(buf, u.b, 9))); u.b[4] = 13; assert((store_16_le(0x0403, u.b+3), !memcmp(buf, u.b, 9))); /* Verify that load_*_n properly does native format. Assume the unaligned thing is okay. */ u.n64 = 0x090a0b0c0d0e0f00LL; assert(load_64_n((unsigned char *) &u.n64) == 0x090a0b0c0d0e0f00LL); u.n32 = 0x06070809; assert(load_32_n((unsigned char *) &u.n32) == 0x06070809); u.n16 = 0x0a0b; assert(load_16_n((unsigned char *) &u.n16) == 0x0a0b); return 0; }
static krb5_error_code k5_sha1_hash(unsigned int icount, const krb5_data *input, krb5_data *output) { SHS_INFO ctx; unsigned int i; if (output->length != SHS_DIGESTSIZE) return(KRB5_CRYPTO_INTERNAL); shsInit(&ctx); for (i=0; i<icount; i++) shsUpdate(&ctx, (unsigned char *) input[i].data, input[i].length); shsFinal(&ctx); for (i=0; i<(sizeof(ctx.digest)/sizeof(ctx.digest[0])); i++) { store_32_be(ctx.digest[i], &output->data[i*4]); } return(0); }
static void set_conn_state_msg_length (struct conn_state *state, const krb5_data *message) { if (!message || message->length == 0) return; if (!state->is_udp) { store_32_be(message->length, state->x.out.msg_len_buf); SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4); SG_SET(&state->x.out.sgbuf[1], message->data, message->length); state->x.out.sg_count = 2; } else { SG_SET(&state->x.out.sgbuf[0], message->data, message->length); SG_SET(&state->x.out.sgbuf[1], 0, 0); state->x.out.sg_count = 1; } }
OM_uint32 KRB5_CALLCONV krb5_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context, int prf_key, const gss_buffer_t prf_in, ssize_t desired_output_len, gss_buffer_t prf_out) { krb5_error_code code; krb5_key key = NULL; krb5_gss_ctx_id_t ctx; int i; OM_uint32 minor; size_t prflen; krb5_data t, ns; unsigned char *p; prf_out->length = 0; prf_out->value = NULL; t.length = 0; t.data = NULL; ns.length = 0; ns.data = NULL; ctx = (krb5_gss_ctx_id_t)context; switch (prf_key) { case GSS_C_PRF_KEY_FULL: if (ctx->have_acceptor_subkey) { key = ctx->acceptor_subkey; break; } /* fallthrough */ case GSS_C_PRF_KEY_PARTIAL: key = ctx->subkey; break; default: code = EINVAL; goto cleanup; } if (key == NULL) { code = EINVAL; goto cleanup; } prf_out->value = k5alloc(desired_output_len, &code); if (prf_out->value == NULL) { code = KG_INPUT_TOO_LONG; goto cleanup; } prf_out->length = desired_output_len; code = krb5_c_prf_length(ctx->k5_context, krb5_k_key_enctype(ctx->k5_context, key), &prflen); if (code != 0) goto cleanup; ns.length = 4 + prf_in->length; ns.data = k5alloc(ns.length, &code); if (ns.data == NULL) { code = KG_INPUT_TOO_LONG; goto cleanup; } t.length = prflen; t.data = k5alloc(t.length, &code); if (t.data == NULL) goto cleanup; memcpy(ns.data + 4, prf_in->value, prf_in->length); i = 0; p = (unsigned char *)prf_out->value; while (desired_output_len > 0) { store_32_be(i, ns.data); code = krb5_k_prf(ctx->k5_context, key, &ns, &t); if (code != 0) goto cleanup; memcpy(p, t.data, MIN(t.length, desired_output_len)); p += t.length; desired_output_len -= t.length; i++; } cleanup: if (code != 0) gss_release_buffer(&minor, prf_out); krb5_free_data_contents(ctx->k5_context, &ns); krb5_free_data_contents(ctx->k5_context, &t); *minor_status = (OM_uint32)code; return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; }
static void pack_int32(prof_int32 oval, unsigned char **bufpp, size_t *remainp) { store_32_be(oval, *bufpp); *bufpp += sizeof(prof_int32); *remainp -= sizeof(prof_int32); }
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); }
/* * NIST SP800-108 KDF in feedback mode (section 5.2). * Parameters: * - CMAC (with enc as the enc provider) is the PRF. * - A block counter of four bytes is used. * - Label is the key derivation constant. * - Context is empty. * - Four bytes are used to encode the output length in the PRF input. */ static krb5_error_code derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_data *outrnd, const krb5_data *in_constant) { size_t blocksize, keybytes, n; krb5_crypto_iov iov[6]; krb5_error_code ret; krb5_data prf; unsigned int i; unsigned char ibuf[4], Lbuf[4]; blocksize = enc->block_size; keybytes = enc->keybytes; if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes) return KRB5_CRYPTO_INTERNAL; /* Allocate encryption data buffer. */ ret = alloc_data(&prf, blocksize); if (ret) return ret; /* K(i-1): the previous block of PRF output, initially all-zeros. */ iov[0].flags = KRB5_CRYPTO_TYPE_DATA; iov[0].data = prf; /* [i]2: four-byte big-endian binary string giving the block counter */ iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = make_data(ibuf, sizeof(ibuf)); /* Label: the fixed derived-key input */ iov[2].flags = KRB5_CRYPTO_TYPE_DATA; iov[2].data = *in_constant; /* 0x00: separator byte */ iov[3].flags = KRB5_CRYPTO_TYPE_DATA; iov[3].data = make_data("", 1); /* Context: (unused) */ iov[4].flags = KRB5_CRYPTO_TYPE_DATA; iov[4].data = empty_data(); /* [L]2: four-byte big-endian binary string giving the output length */ iov[5].flags = KRB5_CRYPTO_TYPE_DATA; iov[5].data = make_data(Lbuf, sizeof(Lbuf)); store_32_be(outrnd->length * 8, Lbuf); for (i = 1, n = 0; n < keybytes; i++) { /* Update the block counter. */ store_32_be(i, ibuf); /* Compute a CMAC checksum, storing the result into K(i-1). */ ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf); if (ret) goto cleanup; /* Copy the result into the appropriate part of the output buffer. */ if (keybytes - n <= blocksize) { memcpy(outrnd->data + n, prf.data, keybytes - n); break; } memcpy(outrnd->data + n, prf.data, blocksize); n += blocksize; } cleanup: zapfree(prf.data, blocksize); return ret; }
static krb5_error_code F(char *output, char *u_tmp1, char *u_tmp2, const struct krb5_hash_provider *hash, size_t hlen, krb5_keyblock *pass, const krb5_data *salt, unsigned long count, int i) { unsigned char ibytes[4]; unsigned int j, k; krb5_data sdata; krb5_data out; krb5_error_code err; #if 0 printf("F(i=%d, count=%lu, pass=%d:%s)\n", i, count, pass->length, pass->data); #endif /* Compute U_1. */ store_32_be(i, ibytes); memcpy(u_tmp2, salt->data, salt->length); memcpy(u_tmp2 + salt->length, ibytes, 4); sdata = make_data(u_tmp2, salt->length + 4); #if 0 printd("initial salt", &sdata); #endif out = make_data(u_tmp1, hlen); #if 0 printf("F: computing hmac #1 (U_1) with %s\n", pdata.contents); #endif err = hmac(hash, pass, &sdata, &out); if (err) return err; #if 0 printd("F: prf return value", &out); #endif memcpy(output, u_tmp1, hlen); /* Compute U_2, .. U_c. */ sdata.length = hlen; for (j = 2; j <= count; j++) { #if 0 printf("F: computing hmac #%d (U_%d)\n", j, j); #endif memcpy(u_tmp2, u_tmp1, hlen); err = hmac(hash, pass, &sdata, &out); if (err) return err; #if 0 printd("F: prf return value", &out); #endif /* And xor them together. */ for (k = 0; k < hlen; k++) output[k] ^= u_tmp1[k]; #if 0 printf("F: xor result:\n"); for (k = 0; k < hlen; k++) printf(" %02x", 0xff & output[k]); printf("\n"); #endif } return 0; }
OM_uint32 krb5_gss_export_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_buffer_t exported_name) { krb5_context context; krb5_error_code code; size_t length; char *str; unsigned char *cp; if (minor_status) *minor_status = 0; code = krb5_gss_init_context(&context); if (code) { if (minor_status) *minor_status = code; return GSS_S_FAILURE; } exported_name->length = 0; exported_name->value = NULL; if (! kg_validate_name(input_name)) { if (minor_status) *minor_status = (OM_uint32) G_VALIDATE_FAILED; krb5_free_context(context); return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); } if ((code = krb5_unparse_name(context, (krb5_principal) input_name, &str))) { if (minor_status) *minor_status = code; save_error_info((OM_uint32)code, context); krb5_free_context(context); return(GSS_S_FAILURE); } krb5_free_context(context); length = strlen(str); exported_name->length = 10 + length + gss_mech_krb5->length; exported_name->value = malloc(exported_name->length); if (!exported_name->value) { free(str); if (minor_status) *minor_status = ENOMEM; return(GSS_S_FAILURE); } cp = exported_name->value; /* Note: we assume the OID will be less than 128 bytes... */ *cp++ = 0x04; *cp++ = 0x01; store_16_be(gss_mech_krb5->length+2, cp); cp += 2; *cp++ = 0x06; *cp++ = (gss_mech_krb5->length) & 0xFF; memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length); cp += gss_mech_krb5->length; store_32_be(length, cp); cp += 4; memcpy(cp, str, length); free(str); return(GSS_S_COMPLETE); }
OM_uint32 KRB5_CALLCONV krb5_gss_export_name_composite(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t exp_composite_name) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; krb5_data *attrs = NULL; char *princstr = NULL; unsigned char *cp; size_t princlen; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } code = krb5_unparse_name(context, kname->princ, &princstr); if (code != 0) goto cleanup; princlen = strlen(princstr); if (kname->ad_context != NULL) { code = krb5_authdata_export_attributes(context, kname->ad_context, AD_USAGE_MASK, &attrs); if (code != 0) goto cleanup; } /* 04 02 OID Name AuthData */ exp_composite_name->length = 10 + gss_mech_krb5->length + princlen; if (attrs != NULL) exp_composite_name->length += 4 + attrs->length; exp_composite_name->value = malloc(exp_composite_name->length); if (exp_composite_name->value == NULL) { code = ENOMEM; goto cleanup; } cp = exp_composite_name->value; /* Note: we assume the OID will be less than 128 bytes... */ *cp++ = 0x04; if (attrs != NULL) *cp++ = 0x02; else *cp++ = 0x01; store_16_be(gss_mech_krb5->length + 2, cp); cp += 2; *cp++ = 0x06; *cp++ = (gss_mech_krb5->length) & 0xFF; memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length); cp += gss_mech_krb5->length; store_32_be(princlen, cp); cp += 4; memcpy(cp, princstr, princlen); cp += princlen; if (attrs != NULL) { store_32_be(attrs->length, cp); cp += 4; memcpy(cp, attrs->data, attrs->length); cp += attrs->length; } cleanup: krb5_free_unparsed_name(context, princstr); krb5_free_data(context, attrs); k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
krb5_error_code krb5_dk_make_checksum(const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *input, krb5_data *output) { int i; const struct krb5_enc_provider *enc; size_t blocksize, keybytes, keylength; krb5_error_code ret; unsigned char constantdata[K5CLENGTH]; krb5_data datain; unsigned char *kcdata; krb5_keyblock kc; for (i=0; i<krb5_enctypes_length; i++) { if (krb5_enctypes_list[i].etype == key->enctype) break; } if (i == krb5_enctypes_length) return(KRB5_BAD_ENCTYPE); enc = krb5_enctypes_list[i].enc; /* allocate and set to-be-derived keys */ blocksize = enc->block_size; keybytes = enc->keybytes; keylength = enc->keylength; /* key->length will be tested in enc->encrypt output->length will be tested in krb5_hmac */ if ((kcdata = (unsigned char *) malloc(keylength)) == NULL) return(ENOMEM); kc.contents = kcdata; kc.length = keylength; /* derive the key */ datain.data = (char *) constantdata; datain.length = K5CLENGTH; store_32_be(usage, constantdata); datain.data[4] = (char) 0x99; if ((ret = krb5_derive_key(enc, key, &kc, &datain)) != 0) goto cleanup; /* hash the data */ datain = *input; if ((ret = krb5_hmac(hash, &kc, 1, &datain, output)) != 0) memset(output->data, 0, output->length); /* ret is set correctly by the prior call */ cleanup: memset(kcdata, 0, keylength); free(kcdata); return(ret); }
static krb5_error_code make_seal_token_v1_iov(krb5_context context, krb5_gss_ctx_id_rec *ctx, int conf_req_flag, int *conf_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { krb5_error_code code = 0; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; krb5_checksum md5cksum; krb5_checksum cksum; size_t k5_headerlen = 0, k5_trailerlen = 0; size_t data_length = 0, assoc_data_length = 0; size_t tmsglen = 0, tlen; unsigned char *ptr; krb5_keyusage sign_usage = KG_USAGE_SIGN; assert(toktype == KG_TOK_WRAP_MSG); md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); if (header == NULL) return EINVAL; padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding == NULL && (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) return EINVAL; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL) trailer->buffer.length = 0; /* Determine confounder length */ if (toktype == KG_TOK_WRAP_MSG || conf_req_flag) k5_headerlen = kg_confounder_size(context, ctx->enc); /* Check padding length */ if (toktype == KG_TOK_WRAP_MSG) { size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; size_t gss_padlen; size_t conf_data_length; kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); conf_data_length = k5_headerlen + data_length - assoc_data_length; if (k5_padlen == 1) gss_padlen = 1; /* one byte to indicate one byte of padding */ else gss_padlen = k5_padlen - (conf_data_length % k5_padlen); if (ctx->gss_flags & GSS_C_DCE_STYLE) { /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */ gss_padlen = 0; if (conf_data_length % k5_padlen) code = KRB5_BAD_MSIZE; } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(padding, gss_padlen); } else if (padding->buffer.length < gss_padlen) { code = KRB5_BAD_MSIZE; } if (code != 0) goto cleanup; /* Initialize padding buffer to pad itself */ if (padding != NULL) { padding->buffer.length = gss_padlen; memset(padding->buffer.value, (int)gss_padlen, gss_padlen); } if (ctx->gss_flags & GSS_C_DCE_STYLE) tmsglen = k5_headerlen; /* confounder length */ else tmsglen = conf_data_length + padding->buffer.length + assoc_data_length; } /* Determine token size */ tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen); k5_headerlen += tlen - tmsglen; if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(header, k5_headerlen); else if (header->buffer.length < k5_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; header->buffer.length = k5_headerlen; ptr = (unsigned char *)header->buffer.value; g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype); /* 0..1 SIGN_ALG */ store_16_le(ctx->signalg, &ptr[0]); /* 2..3 SEAL_ALG or Filler */ if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { store_16_le(ctx->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 checksum */ switch (ctx->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_WRAP_MSG) sign_usage = 15; break; default: case SGN_ALG_DES_MAC: abort (); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen); if (code != 0) goto cleanup; md5cksum.length = k5_trailerlen; if (k5_headerlen != 0) { code = kg_make_confounder(context, ctx->enc, ptr + 14 + ctx->cksum_size); if (code != 0) goto cleanup; } /* compute the checksum */ code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, ctx->cksum_size, ctx->seq, ctx->enc, sign_usage, iov, iov_count, toktype, &md5cksum); if (code != 0) goto cleanup; switch (ctx->signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: code = kg_encrypt(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16); if (code != 0) goto cleanup; cksum.length = ctx->cksum_size; cksum.contents = md5cksum.contents + 16 - cksum.length; memcpy(ptr + 14, cksum.contents, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: assert(md5cksum.length == ctx->cksum_size); memcpy(ptr + 14, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size); break; } /* create the seq_num */ code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF, (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6); if (code != 0) goto cleanup; if (conf_req_flag) { if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; size_t i; store_32_be(ctx->seq_send, bigend_seqnum); code = krb5_copy_keyblock(context, ctx->enc, &enc_key); if (code != 0) goto cleanup; assert(enc_key->length == 16); for (i = 0; i < enc_key->length; i++) ((char *)enc_key->contents)[i] ^= 0xF0; code = kg_arcfour_docrypt_iov(context, enc_key, 0, bigend_seqnum, 4, iov, iov_count); krb5_free_keyblock(context, enc_key); } else { code = kg_encrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), 0 /*EC*/, 0 /*RRC*/, ctx->enc, KG_USAGE_SEAL, NULL, iov, iov_count); } if (code != 0) goto cleanup; } ctx->seq_send++; ctx->seq_send &= 0xFFFFFFFFL; code = 0; if (conf_state != NULL) *conf_state = conf_req_flag; cleanup: if (code != 0) kg_release_iov(iov, iov_count); krb5_free_checksum_contents(context, &md5cksum); return code; }
/* Construct a cookie pa-data item using the cookie values from state, or a * trivial "MIT" cookie if no values are set. */ krb5_error_code kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state, krb5_db_entry *local_tgt, krb5_const_principal client_princ, krb5_pa_data **cookie_out) { krb5_error_code ret; krb5_secure_cookie cookie; krb5_pa_data **contents = state->out_cookie_padata, *pa; krb5_keyblock *key = NULL; krb5_timestamp now; krb5_enc_data enc; krb5_data *der_cookie = NULL; krb5_kvno kvno; size_t ctlen; *cookie_out = NULL; memset(&enc, 0, sizeof(enc)); /* Make a trivial cookie if there are no contents to marshal or we don't * have a TGT entry to encrypt them. */ if (contents == NULL || *contents == NULL || local_tgt == NULL) return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out); ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno); if (ret) goto cleanup; /* Encode the cookie. */ ret = krb5_timeofday(context, &now); if (ret) goto cleanup; cookie.time = now; cookie.data = contents; ret = encode_krb5_secure_cookie(&cookie, &der_cookie); if (ret) goto cleanup; /* Encrypt the cookie in key. */ ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length, &ctlen); if (ret) goto cleanup; ret = alloc_data(&enc.ciphertext, ctlen); if (ret) goto cleanup; ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, der_cookie, &enc); if (ret) goto cleanup; /* Construct the cookie pa-data entry. */ ret = alloc_padata(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa); memcpy(pa->contents, "MIT1", 4); store_32_be(kvno, pa->contents + 4); memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length); *cookie_out = pa; cleanup: krb5_free_keyblock(context, key); if (der_cookie != NULL) { zapfree(der_cookie->data, der_cookie->length); free(der_cookie); } krb5_free_data_contents(context, &enc.ciphertext); return ret; }
static OM_uint32 kg_unseal_v1_iov(krb5_context context, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, gss_iov_buffer_desc *iov, int iov_count, size_t token_wrapper_len, int *conf_state, gss_qop_t *qop_state, int toktype) { OM_uint32 code; gss_iov_buffer_t header; gss_iov_buffer_t trailer; unsigned char *ptr; int sealalg; int signalg; krb5_checksum cksum; krb5_checksum md5cksum; size_t cksum_len = 0; size_t conflen = 0; int direction; krb5_ui_4 seqnum; OM_uint32 retval; size_t sumlen; krb5_keyusage sign_usage = KG_USAGE_SIGN; md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; header = kg_locate_header_iov(iov, iov_count, toktype); assert(header != NULL); trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL && trailer->buffer.length != 0) { *minor_status = (OM_uint32)KRB5_BAD_MSIZE; return GSS_S_DEFECTIVE_TOKEN; } if (header->buffer.length < token_wrapper_len + 14) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } ptr = (unsigned char *)header->buffer.value + token_wrapper_len; signalg = ptr[0]; signalg |= ptr[1] << 8; sealalg = ptr[2]; sealalg |= ptr[3] << 8; if (ptr[4] != 0xFF || ptr[5] != 0xFF) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if (toktype != KG_TOK_WRAP_MSG && sealalg != 0xFFFF) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if (toktype == KG_TOK_WRAP_MSG && !(sealalg == 0xFFFF || sealalg == ctx->sealalg)) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || (ctx->sealalg == SEAL_ALG_DES3KD && signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && signalg != SGN_ALG_HMAC_MD5)) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_HMAC_MD5: cksum_len = 8; if (toktype != KG_TOK_WRAP_MSG) sign_usage = 15; break; case SGN_ALG_3: cksum_len = 16; break; case SGN_ALG_HMAC_SHA1_DES3_KD: cksum_len = 20; break; default: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } /* get the token parameters */ code = kg_get_seq_num(context, ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum); if (code != 0) { *minor_status = code; return GSS_S_BAD_SIG; } /* decode the message, if SEAL */ if (toktype == KG_TOK_WRAP_MSG) { if (sealalg != 0xFFFF) { if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; size_t i; store_32_be(seqnum, bigend_seqnum); code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } assert(enc_key->length == 16); for (i = 0; i < enc_key->length; i++) ((char *)enc_key->contents)[i] ^= 0xF0; code = kg_arcfour_docrypt_iov(context, enc_key, 0, &bigend_seqnum[0], 4, iov, iov_count); krb5_free_keyblock(context, enc_key); } else { code = kg_decrypt_iov(context, 0, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), 0 /*EC*/, 0 /*RRC*/, ctx->enc, KG_USAGE_SEAL, NULL, iov, iov_count); } if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } } conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); } if (header->buffer.length != token_wrapper_len + 14 + cksum_len + conflen) { retval = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } /* compute the checksum of the message */ /* initialize the checksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_DES_MAC: case SGN_ALG_3: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; default: abort(); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } md5cksum.length = sumlen; /* compute the checksum of the message */ code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, cksum_len, ctx->seq, ctx->enc, sign_usage, iov, iov_count, toktype, &md5cksum); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->keyblock.contents : NULL), md5cksum.contents, 16); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } cksum.length = cksum_len; cksum.contents = md5cksum.contents + 16 - cksum.length; code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: case SGN_ALG_HMAC_MD5: code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); break; default: code = 0; retval = GSS_S_DEFECTIVE_TOKEN; goto cleanup; break; } if (code != 0) { code = 0; retval = GSS_S_BAD_SIG; goto cleanup; } /* * For GSS_C_DCE_STYLE, the caller manages the padding, because the * pad length is in the RPC PDU. The value of the padding may be * uninitialized. For normal GSS, the last bytes of the decrypted * data contain the pad length. kg_fixup_padding_iov() will find * this and fixup the last data IOV appropriately. */ if (toktype == KG_TOK_WRAP_MSG && (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { retval = kg_fixup_padding_iov(&code, iov, iov_count); if (retval != GSS_S_COMPLETE) goto cleanup; } if (conf_state != NULL) *conf_state = (sealalg != 0xFFFF); if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; if ((ctx->initiate && direction != 0xff) || (!ctx->initiate && direction != 0)) { *minor_status = (OM_uint32)G_BAD_DIRECTION; retval = GSS_S_BAD_SIG; } code = 0; retval = g_order_check(&ctx->seqstate, (gssint_uint64)seqnum); cleanup: krb5_free_checksum_contents(context, &md5cksum); *minor_status = code; return retval; }