/* * krb5_ser_unpack_int64() - Unpack an 8-byte integer if it's there. */ krb5_error_code KRB5_CALLCONV krb5_ser_unpack_int64(int64_t *intp, krb5_octet **bufp, size_t *remainp) { if (*remainp >= sizeof(int64_t)) { *intp = load_64_be((unsigned char *)*bufp); *bufp += sizeof(int64_t); *remainp -= sizeof(int64_t); return(0); } else return(ENOMEM); }
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; }
OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, unsigned char *ptr, int bodysize, gss_buffer_t message_buffer, int *conf_state, int *qop_state, int toktype) { krb5_context context = *contextptr; krb5_data plain; gssint_uint64 seqnum; size_t ec, rrc; int key_usage; unsigned char acceptor_flag; krb5_checksum sum; krb5_error_code err; krb5_boolean valid; krb5_keyblock *key; ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0); ASSERT(ctx->big_endian == 0); ASSERT(ctx->proto == 1); if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; 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)); /* Oops. I wrote this code assuming ptr would be at the start of the token header. */ ptr -= 2; bodysize += 2; if (bodysize < 16) { defective: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { *minor_status = (OM_uint32)G_BAD_DIRECTION; return GSS_S_BAD_SIG; } /* Two things to note here. First, we can't really enforce the use of the acceptor's subkey, if we're the acceptor; the initiator may have sent messages before getting the subkey. We could probably enforce it if we're the initiator. Second, if someone tweaks the code to not set the flag telling the krb5 library to generate a new subkey in the AP-REP message, the MIT library may include a subkey anyways -- namely, a copy of the AP-REQ subkey, if it was provided. So the initiator may think we wanted a subkey, and set the flag, even though we weren't trying to set the subkey. The "other" key, the one not asserted by the acceptor, will have the same value in that case, though, so we can just ignore the flag. */ if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_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 */ if (toktype == KG_TOK_WRAP_MSG) { if (load_16_be(ptr) != 0x0504) goto defective; if (ptr[3] != 0xff) goto defective; ec = load_16_be(ptr+4); rrc = load_16_be(ptr+6); seqnum = load_64_be(ptr+8); if (!rotate_left(ptr+16, bodysize-16, rrc)) { no_mem: *minor_status = ENOMEM; return GSS_S_FAILURE; } if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) { /* confidentiality */ krb5_enc_data cipher; unsigned char *althdr; size_t plainlen; if (conf_state) *conf_state = 1; /* Do we have no decrypt_size function? For all current cryptosystems, the ciphertext size will be larger than the plaintext size. */ cipher.enctype = key->enctype; cipher.ciphertext.length = bodysize - 16; cipher.ciphertext.data = (char *)ptr + 16; plain.length = plainlen = bodysize - 16; plain.data = MALLOC(plain.length); if (plain.data == NULL) goto no_mem; err = krb5_c_decrypt(context, key, key_usage, 0, &cipher, &plain); if (err) { goto error; } /* Don't use bodysize here! Use the fact that plain.length has been adjusted to the correct length. */ althdr = (uchar_t *)plain.data + plain.length - 16; if (load_16_be(althdr) != 0x0504 || althdr[2] != ptr[2] || althdr[3] != ptr[3] || memcmp(althdr+8, ptr+8, 8)) { FREE(plain.data, plainlen); goto defective; } message_buffer->length = plain.length - ec - 16; message_buffer->value = MALLOC(message_buffer->length); if (message_buffer->value == NULL) { FREE(plain.data, plainlen); goto no_mem; } (void) memcpy(message_buffer->value, plain.data, message_buffer->length); FREE(plain.data, plainlen); } else { /* no confidentiality */ if (conf_state) *conf_state = 0; if (ec + 16 < ec) /* overflow check */ goto defective; if (ec + 16 > bodysize) goto defective; /* We have: header | msg | cksum. We need cksum(msg | header). Rotate the first two. */ store_16_be(0, ptr+4); store_16_be(0, ptr+6); plain.length = bodysize-ec; plain.data = (char *)ptr; if (!rotate_left(ptr, bodysize-ec, 16)) goto no_mem; sum.length = ec; if (sum.length != ctx->cksum_size) { *minor_status = 0; return GSS_S_BAD_SIG; } sum.contents = ptr+bodysize-ec; sum.checksum_type = ctx->cksumtype; err = krb5_c_verify_checksum(context, key, key_usage, &plain, &sum, &valid); if (err) { *minor_status = err; return GSS_S_BAD_SIG; } if (!valid) { *minor_status = 0; return GSS_S_BAD_SIG; } message_buffer->length = plain.length - 16; message_buffer->value = MALLOC(message_buffer->length); if (message_buffer->value == NULL) goto no_mem; (void) memcpy(message_buffer->value, plain.data, message_buffer->length); /* * Solaris Kerberos: Restore the original token. * This allows the token to be detected as a duplicate if it * is passed in to gss_unwrap() again. */ if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16)) goto no_mem; store_16_be(ec, ptr+4); store_16_be(rrc, ptr+6); } err = g_order_check(&ctx->seqstate, seqnum); *minor_status = 0; return err; } else if (toktype == KG_TOK_MIC_MSG) { /* wrap token, no confidentiality */ if (load_16_be(ptr) != 0x0404) goto defective; verify_mic_1: if (ptr[3] != 0xff) goto defective; if (load_32_be(ptr+4) != (ulong_t)0xffffffffU) goto defective; seqnum = load_64_be(ptr+8); plain.length = message_buffer->length + 16; plain.data = MALLOC(plain.length); if (plain.data == NULL) goto no_mem; if (message_buffer->length) (void) memcpy(plain.data, message_buffer->value, message_buffer->length); (void) memcpy(plain.data + message_buffer->length, ptr, 16); sum.length = bodysize - 16; sum.contents = ptr + 16; sum.checksum_type = ctx->cksumtype; err = krb5_c_verify_checksum(context, key, key_usage, &plain, &sum, &valid); if (err) { error: FREE(plain.data, plain.length); *minor_status = err; save_error_info(*minor_status, context); return GSS_S_BAD_SIG; /* XXX */ } FREE(plain.data, plain.length); if (!valid) { *minor_status = 0; return GSS_S_BAD_SIG; } err = g_order_check(&ctx->seqstate, seqnum); *minor_status = 0; return err; } else if (toktype == KG_TOK_DEL_CTX) { if (load_16_be(ptr) != 0x0405) goto defective; message_buffer = (gss_buffer_t)&empty_message; goto verify_mic_1; } else { goto defective; } }
OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, unsigned char *ptr, unsigned int bodysize, gss_buffer_t message_buffer, int *conf_state, gss_qop_t *qop_state, int toktype) { krb5_context context = *contextptr; krb5_data plain; uint64_t seqnum; size_t ec, rrc; int key_usage; unsigned char acceptor_flag; krb5_checksum sum; krb5_error_code err; krb5_boolean valid; krb5_key key; krb5_cksumtype cksumtype; if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; 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)); /* Oops. I wrote this code assuming ptr would be at the start of the token header. */ ptr -= 2; bodysize += 2; if (bodysize < 16) { defective: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { *minor_status = (OM_uint32)G_BAD_DIRECTION; return GSS_S_BAD_SIG; } /* Two things to note here. First, we can't really enforce the use of the acceptor's subkey, if we're the acceptor; the initiator may have sent messages before getting the subkey. We could probably enforce it if we're the initiator. Second, if someone tweaks the code to not set the flag telling the krb5 library to generate a new subkey in the AP-REP message, the MIT library may include a subkey anyways -- namely, a copy of the AP-REQ subkey, if it was provided. So the initiator may think we wanted a subkey, and set the flag, even though we weren't trying to set the subkey. The "other" key, the one not asserted by the acceptor, will have the same value in that case, though, so we can just ignore the flag. */ if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { key = ctx->acceptor_subkey; cksumtype = ctx->acceptor_subkey_cksumtype; } else { key = ctx->subkey; cksumtype = ctx->cksumtype; } assert(key != NULL); if (toktype == KG_TOK_WRAP_MSG) { if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) goto defective; if (ptr[3] != 0xff) goto defective; ec = load_16_be(ptr+4); rrc = load_16_be(ptr+6); seqnum = load_64_be(ptr+8); if (!gss_krb5int_rotate_left(ptr+16, bodysize-16, rrc)) { no_mem: *minor_status = ENOMEM; return GSS_S_FAILURE; } if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) { /* confidentiality */ krb5_enc_data cipher; unsigned char *althdr; if (conf_state) *conf_state = 1; /* Do we have no decrypt_size function? For all current cryptosystems, the ciphertext size will be larger than the plaintext size. */ cipher.enctype = key->keyblock.enctype; cipher.ciphertext.length = bodysize - 16; cipher.ciphertext.data = (char *)ptr + 16; plain.length = bodysize - 16; plain.data = gssalloc_malloc(plain.length); if (plain.data == NULL) goto no_mem; err = krb5_k_decrypt(context, key, key_usage, 0, &cipher, &plain); if (err) { gssalloc_free(plain.data); goto error; } /* Don't use bodysize here! Use the fact that cipher.ciphertext.length has been adjusted to the correct length. */ althdr = (unsigned char *)plain.data + plain.length - 16; if (load_16_be(althdr) != KG2_TOK_WRAP_MSG || althdr[2] != ptr[2] || althdr[3] != ptr[3] || memcmp(althdr+8, ptr+8, 8)) { free(plain.data); goto defective; } message_buffer->value = plain.data; message_buffer->length = plain.length - ec - 16; if(message_buffer->length == 0) { gssalloc_free(message_buffer->value); message_buffer->value = NULL; } } else { size_t cksumsize; err = krb5_c_checksum_length(context, cksumtype, &cksumsize); if (err) goto error; /* no confidentiality */ if (conf_state) *conf_state = 0; if (ec + 16 < ec) /* overflow check */ goto defective; if (ec + 16 > bodysize) goto defective; /* We have: header | msg | cksum. We need cksum(msg | header). Rotate the first two. */ store_16_be(0, ptr+4); store_16_be(0, ptr+6); plain = make_data(ptr, bodysize - ec); if (!gss_krb5int_rotate_left(ptr, bodysize-ec, 16)) goto no_mem; sum.length = ec; if (sum.length != cksumsize) { *minor_status = 0; return GSS_S_BAD_SIG; } sum.contents = ptr+bodysize-ec; sum.checksum_type = cksumtype; err = krb5_k_verify_checksum(context, key, key_usage, &plain, &sum, &valid); if (err) goto error; if (!valid) { *minor_status = 0; return GSS_S_BAD_SIG; } message_buffer->length = plain.length - 16; message_buffer->value = gssalloc_malloc(message_buffer->length); if (message_buffer->value == NULL) goto no_mem; memcpy(message_buffer->value, plain.data, message_buffer->length); } err = g_seqstate_check(ctx->seqstate, seqnum); *minor_status = 0; return err; } else if (toktype == KG_TOK_MIC_MSG) { /* wrap token, no confidentiality */ if (load_16_be(ptr) != KG2_TOK_MIC_MSG) goto defective; verify_mic_1: if (ptr[3] != 0xff) goto defective; if (load_32_be(ptr+4) != 0xffffffffL) goto defective; seqnum = load_64_be(ptr+8); plain.length = message_buffer->length + 16; plain.data = malloc(plain.length); if (plain.data == NULL) goto no_mem; if (message_buffer->length) memcpy(plain.data, message_buffer->value, message_buffer->length); memcpy(plain.data + message_buffer->length, ptr, 16); sum.length = bodysize - 16; sum.contents = ptr + 16; sum.checksum_type = cksumtype; err = krb5_k_verify_checksum(context, key, key_usage, &plain, &sum, &valid); free(plain.data); plain.data = NULL; if (err) { error: *minor_status = err; save_error_info(*minor_status, context); return GSS_S_BAD_SIG; /* XXX */ } if (!valid) { *minor_status = 0; return GSS_S_BAD_SIG; } err = g_seqstate_check(ctx->seqstate, seqnum); *minor_status = 0; return err; } else if (toktype == KG_TOK_DEL_CTX) { if (load_16_be(ptr) != KG2_TOK_DEL_CTX) goto defective; message_buffer = (gss_buffer_t)&empty_message; goto verify_mic_1; } else { goto defective; } }
OM_uint32 gss_krb5int_unseal_v3_iov(krb5_context context, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, gss_iov_buffer_desc *iov, int iov_count, int *conf_state, gss_qop_t *qop_state, int toktype) { OM_uint32 code; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; unsigned char acceptor_flag; unsigned char *ptr = NULL; int key_usage; size_t rrc, ec; size_t data_length, assoc_data_length; krb5_key key; gssint_uint64 seqnum; krb5_boolean valid; krb5_cksumtype cksumtype; int conf_flag = 0; if (ctx->big_endian != 0) return GSS_S_DEFECTIVE_TOKEN; if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); assert(header != NULL); padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding != NULL && padding->buffer.length != 0) return GSS_S_DEFECTIVE_TOKEN; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; 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)); kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); ptr = (unsigned char *)header->buffer.value; if (header->buffer.length < 16) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { *minor_status = (OM_uint32)G_BAD_DIRECTION; return GSS_S_BAD_SIG; } if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { key = ctx->acceptor_subkey; cksumtype = ctx->acceptor_subkey_cksumtype; } else { key = ctx->subkey; cksumtype = ctx->cksumtype; } assert(key != NULL); if (toktype == KG_TOK_WRAP_MSG) { unsigned int k5_trailerlen; if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) goto defective; conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0); if (ptr[3] != 0xFF) goto defective; ec = load_16_be(ptr + 4); rrc = load_16_be(ptr + 6); seqnum = load_64_be(ptr + 8); code = krb5_c_crypto_length(context, key->keyblock.enctype, conf_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, &k5_trailerlen); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } /* Deal with RRC */ if (trailer == NULL) { size_t desired_rrc = k5_trailerlen; if (conf_flag) { desired_rrc += 16; /* E(Header) */ if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) desired_rrc += ec; } /* According to MS, we only need to deal with a fixed RRC for DCE */ if (rrc != desired_rrc) goto defective; } else if (rrc != 0) { /* Should have been rotated by kg_unseal_stream_iov() */ goto defective; } if (conf_flag) { unsigned char *althdr; /* Decrypt */ code = kg_decrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), ec, rrc, key, key_usage, 0, iov, iov_count); if (code != 0) { *minor_status = code; return GSS_S_BAD_SIG; } /* Validate header integrity */ if (trailer == NULL) althdr = (unsigned char *)header->buffer.value + 16 + ec; else althdr = (unsigned char *)trailer->buffer.value + ec; if (load_16_be(althdr) != KG2_TOK_WRAP_MSG || althdr[2] != ptr[2] || althdr[3] != ptr[3] || memcmp(althdr + 8, ptr + 8, 8) != 0) { *minor_status = 0; return GSS_S_BAD_SIG; } } else { /* Verify checksum: note EC is checksum size here, not padding */ if (ec != k5_trailerlen) goto defective; /* Zero EC, RRC before computing checksum */ store_16_be(0, ptr + 4); store_16_be(0, ptr + 6); code = kg_verify_checksum_iov_v3(context, cksumtype, rrc, key, key_usage, iov, iov_count, &valid); if (code != 0 || valid == FALSE) { *minor_status = code; return GSS_S_BAD_SIG; } } code = g_order_check(&ctx->seqstate, seqnum); } else if (toktype == KG_TOK_MIC_MSG) { if (load_16_be(ptr) != KG2_TOK_MIC_MSG) goto defective; verify_mic_1: if (ptr[3] != 0xFF) goto defective; seqnum = load_64_be(ptr + 8); code = kg_verify_checksum_iov_v3(context, cksumtype, 0, key, key_usage, iov, iov_count, &valid); if (code != 0 || valid == FALSE) { *minor_status = code; return GSS_S_BAD_SIG; } code = g_order_check(&ctx->seqstate, seqnum); } else if (toktype == KG_TOK_DEL_CTX) { if (load_16_be(ptr) != KG2_TOK_DEL_CTX) goto defective; goto verify_mic_1; } else { goto defective; } *minor_status = 0; if (conf_state != NULL) *conf_state = conf_flag; return code; defective: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; }
static krb5_error_code decode_ad_policy_info(const krb5_data *data, char **msg_out) { struct ad_policy_info policy; uint64_t password_days; const char *p; char *msg; struct k5buf buf; *msg_out = NULL; if (data->length != AD_POLICY_INFO_LENGTH) return 0; p = data->data; policy.zero_bytes = load_16_be(p); p += 2; /* first two bytes are zeros */ if (policy.zero_bytes != 0) return 0; /* Read in the rest of structure */ policy.min_length_password = load_32_be(p); p += 4; policy.password_history = load_32_be(p); p += 4; policy.password_properties = load_32_be(p); p += 4; policy.expire = load_64_be(p); p += 8; policy.min_passwordage = load_64_be(p); p += 8; /* Check that we processed exactly the expected number of bytes. */ assert(p == data->data + AD_POLICY_INFO_LENGTH); krb5int_buf_init_dynamic(&buf); /* * Update src/tests/misc/test_chpw_message.c if changing these strings! */ if (policy.password_properties & AD_POLICY_COMPLEX) { krb5int_buf_add(&buf, _("The password must include numbers or symbols. " "Don't include any part of your name in the " "password.")); } if (policy.min_length_password > 0) { add_spaces(&buf); krb5int_buf_add_fmt(&buf, ngettext("The password must contain at least %d " "character.", "The password must contain at least %d " "characters.", policy.min_length_password), policy.min_length_password); } if (policy.password_history) { add_spaces(&buf); krb5int_buf_add_fmt(&buf, ngettext("The password must be different from the " "previous password.", "The password must be different from the " "previous %d passwords.", policy.password_history), policy.password_history); } if (policy.min_passwordage) { password_days = policy.min_passwordage / AD_POLICY_TIME_TO_DAYS; if (password_days == 0) password_days = 1; add_spaces(&buf); krb5int_buf_add_fmt(&buf, ngettext("The password can only be changed once a " "day.", "The password can only be changed every " "%d days.", (int)password_days), (int)password_days); } msg = krb5int_buf_data(&buf); if (msg == NULL) return ENOMEM; if (*msg != '\0') *msg_out = msg; else free(msg); return 0; }