/* * krb5_ser_pack_int64() - Pack an 8-byte integer if space is available. * Update buffer pointer and remaining space. */ krb5_error_code KRB5_CALLCONV krb5_ser_pack_int64(int64_t iarg, krb5_octet **bufp, size_t *remainp) { if (*remainp >= sizeof(int64_t)) { store_64_be(iarg, (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; }
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; }
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 gss_krb5int_make_seal_token_v3_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; unsigned char acceptor_flag; unsigned short tok_id; unsigned char *outbuf = NULL; unsigned char *tbuf = NULL; int key_usage; size_t rrc = 0; unsigned int gss_headerlen, gss_trailerlen; krb5_key key; krb5_cksumtype cksumtype; size_t data_length, assoc_data_length; assert(ctx->big_endian == 0); assert(ctx->proto == 1); 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); assert(cksumtype != 0); kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); 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) padding->buffer.length = 0; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { unsigned int k5_headerlen, k5_trailerlen, k5_padlen; size_t ec = 0; size_t conf_data_length = data_length - assoc_data_length; code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); if (code != 0) goto cleanup; code = krb5_c_padding_length(context, key->keyblock.enctype, conf_data_length + 16 /* E(Header) */, &k5_padlen); if (code != 0) goto cleanup; if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) { /* Windows rejects AEAD tokens with non-zero EC */ code = krb5_c_block_size(context, key->keyblock.enctype, &ec); if (code != 0) goto cleanup; } else ec = k5_padlen; code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen); if (code != 0) goto cleanup; gss_headerlen = 16 /* Header */ + k5_headerlen; gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen; if (trailer == NULL) { rrc = gss_trailerlen; /* Workaround for Windows bug where it rotates by EC + RRC */ if (ctx->gss_flags & GSS_C_DCE_STYLE) rrc -= ec; gss_headerlen += gss_trailerlen; } if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(header, (size_t) gss_headerlen); } else if (header->buffer.length < gss_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; outbuf = (unsigned char *)header->buffer.value; header->buffer.length = (size_t) gss_headerlen; if (trailer != NULL) { if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); else if (trailer->buffer.length < gss_trailerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; trailer->buffer.length = (size_t) gss_trailerlen; } /* 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); /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */ if (trailer == NULL) tbuf = (unsigned char *)header->buffer.value + 16; /* Header */ else tbuf = (unsigned char *)trailer->buffer.value; memset(tbuf, 0xFF, ec); memcpy(tbuf + ec, header->buffer.value, 16); code = kg_encrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), ec, rrc, key, key_usage, 0, iov, iov_count); if (code != 0) goto cleanup; /* RRC */ store_16_be(rrc, outbuf + 6); ctx->seq_send++; } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { tok_id = KG2_TOK_WRAP_MSG; wrap_with_checksum: gss_headerlen = 16; code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_CHECKSUM, &gss_trailerlen); if (code != 0) goto cleanup; assert(gss_trailerlen <= 0xFFFF); if (trailer == NULL) { rrc = gss_trailerlen; gss_headerlen += gss_trailerlen; } if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(header, (size_t) gss_headerlen); else if (header->buffer.length < gss_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; outbuf = (unsigned char *)header->buffer.value; header->buffer.length = (size_t) gss_headerlen; if (trailer != NULL) { if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); else if (trailer->buffer.length < gss_trailerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; trailer->buffer.length = (size_t) gss_trailerlen; } /* 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); code = kg_make_checksum_iov_v3(context, cksumtype, rrc, key, key_usage, iov, iov_count); if (code != 0) goto cleanup; ctx->seq_send++; if (toktype == KG_TOK_WRAP_MSG) { /* Fix up EC field */ store_16_be(gss_trailerlen, outbuf + 4); /* Fix up RRC field */ store_16_be(rrc, outbuf + 6); } } else if (toktype == KG_TOK_MIC_MSG) { tok_id = KG2_TOK_MIC_MSG; trailer = NULL; goto wrap_with_checksum; } else if (toktype == KG_TOK_DEL_CTX) { tok_id = KG2_TOK_DEL_CTX; goto wrap_with_checksum; } else { abort(); } code = 0; if (conf_state != NULL) *conf_state = conf_req_flag; cleanup: if (code != 0) kg_release_iov(iov, iov_count); return code; }