Home | History | Annotate | Download | only in mech
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * lib/gssapi/krb5/k5sealv3.c
      9  *
     10  * Copyright 2003,2004 by the Massachusetts Institute of Technology.
     11  * All Rights Reserved.
     12  *
     13  * Export of this software from the United States of America may
     14  *   require a specific license from the United States Government.
     15  *   It is the responsibility of any person or organization contemplating
     16  *   export to obtain such a license before exporting.
     17  *
     18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     19  * distribute this software and its documentation for any purpose and
     20  * without fee is hereby granted, provided that the above copyright
     21  * notice appear in all copies and that both that copyright notice and
     22  * this permission notice appear in supporting documentation, and that
     23  * the name of M.I.T. not be used in advertising or publicity pertaining
     24  * to distribution of the software without specific, written prior
     25  * permission.  Furthermore if you modify this software you must label
     26  * your software as modified software and not distribute it in such a
     27  * fashion that it might be confused with the original M.I.T. software.
     28  * M.I.T. makes no representations about the suitability of
     29  * this software for any purpose.  It is provided "as is" without express
     30  * or implied warranty.
     31  *
     32  *
     33  */
     34 /* draft-ietf-krb-wg-gssapi-cfx-05 */
     35 
     36 #ifndef _KERNEL
     37 #include <assert.h>
     38 #include <stdarg.h>
     39 
     40 #define ASSERT assert
     41 #endif
     42 
     43 /* Solaris Kerberos */
     44 #include "k5-int.h"		/* for zap() */
     45 #include "k5-platform.h"
     46 
     47 /* Solaris Kerberos */
     48 #include "k5-platform-store_16.h"
     49 #include "k5-platform-store_64.h"
     50 #include "k5-platform-load_16.h"
     51 #include "k5-platform-load_64.h"
     52 
     53 #include "gssapiP_krb5.h"
     54 #include <sys/int_limits.h>
     55 
     56 static int
     57 rotate_left (void *ptr, size_t bufsiz, size_t rc)
     58 {
     59     /* Optimize for receiving.  After some debugging is done, the MIT
     60        implementation won't do any rotates on sending, and while
     61        debugging, they'll be randomly chosen.
     62 
     63        Return 1 for success, 0 for failure (ENOMEM).  */
     64     void *tbuf;
     65 
     66     if (bufsiz == 0)
     67 	return 1;
     68     rc = rc % bufsiz;
     69     if (rc == 0)
     70 	return 1;
     71 
     72     tbuf = MALLOC(rc);
     73     if (tbuf == 0)
     74 	return 0;
     75     (void) memcpy(tbuf, ptr, rc);
     76     (void) memmove(ptr, (char *)ptr + rc, bufsiz - rc);
     77     (void) memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
     78     FREE(tbuf, rc);
     79     return 1;
     80 }
     81 
     82 static const gss_buffer_desc empty_message = { 0, 0 };
     83 
     84 #define FLAG_SENDER_IS_ACCEPTOR	0x01
     85 #define FLAG_WRAP_CONFIDENTIAL	0x02
     86 #define FLAG_ACCEPTOR_SUBKEY	0x04
     87 
     88 krb5_error_code
     89 gss_krb5int_make_seal_token_v3 (krb5_context context,
     90 				krb5_gss_ctx_id_rec *ctx,
     91 				const gss_buffer_desc * message,
     92 				gss_buffer_t token,
     93 				int conf_req_flag, int toktype)
     94 {
     95     size_t bufsize = 16;
     96     unsigned char *outbuf = 0;
     97     krb5_error_code err;
     98     int key_usage;
     99     unsigned char acceptor_flag;
    100     const gss_buffer_desc *message2 = message;
    101 #ifdef CFX_EXERCISE
    102     size_t rrc;
    103 #endif
    104     size_t ec;
    105     unsigned short tok_id;
    106     krb5_checksum sum;
    107     krb5_keyblock *key;
    108 
    109     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
    110     ASSERT(ctx->big_endian == 0);
    111 
    112     acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
    113     key_usage = (toktype == KG_TOK_WRAP_MSG
    114 		 ? (ctx->initiate
    115 		    ? KG_USAGE_INITIATOR_SEAL
    116 		    : KG_USAGE_ACCEPTOR_SEAL)
    117 		 : (ctx->initiate
    118 		    ? KG_USAGE_INITIATOR_SIGN
    119 		    : KG_USAGE_ACCEPTOR_SIGN));
    120     if (ctx->have_acceptor_subkey) {
    121 	key = ctx->acceptor_subkey;
    122     } else {
    123 	key = ctx->enc;
    124     }
    125 
    126 #ifdef _KERNEL
    127     context->kef_cipher_mt = get_cipher_mech_type(context, key);
    128     context->kef_hash_mt = get_hash_mech_type(context, key);
    129 
    130     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
    131 	return (GSS_S_FAILURE);
    132     }
    133 
    134 #endif /* _KERNEL */
    135 
    136 #ifdef CFX_EXERCISE
    137     {
    138 	static int initialized = 0;
    139 	if (!initialized) {
    140 	    srand(time(0));
    141 	    initialized = 1;
    142 	}
    143     }
    144 #endif
    145 
    146     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
    147 	krb5_data plain;
    148 	krb5_enc_data cipher;
    149 	size_t ec_max;
    150 	size_t tlen;
    151 
    152 	/* 300: Adds some slop.  */
    153 	if (SIZE_MAX - 300 < message->length)
    154 	    return ENOMEM;
    155 	ec_max = SIZE_MAX - message->length - 300;
    156 	if (ec_max > 0xffff)
    157 	    ec_max = 0xffff;
    158 	/*
    159 	 * EC should really be a multiple (1) of the number of octets that
    160 	 * the cryptosystem would pad by if we didn't have the filler.
    161 	 *
    162 	 * For AES-CTS this will always be 0 and we expect no further
    163 	 * enctypes, so there should be no issue here.
    164 	 */
    165 	ec = 0;
    166 	plain.length = message->length + 16 + ec;
    167 	plain.data = MALLOC(plain.length);
    168 	if (plain.data == NULL)
    169 	    return ENOMEM;
    170 
    171 	/* Get size of ciphertext.  */
    172 	if ((err = krb5_c_encrypt_length(context,
    173 		ctx->enc->enctype, plain.length, &tlen))) {
    174 	    FREE(plain.data, plain.length);
    175 	    return (err);
    176         }
    177 
    178 	bufsize = 16 + tlen;
    179 	/* Allocate space for header plus encrypted data.  */
    180 	outbuf = MALLOC(bufsize);
    181 	if (outbuf == NULL) {
    182 	    FREE(plain.data, plain.length);
    183 	    return ENOMEM;
    184 	}
    185 
    186 	/* TOK_ID */
    187 	store_16_be(0x0504, outbuf);
    188 	/* flags */
    189 	outbuf[2] = (acceptor_flag
    190 		     | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
    191 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
    192 	/* filler */
    193 	outbuf[3] = 0xff;
    194 	/* EC */
    195 	store_16_be(ec, outbuf+4);
    196 	/* RRC */
    197 	store_16_be(0, outbuf+6);
    198 	store_64_be(ctx->seq_send, outbuf+8);
    199 
    200 	(void) memcpy(plain.data, message->value, message->length);
    201 	(void) memset(plain.data + message->length, 'x', ec);
    202 	(void) memcpy(plain.data + message->length + ec, outbuf, 16);
    203 
    204 	/* Should really use scatter/gather crypto interfaces */
    205 	cipher.ciphertext.data = (char *)outbuf + 16;
    206 	cipher.ciphertext.length = bufsize - 16;
    207 	cipher.enctype = key->enctype;
    208 	err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
    209 	(void) bzero(plain.data, plain.length);
    210 	FREE(plain.data, plain.length);
    211 	plain.data = 0;
    212 	if (err)
    213 	    goto error;
    214 
    215 	/* Now that we know we're returning a valid token....  */
    216 	ctx->seq_send++;
    217 
    218 #ifdef CFX_EXERCISE
    219 	rrc = rand() & 0xffff;
    220 	if (rotate_left(outbuf+16, bufsize-16,
    221 			(bufsize-16) - (rrc % (bufsize - 16))))
    222 	    store_16_be(rrc, outbuf+6);
    223 	/* If the rotate fails, don't worry about it.  */
    224 #endif
    225     } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
    226 	krb5_data plain;
    227 
    228 	/* Here, message is the application-supplied data; message2 is
    229 	   what goes into the output token.  They may be the same, or
    230 	   message2 may be empty (for MIC).  */
    231 
    232 	tok_id = 0x0504;
    233 
    234     wrap_with_checksum:
    235 	plain.length = message->length + 16;
    236 	plain.data = MALLOC(message->length + 16);
    237 	if (plain.data == NULL)
    238 	    return ENOMEM;
    239 
    240 	if (ctx->cksum_size > 0xffff) {
    241 	    FREE(plain.data, plain.length);
    242 	    return EINVAL;
    243 	}
    244 
    245 	bufsize = 16 + message2->length + ctx->cksum_size;
    246 	outbuf = MALLOC(bufsize);
    247 	if (outbuf == NULL) {
    248 	    FREE(plain.data, plain.length);
    249 	    plain.data = 0;
    250 	    err = ENOMEM;
    251 	    goto error;
    252 	}
    253 
    254 	/* TOK_ID */
    255 	store_16_be(tok_id, outbuf);
    256 	/* flags */
    257 	outbuf[2] = (acceptor_flag
    258 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
    259 	/* filler */
    260 	outbuf[3] = 0xff;
    261 	if (toktype == KG_TOK_WRAP_MSG) {
    262 	    /* Use 0 for checksum calculation, substitute
    263 	       checksum length later.  */
    264 	    /* EC */
    265 	    store_16_be(0, outbuf+4);
    266 	    /* RRC */
    267 	    store_16_be(0, outbuf+6);
    268 	} else {
    269 	    /* MIC and DEL store 0xFF in EC and RRC.  */
    270 	    store_16_be(0xffff, outbuf+4);
    271 	    store_16_be(0xffff, outbuf+6);
    272 	}
    273 	store_64_be(ctx->seq_send, outbuf+8);
    274 
    275 	(void) memcpy(plain.data, message->value, message->length);
    276 	(void) memcpy(plain.data + message->length, outbuf, 16);
    277 
    278 	/* Fill in the output token -- data contents, if any, and
    279 	   space for the checksum.  */
    280 	if (message2->length)
    281 	    (void) memcpy(outbuf + 16, message2->value, message2->length);
    282 
    283 	sum.contents = outbuf + 16 + message2->length;
    284 	sum.length = ctx->cksum_size;
    285 
    286 	err = krb5_c_make_checksum(context, ctx->cksumtype, key,
    287 				   key_usage, &plain, &sum);
    288 	bzero(plain.data, plain.length);
    289 	FREE(plain.data, plain.length);
    290 	plain.data = 0;
    291 	if (err) {
    292 	    bzero(outbuf,bufsize);
    293 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
    294 	    goto error;
    295 	}
    296 	if (sum.length != ctx->cksum_size) {
    297 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
    298 	    goto error;
    299 	}
    300 	(void) memcpy(outbuf + 16 + message2->length, sum.contents,
    301 	    ctx->cksum_size);
    302 	krb5_free_checksum_contents(context, &sum);
    303 	sum.contents = 0;
    304 	/* Now that we know we're actually generating the token...  */
    305 	ctx->seq_send++;
    306 
    307 	if (toktype == KG_TOK_WRAP_MSG) {
    308 #ifdef CFX_EXERCISE
    309 	    rrc = rand() & 0xffff;
    310 	    /* If the rotate fails, don't worry about it.  */
    311 	    if (rotate_left(outbuf+16, bufsize-16,
    312 			    (bufsize-16) - (rrc % (bufsize - 16))))
    313 		store_16_be(rrc, outbuf+6);
    314 #endif
    315 	    /* Fix up EC field.  */
    316 	    store_16_be(ctx->cksum_size, outbuf+4);
    317 	} else {
    318 	    store_16_be(0xffff, outbuf+6);
    319 	}
    320     } else if (toktype == KG_TOK_MIC_MSG) {
    321 	tok_id = 0x0404;
    322 	message2 = &empty_message;
    323 	goto wrap_with_checksum;
    324     } else if (toktype == KG_TOK_DEL_CTX) {
    325 	/*
    326 	 * Solaris Kerberos:
    327 	 * No token should be generated for context deletion. Just
    328 	 * return.
    329 	 */
    330 	return 0;
    331     } else {
    332 	err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
    333 	goto error;
    334     }
    335 
    336     token->value = outbuf;
    337     token->length = bufsize;
    338     return 0;
    339 
    340 error:
    341     FREE(outbuf, bufsize);
    342     token->value = NULL;
    343     token->length = 0;
    344     return err;
    345 }
    346 
    347 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
    348    conf_state is only valid if SEAL. */
    349 
    350 OM_uint32
    351 gss_krb5int_unseal_token_v3(krb5_context *contextptr,
    352 			    OM_uint32 *minor_status,
    353 			    krb5_gss_ctx_id_rec *ctx,
    354 			    unsigned char *ptr, int bodysize,
    355 			    gss_buffer_t message_buffer,
    356 			    int *conf_state, int *qop_state, int toktype)
    357 {
    358     krb5_context context = *contextptr;
    359     krb5_data plain;
    360     gssint_uint64 seqnum;
    361     size_t ec, rrc;
    362     int key_usage;
    363     unsigned char acceptor_flag;
    364     krb5_checksum sum;
    365     krb5_error_code err;
    366     krb5_boolean valid;
    367     krb5_keyblock *key;
    368 
    369     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
    370     ASSERT(ctx->big_endian == 0);
    371     ASSERT(ctx->proto == 1);
    372 
    373     if (qop_state)
    374 	*qop_state = GSS_C_QOP_DEFAULT;
    375 
    376     acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
    377     key_usage = (toktype == KG_TOK_WRAP_MSG
    378 		 ? (!ctx->initiate
    379 		    ? KG_USAGE_INITIATOR_SEAL
    380 		    : KG_USAGE_ACCEPTOR_SEAL)
    381 		 : (!ctx->initiate
    382 		    ? KG_USAGE_INITIATOR_SIGN
    383 		    : KG_USAGE_ACCEPTOR_SIGN));
    384 
    385     /* Oops.  I wrote this code assuming ptr would be at the start of
    386        the token header.  */
    387     ptr -= 2;
    388     bodysize += 2;
    389 
    390     if (bodysize < 16) {
    391     defective:
    392 	*minor_status = 0;
    393 	return GSS_S_DEFECTIVE_TOKEN;
    394     }
    395     if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
    396 	*minor_status = (OM_uint32)G_BAD_DIRECTION;
    397 	return GSS_S_BAD_SIG;
    398     }
    399 
    400     /* Two things to note here.
    401 
    402        First, we can't really enforce the use of the acceptor's subkey,
    403        if we're the acceptor; the initiator may have sent messages
    404        before getting the subkey.  We could probably enforce it if
    405        we're the initiator.
    406 
    407        Second, if someone tweaks the code to not set the flag telling
    408        the krb5 library to generate a new subkey in the AP-REP
    409        message, the MIT library may include a subkey anyways --
    410        namely, a copy of the AP-REQ subkey, if it was provided.  So
    411        the initiator may think we wanted a subkey, and set the flag,
    412        even though we weren't trying to set the subkey.  The "other"
    413        key, the one not asserted by the acceptor, will have the same
    414        value in that case, though, so we can just ignore the flag.  */
    415     if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
    416 	key = ctx->acceptor_subkey;
    417     } else {
    418 	key = ctx->enc;
    419     }
    420 
    421 #ifdef _KERNEL
    422     context->kef_cipher_mt = get_cipher_mech_type(context, key);
    423     context->kef_hash_mt = get_hash_mech_type(context, key);
    424 
    425     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
    426 	return (GSS_S_FAILURE);
    427     }
    428 #endif /* _KERNEL */
    429 
    430     if (toktype == KG_TOK_WRAP_MSG) {
    431 	if (load_16_be(ptr) != 0x0504)
    432 	    goto defective;
    433 	if (ptr[3] != 0xff)
    434 	    goto defective;
    435 	ec = load_16_be(ptr+4);
    436 	rrc = load_16_be(ptr+6);
    437 	seqnum = load_64_be(ptr+8);
    438 	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
    439 	no_mem:
    440 	    *minor_status = ENOMEM;
    441 	    return GSS_S_FAILURE;
    442 	}
    443 	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
    444 	    /* confidentiality */
    445 	    krb5_enc_data cipher;
    446 	    unsigned char *althdr;
    447             size_t plainlen;
    448 
    449 	    if (conf_state)
    450 		*conf_state = 1;
    451 	    /* Do we have no decrypt_size function?
    452 
    453 	       For all current cryptosystems, the ciphertext size will
    454 	       be larger than the plaintext size.  */
    455 	    cipher.enctype = key->enctype;
    456 	    cipher.ciphertext.length = bodysize - 16;
    457 	    cipher.ciphertext.data = (char *)ptr + 16;
    458 	    plain.length = plainlen = bodysize - 16;
    459 	    plain.data = MALLOC(plain.length);
    460 	    if (plain.data == NULL)
    461 		goto no_mem;
    462 	    err = krb5_c_decrypt(context, key, key_usage, 0,
    463 				 &cipher, &plain);
    464 	    if (err) {
    465 		goto error;
    466 	    }
    467 	    /* Don't use bodysize here!  Use the fact that
    468 	       plain.length has been adjusted to the
    469 	       correct length.  */
    470 	    althdr = (uchar_t *)plain.data + plain.length - 16;
    471 	    if (load_16_be(althdr) != 0x0504
    472 		|| althdr[2] != ptr[2]
    473 		|| althdr[3] != ptr[3]
    474 		|| memcmp(althdr+8, ptr+8, 8)) {
    475 		FREE(plain.data, plainlen);
    476 		goto defective;
    477 	    }
    478 	    message_buffer->length = plain.length - ec - 16;
    479 	    message_buffer->value = MALLOC(message_buffer->length);
    480 	    if (message_buffer->value == NULL) {
    481 		FREE(plain.data, plainlen);
    482 		goto no_mem;
    483 	    }
    484 	    (void) memcpy(message_buffer->value, plain.data,
    485 			message_buffer->length);
    486 	    FREE(plain.data, plainlen);
    487 	} else {
    488 	    /* no confidentiality */
    489 	    if (conf_state)
    490 		*conf_state = 0;
    491 	    if (ec + 16 < ec)
    492 		/* overflow check */
    493 		goto defective;
    494 	    if (ec + 16 > bodysize)
    495 		goto defective;
    496 	    /* We have: header | msg | cksum.
    497 	       We need cksum(msg | header).
    498 	       Rotate the first two.  */
    499 	    store_16_be(0, ptr+4);
    500 	    store_16_be(0, ptr+6);
    501 	    plain.length = bodysize-ec;
    502 	    plain.data = (char *)ptr;
    503 	    if (!rotate_left(ptr, bodysize-ec, 16))
    504 		goto no_mem;
    505 	    sum.length = ec;
    506 	    if (sum.length != ctx->cksum_size) {
    507 		*minor_status = 0;
    508 		return GSS_S_BAD_SIG;
    509 	    }
    510 	    sum.contents = ptr+bodysize-ec;
    511 	    sum.checksum_type = ctx->cksumtype;
    512 	    err = krb5_c_verify_checksum(context, key, key_usage,
    513 					 &plain, &sum, &valid);
    514 	    if (err) {
    515 		*minor_status = err;
    516 		return GSS_S_BAD_SIG;
    517 	    }
    518 	    if (!valid) {
    519 		*minor_status = 0;
    520 		return GSS_S_BAD_SIG;
    521 	    }
    522 	    message_buffer->length = plain.length - 16;
    523 	    message_buffer->value = MALLOC(message_buffer->length);
    524 	    if (message_buffer->value == NULL)
    525 		goto no_mem;
    526 	    (void) memcpy(message_buffer->value,
    527 		plain.data, message_buffer->length);
    528 
    529 		/*
    530 		 * Solaris Kerberos: Restore the original token.
    531 		 * This allows the token to be detected as a duplicate if it
    532 		 * is passed in to gss_unwrap() again.
    533 		 */
    534 		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
    535 			goto no_mem;
    536 		store_16_be(ec, ptr+4);
    537 		store_16_be(rrc, ptr+6);
    538 	}
    539 	err = g_order_check(&ctx->seqstate, seqnum);
    540 	*minor_status = 0;
    541 	return err;
    542     } else if (toktype == KG_TOK_MIC_MSG) {
    543 	/* wrap token, no confidentiality */
    544 	if (load_16_be(ptr) != 0x0404)
    545 	    goto defective;
    546     verify_mic_1:
    547 	if (ptr[3] != 0xff)
    548 	    goto defective;
    549 	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
    550 	    goto defective;
    551 	seqnum = load_64_be(ptr+8);
    552 	plain.length = message_buffer->length + 16;
    553 	plain.data = MALLOC(plain.length);
    554 	if (plain.data == NULL)
    555 	    goto no_mem;
    556 	if (message_buffer->length)
    557 	    (void) memcpy(plain.data,
    558 		message_buffer->value, message_buffer->length);
    559 	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
    560 	sum.length = bodysize - 16;
    561 	sum.contents = ptr + 16;
    562 	sum.checksum_type = ctx->cksumtype;
    563 	err = krb5_c_verify_checksum(context, key, key_usage,
    564 				     &plain, &sum, &valid);
    565 	if (err) {
    566 	error:
    567 	    FREE(plain.data, plain.length);
    568 	    *minor_status = err;
    569 	    return GSS_S_BAD_SIG; /* XXX */
    570 	}
    571 	FREE(plain.data, plain.length);
    572 	if (!valid) {
    573 	    *minor_status = 0;
    574 	    return GSS_S_BAD_SIG;
    575 	}
    576 	err = g_order_check(&ctx->seqstate, seqnum);
    577 	*minor_status = 0;
    578 	return err;
    579     } else if (toktype == KG_TOK_DEL_CTX) {
    580 	if (load_16_be(ptr) != 0x0405)
    581 	    goto defective;
    582 	message_buffer = (gss_buffer_t)&empty_message;
    583 	goto verify_mic_1;
    584     } else {
    585 	goto defective;
    586     }
    587 }
    588