From fdceb225f881e2b1337eebcb9a9443fa4a9be3fd Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Wed, 15 Mar 2023 13:56:21 -0400 Subject: Use k5-der.h in GSS library Remove the DER implementations in lib/gssapi and use k5-der.h instead. --- src/lib/gssapi/generic/gssapiP_generic.h | 4 +- src/lib/gssapi/generic/util_token.c | 199 +---- src/lib/gssapi/krb5/accept_sec_context.c | 26 +- src/lib/gssapi/krb5/iakerb.c | 60 +- src/lib/gssapi/krb5/init_sec_context.c | 18 +- src/lib/gssapi/krb5/k5seal.c | 59 +- src/lib/gssapi/krb5/k5sealiov.c | 47 +- src/lib/gssapi/mechglue/g_encapsulate_token.c | 10 +- src/lib/gssapi/mechglue/g_glue.c | 284 +----- src/lib/gssapi/mechglue/g_imp_name.c | 112 +-- src/lib/gssapi/mechglue/mglueP.h | 17 - src/lib/gssapi/spnego/spnego_mech.c | 1168 ++++++------------------- 12 files changed, 474 insertions(+), 1530 deletions(-) diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h index 686a217..5d01fb4 100644 --- a/src/lib/gssapi/generic/gssapiP_generic.h +++ b/src/lib/gssapi/generic/gssapiP_generic.h @@ -153,8 +153,8 @@ int g_make_string_buffer (const char *str, gss_buffer_t buffer); unsigned int g_token_size (const gss_OID_desc * mech, unsigned int body_size); -void g_make_token_header (const gss_OID_desc * mech, unsigned int body_size, - unsigned char **buf, int tok_type); +void g_make_token_header (struct k5buf *buf, const gss_OID_desc *mech, + size_t body_size, int tok_type); /* flags for g_verify_token_header() */ #define G_VFY_TOKEN_HDR_WRAPPER_REQUIRED 0x01 diff --git a/src/lib/gssapi/generic/util_token.c b/src/lib/gssapi/generic/util_token.c index 6e339f4..2369cae 100644 --- a/src/lib/gssapi/generic/util_token.c +++ b/src/lib/gssapi/generic/util_token.c @@ -22,6 +22,7 @@ */ #include "gssapiP_generic.h" +#include "k5-der.h" #ifdef HAVE_MEMORY_H #include #endif @@ -31,131 +32,33 @@ * $Id$ */ -/* XXXX this code currently makes the assumption that a mech oid will - never be longer than 127 bytes. This assumption is not inherent in - the interfaces, so the code can be fixed if the OSI namespace - balloons unexpectedly. */ - -/* - * Each token looks like this: - * 0x60 tag for APPLICATION 0, SEQUENCE - * (constructed, definite-length) - * possible multiple bytes, need to parse/generate - * 0x06 tag for OBJECT IDENTIFIER - * compile-time constant string (assume 1 byte) - * compile-time constant string - * the ANY containing the application token - * bytes 0,1 are the token type - * bytes 2,n are the token data - * - * Note that the token type field is a feature of RFC 1964 mechanisms and - * is not used by other GSSAPI mechanisms. As such, a token type of -1 - * is interpreted to mean that no token type should be expected or - * generated. - * - * For the purposes of this abstraction, the token "header" consists of - * the sequence tag and length octets, the mech OID DER encoding, and the - * first two inner bytes, which indicate the token type. The token - * "body" consists of everything else. - */ -static unsigned int -der_length_size(int length) -{ - if (length < (1<<7)) - return(1); - else if (length < (1<<8)) - return(2); -#if INT_MAX == 0x7fff - else - return(3); -#else - else if (length < (1<<16)) - return(3); - else if (length < (1<<24)) - return(4); - else - return(5); -#endif -} - -static void -der_write_length(unsigned char **buf, int length) -{ - if (length < (1<<7)) { - *(*buf)++ = (unsigned char) length; - } else { - *(*buf)++ = (unsigned char) (der_length_size(length)+127); -#if INT_MAX > 0x7fff - if (length >= (1<<24)) - *(*buf)++ = (unsigned char) (length>>24); - if (length >= (1<<16)) - *(*buf)++ = (unsigned char) ((length>>16)&0xff); -#endif - if (length >= (1<<8)) - *(*buf)++ = (unsigned char) ((length>>8)&0xff); - *(*buf)++ = (unsigned char) (length&0xff); - } -} - -/* returns decoded length, or < 0 on failure. Advances buf and - decrements bufsize */ - -static int -der_read_length(unsigned char **buf, int *bufsize) -{ - unsigned char sf; - int ret; - - if (*bufsize < 1) - return(-1); - sf = *(*buf)++; - (*bufsize)--; - if (sf & 0x80) { - if ((sf &= 0x7f) > ((*bufsize)-1)) - return(-1); - if (sf > sizeof(int)) - return (-1); - ret = 0; - for (; sf; sf--) { - ret = (ret<<8) + (*(*buf)++); - (*bufsize)--; - } - } else { - ret = sf; - } - - return(ret); -} - -/* returns the length of a token, given the mech oid and the body size */ - +/* Return the length of an RFC 4121 token with RFC 2743 token framing, given + * the mech oid and the body size (without the two-byte RFC 4121 token ID). */ unsigned int g_token_size(const gss_OID_desc * mech, unsigned int body_size) { - /* set body_size to sequence contents size */ - body_size += 4 + (unsigned int)mech->length; /* NEED overflow check */ - return(1 + der_length_size(body_size) + body_size); -} + size_t mech_der_len = k5_der_value_len(mech->length); -/* fills in a buffer with the token header. The buffer is assumed to - be the right size. buf is advanced past the token header */ + return k5_der_value_len(mech_der_len + 2 + body_size); +} +/* + * Add RFC 2743 generic token framing to buf with room left for body_size bytes + * in the sequence to be added by the caller. If tok_type is not -1, add it as + * a two-byte RFC 4121 token identifier after the framing and include room for + * it in the sequence. + */ void -g_make_token_header( - const gss_OID_desc * mech, - unsigned int body_size, - unsigned char **buf, - int tok_type) +g_make_token_header(struct k5buf *buf, const gss_OID_desc *mech, + size_t body_size, int tok_type) { - *(*buf)++ = 0x60; - der_write_length(buf, ((tok_type == -1) ? 2 : 4) + mech->length + body_size); - *(*buf)++ = 0x06; - *(*buf)++ = (unsigned char) mech->length; - TWRITE_STR(*buf, mech->elements, mech->length); - if (tok_type != -1) { - *(*buf)++ = (unsigned char) ((tok_type>>8)&0xff); - *(*buf)++ = (unsigned char) (tok_type&0xff); - } + size_t tok_len = (tok_type == -1) ? 0 : 2; + size_t seq_len = k5_der_value_len(mech->length) + body_size + tok_len; + + k5_der_add_taglen(buf, 0x60, seq_len); + k5_der_add_value(buf, 0x06, mech->elements, mech->length); + if (tok_type != -1) + k5_buf_add_uint16_be(buf, tok_type); } /* @@ -176,54 +79,30 @@ g_verify_token_header( unsigned int toksize_in, int flags) { - unsigned char *buf = *buf_in; - int seqsize; + struct k5input in, mech_der; gss_OID_desc toid; - int toksize = toksize_in; - if ((toksize-=1) < 0) - return(G_BAD_TOK_HEADER); - if (*buf++ != 0x60) { - if (flags & G_VFY_TOKEN_HDR_WRAPPER_REQUIRED) - return(G_BAD_TOK_HEADER); - buf--; - toksize++; - goto skip_wrapper; + k5_input_init(&in, *buf_in, toksize_in); + + if (k5_der_get_value(&in, 0x60, &in)) { + if (in.ptr + in.len != *buf_in + toksize_in) + return G_BAD_TOK_HEADER; + if (!k5_der_get_value(&in, 0x06, &mech_der)) + return G_BAD_TOK_HEADER; + toid.elements = (uint8_t *)mech_der.ptr; + toid.length = mech_der.len; + if (!g_OID_equal(&toid, mech)) + return G_WRONG_MECH; + } else if (flags & G_VFY_TOKEN_HDR_WRAPPER_REQUIRED) { + return G_BAD_TOK_HEADER; } - if ((seqsize = der_read_length(&buf, &toksize)) < 0) - return(G_BAD_TOK_HEADER); - - if (seqsize != toksize) - return(G_BAD_TOK_HEADER); - - if ((toksize-=1) < 0) - return(G_BAD_TOK_HEADER); - if (*buf++ != 0x06) - return(G_BAD_TOK_HEADER); - - if ((toksize-=1) < 0) - return(G_BAD_TOK_HEADER); - toid.length = *buf++; - - if ((toksize-=toid.length) < 0) - return(G_BAD_TOK_HEADER); - toid.elements = buf; - buf+=toid.length; - - if (! g_OID_equal(&toid, mech)) - return G_WRONG_MECH; -skip_wrapper: if (tok_type != -1) { - if ((toksize-=2) < 0) - return(G_BAD_TOK_HEADER); - - if ((*buf++ != ((tok_type>>8)&0xff)) || - (*buf++ != (tok_type&0xff))) - return(G_WRONG_TOKID); + if (k5_input_get_uint16_be(&in) != tok_type) + return in.status ? G_BAD_TOK_HEADER : G_WRONG_TOKID; } - *buf_in = buf; - *body_size = toksize; + *buf_in = (uint8_t *)in.ptr; + *body_size = in.len; return 0; } diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index 44c056a..dd4caf3 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -684,6 +684,7 @@ kg_accept_krb5(minor_status, context_handle, krb5_enctype negotiated_etype; krb5_authdata_context ad_context = NULL; krb5_ap_req *request = NULL; + struct k5buf buf; code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); if (code) { @@ -1009,7 +1010,6 @@ kg_accept_krb5(minor_status, context_handle, /* generate an AP_REP if necessary */ if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { - unsigned char * ptr3; krb5_int32 seq_temp; int cfx_generate_subkey; @@ -1114,18 +1114,16 @@ kg_accept_krb5(minor_status, context_handle, ctx->established = 1; token.length = g_token_size(mech_used, ap_rep.length); - - if ((token.value = (unsigned char *) gssalloc_malloc(token.length)) - == NULL) { + token.value = gssalloc_malloc(token.length); + if (token.value == NULL) { major_status = GSS_S_FAILURE; code = ENOMEM; goto fail; } - ptr3 = token.value; - g_make_token_header(mech_used, ap_rep.length, - &ptr3, KG_TOK_CTX_AP_REP); - - TWRITE_STR(ptr3, ap_rep.data, ap_rep.length); + k5_buf_init_fixed(&buf, token.value, token.length); + g_make_token_header(&buf, mech_used, ap_rep.length, KG_TOK_CTX_AP_REP); + k5_buf_add_len(&buf, ap_rep.data, ap_rep.length); + assert(buf.len == token.length); ctx->established = 1; @@ -1220,7 +1218,6 @@ fail: (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) || major_status == GSS_S_CONTINUE_NEEDED)) { unsigned int tmsglen; - int toktype; /* * The client is expecting a response, so we can send an @@ -1242,17 +1239,16 @@ fail: goto done; tmsglen = scratch.length; - toktype = KG_TOK_CTX_ERROR; token.length = g_token_size(mech_used, tmsglen); token.value = gssalloc_malloc(token.length); if (!token.value) goto done; + k5_buf_init_fixed(&buf, token.value, token.length); + g_make_token_header(&buf, mech_used, tmsglen, KG_TOK_CTX_ERROR); + k5_buf_add_len(&buf, scratch.data, scratch.length); + assert(buf.len == token.length); - ptr = token.value; - g_make_token_header(mech_used, tmsglen, &ptr, toktype); - - TWRITE_STR(ptr, scratch.data, scratch.length); krb5_free_data_contents(context, &scratch); *output_token = token; diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c index b0d0ede..a0d298c 100644 --- a/src/lib/gssapi/krb5/iakerb.c +++ b/src/lib/gssapi/krb5/iakerb.c @@ -23,14 +23,13 @@ * or implied warranty. */ #include "k5-int.h" +#include "k5-der.h" #include "gssapiP_krb5.h" /* * IAKERB implementation */ -extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*); - enum iakerb_state { IAKERB_AS_REQ, /* acquiring ticket with initial creds */ IAKERB_TGS_REQ, /* acquiring ticket with TGT */ @@ -172,11 +171,11 @@ iakerb_parse_token(iakerb_ctx_id_t ctx, { krb5_error_code code; krb5_iakerb_header *iah = NULL; - unsigned int bodysize, lenlen; - int length; - unsigned char *ptr; + unsigned int bodysize; + uint8_t *body; int flags = 0; krb5_data data; + struct k5input in, seq; if (token == GSS_C_NO_BUFFER || token->length == 0) { code = KRB5_BAD_MSIZE; @@ -186,32 +185,20 @@ iakerb_parse_token(iakerb_ctx_id_t ctx, if (initialContextToken) flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED; - ptr = token->value; - - code = g_verify_token_header(gss_mech_iakerb, - &bodysize, &ptr, - IAKERB_TOK_PROXY, - token->length, flags); + body = token->value; + code = g_verify_token_header(gss_mech_iakerb, &bodysize, &body, + IAKERB_TOK_PROXY, token->length, flags); if (code != 0) goto cleanup; - data.data = (char *)ptr; - - if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) { + /* Find the end of the DER sequence tag and decode it (with the tag) as the + * IAKERB jeader. */ + k5_input_init(&in, body, bodysize); + if (!k5_der_get_value(&in, 0x30, &seq)) { code = ASN1_BAD_ID; goto cleanup; } - - length = gssint_get_der_length(&ptr, bodysize, &lenlen); - if (length < 0 || bodysize - lenlen < (unsigned int)length) { - code = KRB5_BAD_MSIZE; - goto cleanup; - } - data.length = 1 /* SEQUENCE */ + lenlen + length; - - ptr += length; - bodysize -= (lenlen + length); - + data = make_data(body, seq.ptr + seq.len - body); code = decode_krb5_iakerb_header(&data, &iah); if (code != 0) goto cleanup; @@ -226,9 +213,8 @@ iakerb_parse_token(iakerb_ctx_id_t ctx, iah->cookie = NULL; } - request->data = (char *)ptr; - request->length = bodysize; - + /* The remainder of the token body is the request. */ + *request = make_data((uint8_t *)in.ptr, in.len); assert(request->data + request->length == (char *)token->value + token->length); @@ -254,7 +240,7 @@ iakerb_make_token(iakerb_ctx_id_t ctx, krb5_data *data = NULL; char *p; unsigned int tokenSize; - unsigned char *q; + struct k5buf buf; token->value = NULL; token->length = 0; @@ -288,24 +274,22 @@ iakerb_make_token(iakerb_ctx_id_t ctx, else tokenSize = 2 + data->length; - token->value = q = gssalloc_malloc(tokenSize); - if (q == NULL) { + token->value = gssalloc_malloc(tokenSize); + if (token->value == NULL) { code = ENOMEM; goto cleanup; } token->length = tokenSize; + k5_buf_init_fixed(&buf, token->value, token->length); if (initialContextToken) { - g_make_token_header(gss_mech_iakerb, data->length, &q, + g_make_token_header(&buf, gss_mech_iakerb, data->length, IAKERB_TOK_PROXY); } else { - store_16_be(IAKERB_TOK_PROXY, q); - q += 2; + k5_buf_add_uint16_be(&buf, IAKERB_TOK_PROXY); } - memcpy(q, data->data, data->length); - q += data->length; - - assert(q == (unsigned char *)token->value + token->length); + k5_buf_add_len(&buf, data->data, data->length); + assert(buf.len == token->length); cleanup: krb5_free_data(ctx->k5c, data); diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index f0f094c..83fd514 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -393,9 +393,9 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context, struct gss_checksum_data cksum_struct; krb5_checksum md5; krb5_data ap_req; - unsigned char *ptr; unsigned char *t; unsigned int tlen; + struct k5buf buf; k5_mutex_assert_locked(&cred->lock); ap_req.data = 0; @@ -447,19 +447,15 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context, } else { /* allocate space for the token */ tlen = g_token_size((gss_OID) mech_type, ap_req.length); - - if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) { + t = gssalloc_malloc(tlen); + if (t == NULL) { code = ENOMEM; goto cleanup; } - - /* fill in the buffer */ - ptr = t; - - g_make_token_header(mech_type, ap_req.length, - &ptr, KG_TOK_CTX_AP_REQ); - - TWRITE_STR(ptr, ap_req.data, ap_req.length); + k5_buf_init_fixed(&buf, t, tlen); + g_make_token_header(&buf, mech_type, ap_req.length, KG_TOK_CTX_AP_REQ); + k5_buf_add_len(&buf, ap_req.data, ap_req.length); + assert(buf.len == tlen); /* pass it back */ diff --git a/src/lib/gssapi/krb5/k5seal.c b/src/lib/gssapi/krb5/k5seal.c index d1cdce4..99275be 100644 --- a/src/lib/gssapi/krb5/k5seal.c +++ b/src/lib/gssapi/krb5/k5seal.c @@ -78,11 +78,11 @@ make_seal_token_v1 (krb5_context context, * tlen is the length of the token * including header. */ unsigned int conflen=0, tmsglen, tlen, msglen; - unsigned char *t, *ptr; + unsigned char *t, *metadata, *checksum, *payload; unsigned char *plain; unsigned char pad; krb5_keyusage sign_usage = KG_USAGE_SIGN; - + struct k5buf buf; assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG)); /* create the token buffer */ @@ -108,31 +108,37 @@ make_seal_token_v1 (krb5_context context, msglen = text->length; pad = 0; } - tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen); - if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) + tlen = g_token_size(oid, 14 + cksum_size + tmsglen); + t = gssalloc_malloc(tlen); + if (t == NULL) return(ENOMEM); + k5_buf_init_fixed(&buf, t, tlen); /*** fill in the token */ - ptr = t; - g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype); + g_make_token_header(&buf, oid, 14 + cksum_size + tmsglen, toktype); + metadata = k5_buf_get_space(&buf, 14); + checksum = k5_buf_get_space(&buf, cksum_size); + payload = k5_buf_get_space(&buf, tmsglen); + assert(metadata != NULL && checksum != NULL && payload != NULL); + assert(buf.len == tlen); /* 0..1 SIGN_ALG */ - store_16_le(signalg, &ptr[0]); + store_16_le(signalg, &metadata[0]); /* 2..3 SEAL_ALG or Filler */ if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) { - store_16_le(sealalg, &ptr[2]); + store_16_le(sealalg, &metadata[2]); } else { /* No seal */ - ptr[2] = 0xff; - ptr[3] = 0xff; + metadata[2] = 0xFF; + metadata[3] = 0xFF; } /* 4..5 Filler */ - ptr[4] = 0xff; - ptr[5] = 0xff; + metadata[4] = 0xFF; + metadata[5] = 0xFF; /* pad the plaintext, encrypt if needed, and stick it in the token */ @@ -183,8 +189,9 @@ make_seal_token_v1 (krb5_context context, gssalloc_free(t); return(ENOMEM); } - (void) memcpy(data_ptr, ptr-2, 8); - (void) memcpy(data_ptr+8, plain, msglen); + /* Checksum over the token ID, metadata bytes, and plaintext. */ + memcpy(data_ptr, metadata - 2, 8); + memcpy(data_ptr + 8, plain, msglen); plaind.length = 8 + msglen; plaind.data = data_ptr; code = krb5_k_make_checksum(context, md5cksum.checksum_type, seq, @@ -204,10 +211,10 @@ make_seal_token_v1 (krb5_context context, */ if (md5cksum.length != cksum_size) abort (); - memcpy (ptr+14, md5cksum.contents, md5cksum.length); + memcpy(checksum, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: - memcpy (ptr+14, md5cksum.contents, cksum_size); + memcpy(checksum, md5cksum.contents, cksum_size); break; } @@ -215,8 +222,9 @@ make_seal_token_v1 (krb5_context context, /* create the seq_num */ - if ((code = kg_make_seq_num(context, seq, direction?0:0xff, - (krb5_ui_4)*seqnum, ptr+14, ptr+6))) { + code = kg_make_seq_num(context, seq, direction?0:0xff, + (krb5_ui_4)*seqnum, checksum, metadata + 6); + if (code) { xfree (plain); gssalloc_free(t); return(code); @@ -240,10 +248,8 @@ make_seal_token_v1 (krb5_context context, 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); + code = kg_arcfour_docrypt(enc_key, 0, bigend_seqnum, 4, plain, + tmsglen, payload); krb5_free_keyblock (context, enc_key); if (code) { @@ -254,10 +260,9 @@ make_seal_token_v1 (krb5_context context, } break; default: - if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, - (krb5_pointer) plain, - (krb5_pointer) (ptr+cksum_size+14), - tmsglen))) { + code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, plain, + payload, tmsglen); + if (code) { xfree(plain); gssalloc_free(t); return(code); @@ -265,7 +270,7 @@ make_seal_token_v1 (krb5_context context, } }else { if (tmsglen) - memcpy(ptr+14+cksum_size, plain, tmsglen); + memcpy(payload, plain, tmsglen); } xfree(plain); diff --git a/src/lib/gssapi/krb5/k5sealiov.c b/src/lib/gssapi/krb5/k5sealiov.c index 9bb2ee1..7bf7609 100644 --- a/src/lib/gssapi/krb5/k5sealiov.c +++ b/src/lib/gssapi/krb5/k5sealiov.c @@ -44,9 +44,10 @@ make_seal_token_v1_iov(krb5_context context, 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; + size_t tmsglen = 0, cnflen = 0, tlen; + uint8_t *metadata, *checksum, *confounder; krb5_keyusage sign_usage = KG_USAGE_SIGN; + struct k5buf buf; md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; @@ -65,17 +66,15 @@ make_seal_token_v1_iov(krb5_context context, trailer->buffer.length = 0; /* Determine confounder length */ - if (toktype == KG_TOK_WRAP_MSG || conf_req_flag) - k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype); - - /* 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; + cnflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); - conf_data_length = k5_headerlen + data_length - assoc_data_length; + conf_data_length = cnflen + data_length - assoc_data_length; if (k5_padlen == 1) gss_padlen = 1; /* one byte to indicate one byte of padding */ @@ -103,7 +102,7 @@ make_seal_token_v1_iov(krb5_context context, } if (ctx->gss_flags & GSS_C_DCE_STYLE) - tmsglen = k5_headerlen; /* confounder length */ + tmsglen = cnflen; /* confounder length */ else tmsglen = conf_data_length + padding->buffer.length; } @@ -111,7 +110,7 @@ make_seal_token_v1_iov(krb5_context context, /* Determine token size */ tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen); - k5_headerlen += tlen - tmsglen; + k5_headerlen = cnflen + tlen - tmsglen; if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(header, k5_headerlen); @@ -122,24 +121,28 @@ make_seal_token_v1_iov(krb5_context context, 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); + k5_buf_init_fixed(&buf, header->buffer.value, k5_headerlen); + g_make_token_header(&buf, ctx->mech_used, 14 + ctx->cksum_size + tmsglen, + toktype); + metadata = k5_buf_get_space(&buf, 14); + checksum = k5_buf_get_space(&buf, ctx->cksum_size); + assert(metadata != NULL && checksum != NULL); /* 0..1 SIGN_ALG */ - store_16_le(ctx->signalg, &ptr[0]); + store_16_le(ctx->signalg, &metadata[0]); /* 2..3 SEAL_ALG or Filler */ if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { - store_16_le(ctx->sealalg, &ptr[2]); + store_16_le(ctx->sealalg, &metadata[2]); } else { /* No seal */ - ptr[2] = 0xFF; - ptr[3] = 0xFF; + metadata[2] = 0xFF; + metadata[3] = 0xFF; } /* 4..5 Filler */ - ptr[4] = 0xFF; - ptr[5] = 0xFF; + metadata[4] = 0xFF; + metadata[5] = 0xFF; /* pad the plaintext, encrypt if needed, and stick it in the token */ @@ -163,8 +166,10 @@ make_seal_token_v1_iov(krb5_context context, md5cksum.length = k5_trailerlen; if (k5_headerlen != 0 && toktype == KG_TOK_WRAP_MSG) { + confounder = k5_buf_get_space(&buf, cnflen); + assert(confounder != NULL); code = kg_make_confounder(context, ctx->enc->keyblock.enctype, - ptr + 14 + ctx->cksum_size); + confounder); if (code != 0) goto cleanup; } @@ -180,16 +185,16 @@ make_seal_token_v1_iov(krb5_context context, switch (ctx->signalg) { case SGN_ALG_HMAC_SHA1_DES3_KD: assert(md5cksum.length == ctx->cksum_size); - memcpy(ptr + 14, md5cksum.contents, md5cksum.length); + memcpy(checksum, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: - memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size); + memcpy(checksum, 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); + (OM_uint32)ctx->seq_send, checksum, metadata + 6); if (code != 0) goto cleanup; diff --git a/src/lib/gssapi/mechglue/g_encapsulate_token.c b/src/lib/gssapi/mechglue/g_encapsulate_token.c index 850e3ee..1ccd3cd 100644 --- a/src/lib/gssapi/mechglue/g_encapsulate_token.c +++ b/src/lib/gssapi/mechglue/g_encapsulate_token.c @@ -38,7 +38,7 @@ gss_encapsulate_token(gss_const_buffer_t input_token, gss_buffer_t output_token) { unsigned int tokenSize; - unsigned char *buf; + struct k5buf buf; if (input_token == GSS_C_NO_BUFFER || token_oid == GSS_C_NO_OID) return GSS_S_CALL_INACCESSIBLE_READ; @@ -55,10 +55,10 @@ gss_encapsulate_token(gss_const_buffer_t input_token, if (output_token->value == NULL) return GSS_S_FAILURE; - buf = output_token->value; - - g_make_token_header(token_oid, input_token->length, &buf, -1); - memcpy(buf, input_token->value, input_token->length); + k5_buf_init_fixed(&buf, output_token->value, tokenSize); + g_make_token_header(&buf, token_oid, input_token->length, -1); + k5_buf_add_len(&buf, input_token->value, input_token->length); + assert(buf.len == tokenSize); output_token->length = tokenSize; return GSS_S_COMPLETE; diff --git a/src/lib/gssapi/mechglue/g_glue.c b/src/lib/gssapi/mechglue/g_glue.c index dfef49e..176fbe6 100644 --- a/src/lib/gssapi/mechglue/g_glue.c +++ b/src/lib/gssapi/mechglue/g_glue.c @@ -23,6 +23,7 @@ */ #include "mglueP.h" +#include "k5-der.h" #include #ifdef HAVE_STDLIB_H #include @@ -38,219 +39,24 @@ extern gss_mechanism *gssint_mechs_array; * This file contains the support routines for the glue layer. */ -/* - * get_der_length: Givin a pointer to a buffer that contains a DER encoded - * length, decode the length updating the buffer to point to the character - * after the DER encoding. The parameter bytes will point to the number of - * bytes that made up the DER encoding of the length originally pointed to - * by the buffer. Note we return -1 on error. - */ -int -gssint_get_der_length(unsigned char **buf, unsigned int buf_len, unsigned int *bytes) +/* Retrieve the mechanism OID from an RFC 2743 InitialContextToken. Place + * the result into *oid_out, aliasing memory from token. */ +OM_uint32 gssint_get_mech_type_oid(gss_OID oid_out, gss_buffer_t token) { - /* p points to the beginning of the buffer */ - unsigned char *p = *buf; - int length, new_length; - unsigned int octets; - - if (buf_len < 1) - return (-1); - - /* We should have at least one byte */ - *bytes = 1; - - /* - * If the High order bit is not set then the length is just the value - * of *p. - */ - if (*p < 128) { - *buf = p+1; /* Advance the buffer */ - return (*p); /* return the length */ - } - - /* - * if the High order bit is set, then the low order bits represent - * the number of bytes that contain the DER encoding of the length. - */ + struct k5input in; - octets = *p++ & 0x7f; - *bytes += octets; - - /* See if the supplied buffer contains enough bytes for the length. */ - if (octets > buf_len - 1) - return (-1); - - /* - * Calculate a multibyte length. The length is encoded as an - * unsigned integer base 256. - */ - for (length = 0; octets; octets--) { - new_length = (length << 8) + *p++; - if (new_length < length) /* overflow */ - return (-1); - length = new_length; - } - - *buf = p; /* Advance the buffer */ - - return (length); -} - -/* - * der_length_size: Return the number of bytes to encode a given length. - */ -unsigned int -gssint_der_length_size(unsigned int len) -{ - int i; - - if (len < 128) - return (1); - - for (i = 0; len; i++) { - len >>= 8; - } - - return (i+1); -} - -/* - * put_der_length: Encode the supplied length into the buffer pointed to - * by buf. max_length represents the maximum length of the buffer pointed - * to by buff. We will advance buf to point to the character after the newly - * DER encoded length. We return 0 on success or -l it the length cannot - * be encoded in max_len characters. - */ -int -gssint_put_der_length(unsigned int length, unsigned char **buf, unsigned int max_len) -{ - unsigned char *s, *p; - unsigned int buf_len = 0; - int i, first; - - /* Oops */ - if (buf == 0 || max_len < 1) - return (-1); - - s = *buf; - - /* Single byte is the length */ - if (length < 128) { - *s++ = length; - *buf = s; - return (0); - } - - /* First byte contains the number of octets */ - p = s + 1; - - /* Running total of the DER encoding length */ - buf_len = 0; - - /* - * Encode MSB first. We do the encoding by setting a shift - * factor to MSO_BIT (24 for 32 bit words) and then shifting the length - * by the factor. We then encode the resulting low order byte. - * We subtract 8 from the shift factor and repeat to ecnode the next - * byte. We stop when the shift factor is zero or we've run out of - * buffer to encode into. - */ - first = 0; - for (i = MSO_BIT; i >= 0 && buf_len <= max_len; i -= 8) { - unsigned int v; - v = (length >> i) & 0xff; - if ((v) || first) { - buf_len += 1; - *p++ = v; - first = 1; - } - } - if (i >= 0) /* buffer overflow */ - return (-1); - - /* - * We go back now and set the first byte to be the length with - * the high order bit set. - */ - *s = buf_len | 0x80; - *buf = p; - - return (0); -} - - -/* - * glue routine for get_mech_type - * - */ - -OM_uint32 gssint_get_mech_type_oid(OID, token) - gss_OID OID; - gss_buffer_t token; -{ - unsigned char * buffer_ptr; - size_t buflen, lenbytes, length, oidlen; - - /* - * This routine reads the prefix of "token" in order to determine - * its mechanism type. It assumes the encoding suggested in - * Appendix B of RFC 1508. This format starts out as follows : - * - * tag for APPLICATION 0, Sequence[constructed, definite length] - * length of remainder of token - * tag of OBJECT IDENTIFIER - * length of mechanism OID - * encoding of mechanism OID - * - * - * Numerically, this looks like : - * - * 0x60 - * - could be multiple bytes - * 0x06 - * - assume only one byte, hence OID length < 127 - * - * - * The routine fills in the OID value and returns an error as necessary. - */ - - if (OID == NULL) - return (GSS_S_CALL_INACCESSIBLE_WRITE); - - if ((token == NULL) || (token->value == NULL)) - return (GSS_S_DEFECTIVE_TOKEN); - - /* Skip past the APP/Sequnce byte and the token length */ - - buffer_ptr = (unsigned char *) token->value; - buflen = token->length; - - if (buflen < 2 || *buffer_ptr++ != 0x60) + if (oid_out == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + if (token == NULL || token->value == NULL) return (GSS_S_DEFECTIVE_TOKEN); - length = *buffer_ptr++; - buflen -= 2; - - /* check if token length is null */ - if (length == 0) - return (GSS_S_DEFECTIVE_TOKEN); - - if (length & 0x80) { - lenbytes = length & 0x7f; - if (lenbytes > 4 || lenbytes > buflen) - return (GSS_S_DEFECTIVE_TOKEN); - buffer_ptr += lenbytes; - buflen -= lenbytes; - } - if (buflen < 2 || *buffer_ptr++ != 0x06) + k5_input_init(&in, token->value, token->length); + if (!k5_der_get_value(&in, 0x60, &in)) return (GSS_S_DEFECTIVE_TOKEN); - oidlen = *buffer_ptr++; - buflen -= 2; - if (oidlen > 0x7f || oidlen > buflen) + if (!k5_der_get_value(&in, 0x06, &in)) return (GSS_S_DEFECTIVE_TOKEN); - - OID->length = oidlen; - OID->elements = (void *) buffer_ptr; + oid_out->length = in.len; + oid_out->elements = (uint8_t *)in.ptr; return (GSS_S_COMPLETE); } @@ -425,12 +231,8 @@ OM_uint32 gssint_export_internal_name(minor_status, mech_type, gss_mechanism mech; gss_buffer_desc dispName; gss_OID nameOid; - unsigned char *buf = NULL; - const unsigned char tokId[] = "\x04\x01"; - const unsigned int tokIdLen = 2; - const int mechOidLenLen = 2, mechOidTagLen = 1, nameLenLen = 4; - int mechOidDERLen = 0; - int mechOidLen = 0; + int mech_der_len = 0; + struct k5buf buf; mech = gssint_get_mechanism(mech_type); if (!mech) @@ -481,52 +283,24 @@ OM_uint32 gssint_export_internal_name(minor_status, mech_type, return (status); } - /* determine the size of the buffer needed */ - mechOidDERLen = gssint_der_length_size(mech_type->length); - name_buf->length = tokIdLen + mechOidLenLen + - mechOidTagLen + mechOidDERLen + - mech_type->length + - nameLenLen + dispName.length; - if ((name_buf->value = (void*)gssalloc_malloc(name_buf->length)) == - (void*)NULL) { + /* Allocate space and prepare a buffer. */ + mech_der_len = k5_der_value_len(mech_type->length); + name_buf->length = 2 + 2 + mech_der_len + 4 + dispName.length; + name_buf->value = gssalloc_malloc(name_buf->length); + if (name_buf->value == NULL) { name_buf->length = 0; (void) gss_release_buffer(&status, &dispName); return (GSS_S_FAILURE); } - - /* now create the name ..... */ - buf = (unsigned char *)name_buf->value; - (void) memset(name_buf->value, 0, name_buf->length); - (void) memcpy(buf, tokId, tokIdLen); - buf += tokIdLen; - - /* spec allows only 2 bytes for the mech oid length */ - mechOidLen = mechOidDERLen + mechOidTagLen + mech_type->length; - store_16_be(mechOidLen, buf); - buf += 2; - - /* - * DER Encoding of mech OID contains OID Tag (0x06), length and - * mech OID value - */ - *buf++ = 0x06; - if (gssint_put_der_length(mech_type->length, &buf, - (name_buf->length - tokIdLen -2)) != 0) { - name_buf->length = 0; - free(name_buf->value); - (void) gss_release_buffer(&status, &dispName); - return (GSS_S_FAILURE); - } - - (void) memcpy(buf, mech_type->elements, mech_type->length); - buf += mech_type->length; - - /* spec designates the next 4 bytes for the name length */ - store_32_be(dispName.length, buf); - buf += 4; - - /* for the final ingredient - add the name from gss_display_name */ - (void) memcpy(buf, dispName.value, dispName.length); + k5_buf_init_fixed(&buf, name_buf->value, name_buf->length); + + /* Assemble the name. */ + k5_buf_add_len(&buf, "\x04\x01", 2); + k5_buf_add_uint16_be(&buf, mech_der_len); + k5_der_add_value(&buf, 0x06, mech_type->elements, mech_type->length); + k5_buf_add_uint32_be(&buf, dispName.length); + k5_buf_add_len(&buf, dispName.value, dispName.length); + assert(buf.len == name_buf->length); /* release the buffer obtained from gss_display_name */ (void) gss_release_buffer(minor_status, &dispName); diff --git a/src/lib/gssapi/mechglue/g_imp_name.c b/src/lib/gssapi/mechglue/g_imp_name.c index c3e809c..a805078 100644 --- a/src/lib/gssapi/mechglue/g_imp_name.c +++ b/src/lib/gssapi/mechglue/g_imp_name.c @@ -28,6 +28,7 @@ */ #include "mglueP.h" +#include "k5-der.h" #include #ifdef HAVE_STDLIB_H #include @@ -181,13 +182,6 @@ allocation_failure: return (major_status); } -/* - * GSS export name constants - */ -static const unsigned int expNameTokIdLen = 2; -static const unsigned int mechOidLenLen = 2; -static const unsigned int nameTypeLenLen = 2; - static OM_uint32 importExportName(minor, unionName, inputNameType) OM_uint32 *minor; @@ -196,59 +190,31 @@ importExportName(minor, unionName, inputNameType) { gss_OID_desc mechOid; gss_buffer_desc expName; - unsigned char *buf; gss_mechanism mech; - OM_uint32 major, mechOidLen, nameLen, curLength; - unsigned int bytes; + OM_uint32 major, mechOidLen, nameLen; + uint8_t b2; + const uint8_t *name; + struct k5input in, oid, old_format; expName.value = unionName->external_name->value; expName.length = unionName->external_name->length; + k5_input_init(&in, expName.value, expName.length); - curLength = expNameTokIdLen + mechOidLenLen; - if (expName.length < curLength) + if (k5_input_get_byte(&in) != 0x04) return (GSS_S_DEFECTIVE_TOKEN); - - buf = (unsigned char *)expName.value; - if (buf[0] != 0x04) - return (GSS_S_DEFECTIVE_TOKEN); - if (buf[1] != 0x01 && buf[1] != 0x02) /* allow composite names */ + b2 = k5_input_get_byte(&in); + if (b2 != 0x01 && b2 != 0x02) /* allow composite names */ return (GSS_S_DEFECTIVE_TOKEN); - buf += expNameTokIdLen; + mechOidLen = k5_input_get_uint16_be(&in); - /* extract the mechanism oid length */ - mechOidLen = (*buf++ << 8); - mechOidLen |= (*buf++); - curLength += mechOidLen; - if (expName.length < curLength) + if (!k5_der_get_value(&in, 0x06, &oid)) return (GSS_S_DEFECTIVE_TOKEN); - /* - * The mechOid itself is encoded in DER format, OID Tag (0x06) - * length and the value of mech_OID - */ - if (*buf++ != 0x06) - return (GSS_S_DEFECTIVE_TOKEN); - - /* - * mechoid Length is encoded twice; once in 2 bytes as - * explained in RFC2743 (under mechanism independent exported - * name object format) and once using DER encoding - * - * We verify both lengths. - */ - - mechOid.length = gssint_get_der_length(&buf, - (expName.length - curLength), &bytes); - mechOid.elements = (void *)buf; - - /* - * 'bytes' is the length of the DER length, '1' is for the DER - * tag for OID - */ - if ((bytes + mechOid.length + 1) != mechOidLen) + /* Verify that mechOidLen is consistent with the DER OID length. */ + if (mechOidLen != k5_der_value_len(oid.len)) return (GSS_S_DEFECTIVE_TOKEN); - - buf += mechOid.length; + mechOid.length = oid.len; + mechOid.elements = (uint8_t *)oid.ptr; if ((mech = gssint_get_mechanism(&mechOid)) == NULL) return (GSS_S_BAD_MECH); @@ -297,21 +263,11 @@ importExportName(minor, unionName, inputNameType) * that included a null terminator which was counted in the * display name gss_buffer_desc. */ - curLength += 4; /* 4 bytes for name len */ - if (expName.length < curLength) - return (GSS_S_DEFECTIVE_TOKEN); /* next 4 bytes in the name are the name length */ - nameLen = load_32_be(buf); - buf += 4; - - /* - * we use < here because bad code in rpcsec_gss rounds up exported - * name token lengths and pads with nulls, otherwise != would be - * appropriate - */ - curLength += nameLen; /* this is the total length */ - if (expName.length < curLength) + nameLen = k5_input_get_uint32_be(&in); + name = k5_input_get_bytes(&in, nameLen); + if (name == NULL) return (GSS_S_DEFECTIVE_TOKEN); /* @@ -324,29 +280,19 @@ importExportName(minor, unionName, inputNameType) * and length) there's the name itself, though null-terminated; * this null terminator should also not be there, but it is. */ - if (nameLen > 0 && *buf == '\0') { + if (nameLen > 0 && *name == '\0') { OM_uint32 nameTypeLen; - /* next two bytes are the name oid */ - if (nameLen < nameTypeLenLen) - return (GSS_S_DEFECTIVE_TOKEN); - - nameLen -= nameTypeLenLen; - nameTypeLen = (*buf++) << 8; - nameTypeLen |= (*buf++); - - if (nameLen < nameTypeLen) + /* Skip the name type. */ + k5_input_init(&old_format, name, nameLen); + nameTypeLen = k5_input_get_uint16_be(&old_format); + if (k5_input_get_bytes(&old_format, nameTypeLen) == NULL) return (GSS_S_DEFECTIVE_TOKEN); - - buf += nameTypeLen; - nameLen -= nameTypeLen; - - /* - * adjust for expected null terminator that should - * really not be there - */ - if (nameLen > 0 && *(buf + nameLen - 1) == '\0') - nameLen--; + /* Remove a null terminator if one is present. */ + if (old_format.len > 0 && old_format.ptr[old_format.len - 1] == 0) + old_format.len--; + name = old_format.ptr; + nameLen = old_format.len; } /* @@ -365,7 +311,7 @@ importExportName(minor, unionName, inputNameType) * IDN is thrown in with Kerberos V extensions). */ expName.length = nameLen; - expName.value = nameLen ? (void *)buf : NULL; + expName.value = nameLen ? (uint8_t *)name : NULL; if (mech->gssspi_import_name_by_mech) { major = mech->gssspi_import_name_by_mech(minor, &mechOid, &expName, GSS_C_NULL_OID, diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index dbd244b..7f836fb 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -819,23 +819,6 @@ OM_uint32 gss_add_mech_name_type * Sun extensions to GSS-API v2 */ -int -gssint_get_der_length( - unsigned char **, /* buf */ - unsigned int, /* buf_len */ - unsigned int * /* bytes */ -); - -unsigned int -gssint_der_length_size(unsigned int /* len */); - -int -gssint_put_der_length( - unsigned int, /* length */ - unsigned char **, /* buf */ - unsigned int /* max_len */ -); - OM_uint32 gssint_wrap_aead (gss_mechanism, /* mech */ OM_uint32 *, /* minor_status */ diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index 654964c..bdd7586 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -60,40 +60,24 @@ /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */ #include +#include #include #include #include "gssapiP_spnego.h" #include -#undef g_token_size -#undef g_verify_token_header -#undef g_make_token_header - #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED) typedef const gss_OID_desc *gss_OID_const; -/* der routines defined in libgss */ -extern unsigned int gssint_der_length_size(unsigned int); -extern int gssint_get_der_length(unsigned char **, unsigned int, - unsigned int*); -extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int); - - /* private routines for spnego_mechanism */ static spnego_token_t make_spnego_token(const char *); static gss_buffer_desc make_err_msg(const char *); -static int g_token_size(gss_OID_const, unsigned int); -static int g_make_token_header(gss_OID_const, unsigned int, - unsigned char **, unsigned int); -static int g_verify_token_header(gss_OID_const, unsigned int *, - unsigned char **, - int, unsigned int); -static int g_verify_neg_token_init(unsigned char **, unsigned int); -static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t); -static gss_buffer_t get_input_token(unsigned char **, unsigned int); -static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int); -static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *); +static int verify_token_header(struct k5input *, gss_OID_const); +static gss_OID get_mech_oid(OM_uint32 *minor_status, struct k5input *); +static gss_buffer_t get_octet_string(struct k5input *); +static gss_OID_set get_mech_set(OM_uint32 *, struct k5input *); +static OM_uint32 get_req_flags(struct k5input *, OM_uint32 *); static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t, gss_const_key_value_set_t, gss_cred_id_t *, gss_OID_set *, @@ -103,9 +87,6 @@ static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_ctx_id_t, static void release_spnego_ctx(spnego_gss_ctx_id_t *); static spnego_gss_ctx_id_t create_spnego_ctx(int); static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf); -static int put_input_token(unsigned char **, gss_buffer_t, unsigned int); -static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int); -static int put_negResult(unsigned char **, OM_uint32, unsigned int); static OM_uint32 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, @@ -150,8 +131,6 @@ acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t, static gss_OID negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *); -static int -g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *); static int make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, @@ -159,8 +138,8 @@ make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_buffer_t, OM_uint32, gss_buffer_t, send_token_flag, gss_buffer_t); -static int -make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t, +static OM_uint32 +make_spnego_tokenTarg_msg(uint8_t, gss_OID, gss_buffer_t, gss_buffer_t, send_token_flag, gss_buffer_t); @@ -169,8 +148,8 @@ get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t, gss_OID_set *, OM_uint32 *, gss_buffer_t *, gss_buffer_t *); static OM_uint32 -get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int, - OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *); +get_negTokenResp(OM_uint32 *, struct k5input *, OM_uint32 *, gss_OID *, + gss_buffer_t *, gss_buffer_t *); static int is_kerb_mech(gss_OID oid); @@ -189,7 +168,6 @@ const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0; static gss_OID_desc negoex_mech = { NEGOEX_OID_LENGTH, NEGOEX_OID }; static int make_NegHints(OM_uint32 *, gss_buffer_t *); -static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int); static OM_uint32 acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *, send_token_flag *, spnego_gss_ctx_id_t *); @@ -720,15 +698,15 @@ init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, send_token_flag *tokflag) { OM_uint32 ret, tmpmin; - unsigned char *ptr; gss_OID supportedMech = GSS_C_NO_OID; + struct k5input in; *acc_negState = UNSPECIFIED; *tokflag = ERROR_TOKEN_SEND; - ptr = buf->value; - ret = get_negTokenResp(minor_status, ptr, buf->length, acc_negState, - &supportedMech, responseToken, mechListMIC); + k5_input_init(&in, buf->value, buf->length); + ret = get_negTokenResp(minor_status, &in, acc_negState, &supportedMech, + responseToken, mechListMIC); if (ret != GSS_S_COMPLETE) goto cleanup; @@ -1179,33 +1157,6 @@ static const gss_OID_desc gss_mech_krb5_wrong_oid = { 9, "\052\206\110\202\367\022\001\002\002" }; /* - * verify that the input token length is not 0. If it is, just return. - * If the token length is greater than 0, der encode as a sequence - * and place in buf_out, advancing buf_out. - */ - -static int -put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token, - unsigned int buflen) -{ - int ret; - - /* if token length is 0, we do not want to send */ - if (input_token->length == 0) - return (0); - - if (input_token->length > buflen) - return (-1); - - *(*buf_out)++ = SEQUENCE; - if ((ret = gssint_put_der_length(input_token->length, buf_out, - input_token->length))) - return (ret); - TWRITE_STR(*buf_out, input_token->value, input_token->length); - return (0); -} - -/* * NegHints ::= SEQUENCE { * hintName [0] GeneralString OPTIONAL, * hintAddress [1] OCTET STRING OPTIONAL @@ -1221,42 +1172,28 @@ static int make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf) { OM_uint32 major_status; - unsigned int tlen = 0; - unsigned int hintNameSize = 0; - unsigned char *ptr; - unsigned char *t; + size_t hint_len, tlen; + uint8_t *t; const char *hintname = "not_defined_in_RFC4178@please_ignore"; const size_t hintname_len = strlen(hintname); + struct k5buf buf; *outbuf = GSS_C_NO_BUFFER; major_status = GSS_S_FAILURE; - /* Length of DER encoded GeneralString */ - tlen = 1 + gssint_der_length_size(hintname_len) + hintname_len; - hintNameSize = tlen; - - /* Length of DER encoded hintName */ - tlen += 1 + gssint_der_length_size(hintNameSize); + hint_len = k5_der_value_len(hintname_len); + tlen = k5_der_value_len(hint_len); t = gssalloc_malloc(tlen); if (t == NULL) { *minor_status = ENOMEM; goto errout; } + k5_buf_init_fixed(&buf, t, tlen); - ptr = t; - - *ptr++ = CONTEXT | 0x00; /* hintName identifier */ - if (gssint_put_der_length(hintNameSize, - &ptr, tlen - (int)(ptr-t))) - goto errout; - - *ptr++ = GENERAL_STRING; - if (gssint_put_der_length(hintname_len, &ptr, tlen - (int)(ptr-t))) - goto errout; - - memcpy(ptr, hintname, hintname_len); - ptr += hintname_len; + k5_der_add_taglen(&buf, CONTEXT | 0x00, hint_len); + k5_der_add_value(&buf, GENERAL_STRING, hintname, hintname_len); + assert(buf.len == tlen); *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc)); if (*outbuf == NULL) { @@ -1264,7 +1201,7 @@ make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf) goto errout; } (*outbuf)->value = (void *)t; - (*outbuf)->length = ptr - t; + (*outbuf)->length = tlen; t = NULL; /* don't free */ @@ -1431,8 +1368,7 @@ acc_ctx_cont(OM_uint32 *minstat, { OM_uint32 ret, tmpmin; gss_OID supportedMech; - unsigned int len; - unsigned char *ptr, *bufstart; + struct k5input in; ret = GSS_S_DEFECTIVE_TOKEN; *negState = REJECT; @@ -1441,27 +1377,18 @@ acc_ctx_cont(OM_uint32 *minstat, *return_token = ERROR_TOKEN_SEND; *responseToken = *mechListMIC = GSS_C_NO_BUFFER; - ptr = bufstart = buf->value; -#define REMAIN (buf->length - (ptr - bufstart)) - if (REMAIN == 0 || REMAIN > INT_MAX) - return GSS_S_DEFECTIVE_TOKEN; + k5_input_init(&in, buf->value, buf->length); - /* - * Attempt to work with old Sun SPNEGO. - */ - if (*ptr == HEADER_ID) { - ret = g_verify_token_header(gss_mech_spnego, - &len, &ptr, 0, REMAIN); + /* Attempt to work with old Sun SPNEGO. */ + if (in.len > 0 && *in.ptr == HEADER_ID) { + ret = verify_token_header(&in, gss_mech_spnego); if (ret) { *minstat = ret; return GSS_S_DEFECTIVE_TOKEN; } } - if (*ptr != (CONTEXT | 0x01)) { - return GSS_S_DEFECTIVE_TOKEN; - } - ret = get_negTokenResp(minstat, ptr, REMAIN, - negState, &supportedMech, + + ret = get_negTokenResp(minstat, &in, negState, &supportedMech, responseToken, mechListMIC); if (ret != GSS_S_COMPLETE) goto cleanup; @@ -1484,7 +1411,6 @@ cleanup: generic_gss_release_oid(&tmpmin, &supportedMech); } return ret; -#undef REMAIN } /* @@ -3329,34 +3255,24 @@ cleanup: /* following are token creation and reading routines */ /* - * If buff_in is not pointing to a MECH_OID, then return NULL and do not - * advance the buffer, otherwise, decode the mech_oid from the buffer and - * place in gss_OID. + * If in contains a tagged OID encoding, return a copy of the contents as a + * gss_OID and advance in past the encoding. Otherwise return NULL and do not + * advance in. */ static gss_OID -get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length) +get_mech_oid(OM_uint32 *minor_status, struct k5input *in) { - OM_uint32 status; - gss_OID_desc toid; - gss_OID mech_out = NULL; - unsigned int bytes; - int oid_length; - - if (length < 1 || **buff_in != MECH_OID) - return (NULL); - (*buff_in)++; - length--; + struct k5input oidrep; + OM_uint32 status; + gss_OID_desc oid; + gss_OID mech_out = NULL; - oid_length = gssint_get_der_length(buff_in, length, &bytes); - if (oid_length < 0 || length - bytes < (size_t)oid_length) + if (!k5_der_get_value(in, MECH_OID, &oidrep)) return (NULL); - toid.length = oid_length; - toid.elements = *buff_in; - *buff_in += toid.length; - - status = generic_gss_copy_oid(minor_status, &toid, &mech_out); - + oid.length = oidrep.len; + oid.elements = (uint8_t *)oidrep.ptr; + status = generic_gss_copy_oid(minor_status, &oid, &mech_out); if (status != GSS_S_COMPLETE) { map_errcode(minor_status); mech_out = NULL; @@ -3366,42 +3282,24 @@ get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length) } /* - * der encode the given mechanism oid into buf_out, advancing the - * buffer pointer. - */ - -static int -put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen) -{ - if (buflen < mech->length + 2) - return (-1); - *(*buf_out)++ = MECH_OID; - *(*buf_out)++ = (unsigned char) mech->length; - memcpy(*buf_out, mech->elements, mech->length); - *buf_out += mech->length; - return (0); -} - -/* - * verify that buff_in points to an octet string, if it does not, - * return NULL and don't advance the pointer. If it is an octet string - * decode buff_in into a gss_buffer_t and return it, advancing the - * buffer pointer. + * If in contains a tagged octet string encoding, return a copy of the contents + * as a gss_buffer_t and advance in past the encoding. Otherwise return NULL + * and do not advance in. */ static gss_buffer_t -get_input_token(unsigned char **buff_in, unsigned int buff_length) +get_octet_string(struct k5input *in) { gss_buffer_t input_token; - unsigned int len; + struct k5input ostr; - if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0) + if (!k5_der_get_value(in, OCTET_STRING, &ostr)) return (NULL); input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc)); if (input_token == NULL) return (NULL); - input_token->length = len; + input_token->length = ostr.len; if (input_token->length > 0) { input_token->value = gssalloc_malloc(input_token->length); if (input_token->value == NULL) { @@ -3409,88 +3307,49 @@ get_input_token(unsigned char **buff_in, unsigned int buff_length) return (NULL); } - memcpy(input_token->value, *buff_in, input_token->length); + memcpy(input_token->value, ostr.ptr, input_token->length); } else { input_token->value = NULL; } - *buff_in += input_token->length; return (input_token); } /* - * verify that the input token length is not 0. If it is, just return. - * If the token length is greater than 0, der encode as an octet string - * and place in buf_out, advancing buf_out. - */ - -static int -put_input_token(unsigned char **buf_out, gss_buffer_t input_token, - unsigned int buflen) -{ - int ret; - - /* if token length is 0, we do not want to send */ - if (input_token->length == 0) - return (0); - - if (input_token->length > buflen) - return (-1); - - *(*buf_out)++ = OCTET_STRING; - if ((ret = gssint_put_der_length(input_token->length, buf_out, - input_token->length))) - return (ret); - TWRITE_STR(*buf_out, input_token->value, input_token->length); - return (0); -} - -/* * verify that buff_in points to a sequence of der encoding. The mech * set is the only sequence of encoded object in the token, so if it is * a sequence of encoding, decode the mechset into a gss_OID_set and * return it, advancing the buffer pointer. */ static gss_OID_set -get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, - unsigned int buff_length) +get_mech_set(OM_uint32 *minor_status, struct k5input *in) { gss_OID_set returned_mechSet; OM_uint32 major_status, tmpmin; - int length; - unsigned int bytes; - OM_uint32 set_length; - unsigned char *start; - int i; + struct k5input seq; - if (buff_length < 1 || **buff_in != SEQUENCE_OF) + if (!k5_der_get_value(in, SEQUENCE_OF, &seq)) return (NULL); - start = *buff_in; - (*buff_in)++; - - length = gssint_get_der_length(buff_in, buff_length - 1, &bytes); - if (length < 0 || buff_length - 1 - bytes < (unsigned int)length) - return NULL; - major_status = gss_create_empty_oid_set(minor_status, &returned_mechSet); if (major_status != GSS_S_COMPLETE) return (NULL); - for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) { - gss_OID_desc *temp = get_mech_oid(minor_status, buff_in, - buff_length - (*buff_in - start)); - if (temp == NULL) - break; + while (!seq.status && seq.len > 0) { + gss_OID_desc *oid = get_mech_oid(minor_status, &seq); + + if (oid == NULL) { + gss_release_oid_set(&tmpmin, &returned_mechSet); + return (NULL); + } major_status = gss_add_oid_set_member(minor_status, - temp, &returned_mechSet); - generic_gss_release_oid(minor_status, &temp); + oid, &returned_mechSet); + generic_gss_release_oid(minor_status, &oid); if (major_status != GSS_S_COMPLETE) { gss_release_oid_set(&tmpmin, &returned_mechSet); return (NULL); } - set_length += returned_mechSet->elements[i].length + 2; } return (returned_mechSet); @@ -3500,74 +3359,48 @@ get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, * Encode mechSet into buf. */ static int -put_mech_set(gss_OID_set mechSet, gss_buffer_t buf) +put_mech_set(gss_OID_set mechSet, gss_buffer_t buffer_out) { - unsigned char *ptr; - unsigned int i; - unsigned int tlen, ilen; + uint8_t *ptr; + size_t ilen, tlen, i; + struct k5buf buf; + + ilen = 0; + for (i = 0; i < mechSet->count; i++) + ilen += k5_der_value_len(mechSet->elements[i].length); + tlen = k5_der_value_len(ilen); - tlen = ilen = 0; - for (i = 0; i < mechSet->count; i++) { - /* - * 0x06 [DER LEN] [OID] - */ - ilen += 1 + - gssint_der_length_size(mechSet->elements[i].length) + - mechSet->elements[i].length; - } - /* - * 0x30 [DER LEN] - */ - tlen = 1 + gssint_der_length_size(ilen) + ilen; ptr = gssalloc_malloc(tlen); if (ptr == NULL) return -1; + k5_buf_init_fixed(&buf, ptr, tlen); - buf->value = ptr; - buf->length = tlen; -#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr)) - - *ptr++ = SEQUENCE_OF; - if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0) - return -1; + k5_der_add_taglen(&buf, SEQUENCE_OF, ilen); for (i = 0; i < mechSet->count; i++) { - if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) { - return -1; - } + k5_der_add_value(&buf, MECH_OID, + mechSet->elements[i].elements, + mechSet->elements[i].length); } + assert(buf.len == tlen); + + buffer_out->value = ptr; + buffer_out->length = tlen; return 0; -#undef REMAIN } -/* - * Verify that buff_in is pointing to a BIT_STRING with the correct - * length and padding for the req_flags. If it is, decode req_flags - * and return them, otherwise, return NULL. - */ +/* Decode SPNEGO request flags from the DER encoding of a bit string and set + * them in *ret_flags. */ static OM_uint32 -get_req_flags(unsigned char **buff_in, OM_uint32 bodysize, - OM_uint32 *req_flags) +get_req_flags(struct k5input *in, OM_uint32 *req_flags) { - unsigned int len; - - if (bodysize < 1 || **buff_in != (CONTEXT | 0x01)) - return (0); - - if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01), - bodysize, &len) < 0 || len != 4) + if (in->status || in->len != 4 || + k5_input_get_byte(in) != BIT_STRING || + k5_input_get_byte(in) != BIT_STRING_LENGTH || + k5_input_get_byte(in) != BIT_STRING_PADDING) return GSS_S_DEFECTIVE_TOKEN; - if (*(*buff_in)++ != BIT_STRING) - return GSS_S_DEFECTIVE_TOKEN; - - if (*(*buff_in)++ != BIT_STRING_LENGTH) - return GSS_S_DEFECTIVE_TOKEN; - - if (*(*buff_in)++ != BIT_STRING_PADDING) - return GSS_S_DEFECTIVE_TOKEN; - - *req_flags = (OM_uint32) (*(*buff_in)++ >> 1); - return (0); + *req_flags = k5_input_get_byte(in) >> 1; + return GSS_S_COMPLETE; } static OM_uint32 @@ -3580,9 +3413,7 @@ get_negTokenInit(OM_uint32 *minor_status, gss_buffer_t *mechListMIC) { OM_uint32 err; - unsigned char *ptr, *bufstart; - unsigned int len; - gss_buffer_desc tmpbuf; + struct k5input in, seq, field; *minor_status = 0; der_mechSet->length = 0; @@ -3591,148 +3422,100 @@ get_negTokenInit(OM_uint32 *minor_status, *req_flags = 0; *mechtok = *mechListMIC = GSS_C_NO_BUFFER; - ptr = bufstart = buf->value; - if ((buf->length - (ptr - bufstart)) > INT_MAX) - return GSS_S_FAILURE; -#define REMAIN (buf->length - (ptr - bufstart)) + k5_input_init(&in, buf->value, buf->length); - err = g_verify_token_header(gss_mech_spnego, - &len, &ptr, 0, REMAIN); - if (err) { - *minor_status = err; - map_errcode(minor_status); - return GSS_S_FAILURE; - } - *minor_status = g_verify_neg_token_init(&ptr, REMAIN); - if (*minor_status) { - map_errcode(minor_status); - return GSS_S_FAILURE; - } + /* Advance past the framing header. */ + err = verify_token_header(&in, gss_mech_spnego); + if (err) + return GSS_S_DEFECTIVE_TOKEN; - /* alias into input_token */ - tmpbuf.value = ptr; - tmpbuf.length = REMAIN; - *mechSet = get_mech_set(minor_status, &ptr, REMAIN); - if (*mechSet == NULL) - return GSS_S_FAILURE; + /* Advance past the [0] tag for the NegotiationToken choice. */ + if (!k5_der_get_value(&in, CONTEXT, &seq)) + return GSS_S_DEFECTIVE_TOKEN; + + /* Advance past the SEQUENCE tag. */ + if (!k5_der_get_value(&seq, SEQUENCE, &seq)) + return GSS_S_DEFECTIVE_TOKEN; + + /* Get the contents of the mechTypes field. */ + if (!k5_der_get_value(&seq, CONTEXT, &field)) + return GSS_S_DEFECTIVE_TOKEN; - tmpbuf.length = ptr - (unsigned char *)tmpbuf.value; - der_mechSet->value = gssalloc_malloc(tmpbuf.length); + /* Store a copy of the contents for MIC computation. */ + der_mechSet->value = gssalloc_malloc(field.len); if (der_mechSet->value == NULL) return GSS_S_FAILURE; - memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length); - der_mechSet->length = tmpbuf.length; + memcpy(der_mechSet->value, field.ptr, field.len); + der_mechSet->length = field.len; - err = get_req_flags(&ptr, REMAIN, req_flags); - if (err != GSS_S_COMPLETE) { - return err; + /* Decode the contents into an OID set. */ + *mechSet = get_mech_set(minor_status, &field); + if (*mechSet == NULL) + return GSS_S_FAILURE; + + if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) { + err = get_req_flags(&field, req_flags); + if (err != GSS_S_COMPLETE) + return err; } - if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02), - REMAIN, &len) >= 0) { - *mechtok = get_input_token(&ptr, len); - if (*mechtok == GSS_C_NO_BUFFER) { + + if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) { + *mechtok = get_octet_string(&field); + if (*mechtok == GSS_C_NO_BUFFER) return GSS_S_FAILURE; - } } - if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03), - REMAIN, &len) >= 0) { - *mechListMIC = get_input_token(&ptr, len); - if (*mechListMIC == GSS_C_NO_BUFFER) { + + if (k5_der_get_value(&seq, CONTEXT | 0x03, &field)) { + *mechListMIC = get_octet_string(&field); + if (*mechListMIC == GSS_C_NO_BUFFER) return GSS_S_FAILURE; - } } - return GSS_S_COMPLETE; -#undef REMAIN + + return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE; } +/* Decode a NegotiationToken of type negTokenResp. */ static OM_uint32 -get_negTokenResp(OM_uint32 *minor_status, - unsigned char *buf, unsigned int buflen, - OM_uint32 *negState, - gss_OID *supportedMech, - gss_buffer_t *responseToken, - gss_buffer_t *mechListMIC) +get_negTokenResp(OM_uint32 *minor_status, struct k5input *in, + OM_uint32 *negState, gss_OID *supportedMech, + gss_buffer_t *responseToken, gss_buffer_t *mechListMIC) { - unsigned char *ptr, *bufstart; - unsigned int len; - int tmplen; - unsigned int tag, bytes; + struct k5input seq, field, en; *negState = UNSPECIFIED; *supportedMech = GSS_C_NO_OID; *responseToken = *mechListMIC = GSS_C_NO_BUFFER; - ptr = bufstart = buf; -#define REMAIN (buflen - (ptr - bufstart)) - if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0) + /* Advance past the [1] tag for the NegotiationToken choice. */ + if (!k5_der_get_value(in, CONTEXT | 0x01, &seq)) return GSS_S_DEFECTIVE_TOKEN; - if (*ptr++ == SEQUENCE) { - tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); - if (tmplen < 0 || REMAIN < (unsigned int)tmplen) - return GSS_S_DEFECTIVE_TOKEN; - } - if (REMAIN < 1) - tag = 0; - else - tag = *ptr++; - - if (tag == CONTEXT) { - tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); - if (tmplen < 0 || REMAIN < (unsigned int)tmplen) - return GSS_S_DEFECTIVE_TOKEN; - if (g_get_tag_and_length(&ptr, ENUMERATED, - REMAIN, &len) < 0) - return GSS_S_DEFECTIVE_TOKEN; + /* Advance seq past the SEQUENCE tag (historically this code allows the + * tag to be missing). */ + (void)k5_der_get_value(&seq, SEQUENCE, &seq); - if (len != ENUMERATION_LENGTH) + if (k5_der_get_value(&seq, CONTEXT, &field)) { + if (!k5_der_get_value(&field, ENUMERATED, &en)) return GSS_S_DEFECTIVE_TOKEN; - - if (REMAIN < 1) + if (en.len != ENUMERATION_LENGTH) return GSS_S_DEFECTIVE_TOKEN; - *negState = *ptr++; - - if (REMAIN < 1) - tag = 0; - else - tag = *ptr++; + *negState = *en.ptr; } - if (tag == (CONTEXT | 0x01)) { - tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); - if (tmplen < 0 || REMAIN < (unsigned int)tmplen) - return GSS_S_DEFECTIVE_TOKEN; - *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN); + if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) { + *supportedMech = get_mech_oid(minor_status, &field); if (*supportedMech == GSS_C_NO_OID) return GSS_S_DEFECTIVE_TOKEN; - - if (REMAIN < 1) - tag = 0; - else - tag = *ptr++; } - if (tag == (CONTEXT | 0x02)) { - tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); - if (tmplen < 0 || REMAIN < (unsigned int)tmplen) - return GSS_S_DEFECTIVE_TOKEN; - *responseToken = get_input_token(&ptr, REMAIN); + if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) { + *responseToken = get_octet_string(&field); if (*responseToken == GSS_C_NO_BUFFER) return GSS_S_DEFECTIVE_TOKEN; - - if (REMAIN < 1) - tag = 0; - else - tag = *ptr++; } - if (tag == (CONTEXT | 0x03)) { - tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); - if (tmplen < 0 || REMAIN < (unsigned int)tmplen) - return GSS_S_DEFECTIVE_TOKEN; - *mechListMIC = get_input_token(&ptr, REMAIN); - if (*mechListMIC == GSS_C_NO_BUFFER) - return GSS_S_DEFECTIVE_TOKEN; + if (k5_der_get_value(&seq, CONTEXT | 0x04, &field)) { + *mechListMIC = get_octet_string(&field); /* Handle Windows 2000 duplicate response token */ if (*responseToken && @@ -3746,25 +3529,8 @@ get_negTokenResp(OM_uint32 *minor_status, *mechListMIC = NULL; } } - return GSS_S_COMPLETE; -#undef REMAIN -} -/* - * der encode the passed negResults as an ENUMERATED type and - * place it in buf_out, advancing the buffer. - */ - -static int -put_negResult(unsigned char **buf_out, OM_uint32 negResult, - unsigned int buflen) -{ - if (buflen < 3) - return (-1); - *(*buf_out)++ = ENUMERATED; - *(*buf_out)++ = ENUMERATION_LENGTH; - *(*buf_out)++ = (unsigned char) negResult; - return (0); + return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE; } /* @@ -3848,22 +3614,15 @@ make_err_msg(const char *name) * Use DER rules, definite length method per RFC 2478 */ static int -make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, - int negHintsCompat, - gss_buffer_t mechListMIC, OM_uint32 req_flags, - gss_buffer_t data, send_token_flag sendtoken, +make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, int negHintsCompat, + gss_buffer_t mic, OM_uint32 req_flags, + gss_buffer_t token, send_token_flag sendtoken, gss_buffer_t outbuf) { - int ret = 0; - unsigned int tlen, dataLen = 0; - unsigned int negTokenInitSize = 0; - unsigned int negTokenInitSeqSize = 0; - unsigned int negTokenInitContSize = 0; - unsigned int rspTokenSize = 0; - unsigned int mechListTokenSize = 0; - unsigned int micTokenSize = 0; - unsigned char *t; - unsigned char *ptr; + size_t f0len, f2len, f3len, fields_len, seq_len, choice_len; + size_t mech_len, framed_len; + uint8_t *t; + struct k5buf buf; if (outbuf == GSS_C_NO_BUFFER) return (-1); @@ -3871,141 +3630,66 @@ make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, outbuf->length = 0; outbuf->value = NULL; - /* calculate the data length */ - - /* - * 0xa0 [DER LEN] [mechTypes] - */ - mechListTokenSize = 1 + - gssint_der_length_size(spnego_ctx->DER_mechTypes.length) + - spnego_ctx->DER_mechTypes.length; - dataLen += mechListTokenSize; - - /* - * If a token from gss_init_sec_context exists, - * add the length of the token + the ASN.1 overhead - */ - if (data != NULL) { - /* - * Encoded in final output as: - * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA] - * -----s--------|--------s2---------- - */ - rspTokenSize = 1 + - gssint_der_length_size(data->length) + - data->length; - dataLen += 1 + gssint_der_length_size(rspTokenSize) + - rspTokenSize; - } - - if (mechListMIC) { - /* - * Encoded in final output as: - * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA] - * --s-- -----tlen------------ - */ - micTokenSize = 1 + - gssint_der_length_size(mechListMIC->length) + - mechListMIC->length; - dataLen += 1 + - gssint_der_length_size(micTokenSize) + - micTokenSize; - } - - /* - * Add size of DER encoding - * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ] - * 0x30 [DER_LEN] [data] - * - */ - negTokenInitContSize = dataLen; - negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen; - dataLen = negTokenInitSeqSize; - - /* - * negTokenInitSize indicates the bytes needed to - * hold the ASN.1 encoding of the entire NegTokenInit - * SEQUENCE. - * 0xa0 [DER_LEN] + data - * - */ - negTokenInitSize = 1 + - gssint_der_length_size(negTokenInitSeqSize) + - negTokenInitSeqSize; - - tlen = g_token_size(gss_mech_spnego, negTokenInitSize); - - t = (unsigned char *) gssalloc_malloc(tlen); - - if (t == NULL) { + /* Calculate the length of each field and the total fields length. */ + fields_len = 0; + /* mechTypes [0] MechTypeList, previously assembled in spnego_ctx */ + f0len = spnego_ctx->DER_mechTypes.length; + fields_len += k5_der_value_len(f0len); + if (token != NULL) { + /* mechToken [2] OCTET STRING OPTIONAL */ + f2len = k5_der_value_len(token->length); + fields_len += k5_der_value_len(f2len); + } + if (mic != GSS_C_NO_BUFFER) { + /* mechListMIC [3] OCTET STRING OPTIONAL */ + f3len = k5_der_value_len(mic->length); + fields_len += k5_der_value_len(f3len); + } + + /* Calculate the length of the sequence and choice. */ + seq_len = k5_der_value_len(fields_len); + choice_len = k5_der_value_len(seq_len); + + /* Calculate the framed token length. */ + mech_len = k5_der_value_len(gss_mech_spnego->length); + framed_len = k5_der_value_len(mech_len + choice_len); + + /* Allocate space and prepare a buffer. */ + t = gssalloc_malloc(framed_len); + if (t == NULL) return (-1); - } - - ptr = t; + k5_buf_init_fixed(&buf, t, framed_len); - /* create the message */ - if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize, - &ptr, tlen))) - goto errout; + /* Add generic token framing. */ + k5_der_add_taglen(&buf, HEADER_ID, mech_len + choice_len); + k5_der_add_value(&buf, MECH_OID, gss_mech_spnego->elements, + gss_mech_spnego->length); - *ptr++ = CONTEXT; /* NegotiationToken identifier */ - if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen))) - goto errout; + /* Add NegotiationToken choice tag and NegTokenInit sequence tag. */ + k5_der_add_taglen(&buf, CONTEXT | 0x00, seq_len); + k5_der_add_taglen(&buf, SEQUENCE, fields_len); - *ptr++ = SEQUENCE; - if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr, - tlen - (int)(ptr-t)))) - goto errout; + /* Add the already-encoded mechanism list as mechTypes. */ + k5_der_add_value(&buf, CONTEXT | 0x00, spnego_ctx->DER_mechTypes.value, + spnego_ctx->DER_mechTypes.length); - *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */ - if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length, - &ptr, tlen - (int)(ptr-t)))) - goto errout; - - /* We already encoded the MechSetList */ - (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value, - spnego_ctx->DER_mechTypes.length); - - ptr += spnego_ctx->DER_mechTypes.length; - - if (data != NULL) { - *ptr++ = CONTEXT | 0x02; - if ((ret = gssint_put_der_length(rspTokenSize, - &ptr, tlen - (int)(ptr - t)))) - goto errout; - - if ((ret = put_input_token(&ptr, data, - tlen - (int)(ptr - t)))) - goto errout; + if (token != NULL) { + k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len); + k5_der_add_value(&buf, OCTET_STRING, token->value, + token->length); } - if (mechListMIC != GSS_C_NO_BUFFER) { - *ptr++ = CONTEXT | 0x03; - if ((ret = gssint_put_der_length(micTokenSize, - &ptr, tlen - (int)(ptr - t)))) - goto errout; - - if (negHintsCompat) { - ret = put_neg_hints(&ptr, mechListMIC, - tlen - (int)(ptr - t)); - if (ret) - goto errout; - } else if ((ret = put_input_token(&ptr, mechListMIC, - tlen - (int)(ptr - t)))) - goto errout; + if (mic != GSS_C_NO_BUFFER) { + uint8_t id = negHintsCompat ? SEQUENCE : OCTET_STRING; + k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len); + k5_der_add_value(&buf, id, mic->value, mic->length); } -errout: - if (ret != 0) { - if (t) - free(t); - t = NULL; - tlen = 0; - } - outbuf->length = tlen; - outbuf->value = (void *) t; + assert(buf.len == framed_len); + outbuf->length = framed_len; + outbuf->value = t; - return (ret); + return (0); } /* @@ -4013,414 +3697,106 @@ errout: * gss_accept_sec_context and eventually up to the application program * and over to the client. */ -static int -make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted, - gss_buffer_t data, gss_buffer_t mechListMIC, +static OM_uint32 +make_spnego_tokenTarg_msg(uint8_t status, gss_OID mech_wanted, + gss_buffer_t token, gss_buffer_t mic, send_token_flag sendtoken, gss_buffer_t outbuf) { - unsigned int tlen = 0; - unsigned int ret = 0; - unsigned int NegTokenTargSize = 0; - unsigned int NegTokenSize = 0; - unsigned int rspTokenSize = 0; - unsigned int micTokenSize = 0; - unsigned int dataLen = 0; - unsigned char *t; - unsigned char *ptr; + size_t f0len, f1len, f2len, f3len, fields_len, seq_len, choice_len; + uint8_t *t; + struct k5buf buf; if (outbuf == GSS_C_NO_BUFFER) return (GSS_S_DEFECTIVE_TOKEN); if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID) - return (GSS_S_DEFECTIVE_TOKEN); + return (GSS_S_DEFECTIVE_TOKEN); outbuf->length = 0; outbuf->value = NULL; - /* - * ASN.1 encoding of the negResult - * ENUMERATED type is 3 bytes - * ENUMERATED TAG, Length, Value, - * Plus 2 bytes for the CONTEXT id and length. - */ - dataLen = 5; - - /* - * calculate data length - * - * If this is the initial token, include length of - * mech_type and the negotiation result fields. - */ + /* Calculate the length of each field and the total fields length. */ + fields_len = 0; + /* negState [0] ENUMERATED { ... } OPTIONAL */ + f0len = k5_der_value_len(1); + fields_len += k5_der_value_len(f0len); if (sendtoken == INIT_TOKEN_SEND) { - int mechlistTokenSize; - /* - * 1 byte for the CONTEXT ID(0xa0), - * 1 byte for the OID ID(0x06) - * 1 byte for OID Length field - * Plus the rest... (OID Length, OID value) - */ - mechlistTokenSize = 3 + mech_wanted->length + - gssint_der_length_size(mech_wanted->length); - - dataLen += mechlistTokenSize; + /* supportedMech [1] MechType OPTIONAL */ + f1len = k5_der_value_len(mech_wanted->length); + fields_len += k5_der_value_len(f1len); } - if (data != NULL && data->length > 0) { - /* Length of the inner token */ - rspTokenSize = 1 + gssint_der_length_size(data->length) + - data->length; - - dataLen += rspTokenSize; - - /* Length of the outer token */ - dataLen += 1 + gssint_der_length_size(rspTokenSize); + if (token != NULL && token->length > 0) { + /* mechToken [2] OCTET STRING OPTIONAL */ + f2len = k5_der_value_len(token->length); + fields_len += k5_der_value_len(f2len); } - if (mechListMIC != NULL) { - - /* Length of the inner token */ - micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) + - mechListMIC->length; - - dataLen += micTokenSize; - - /* Length of the outer token */ - dataLen += 1 + gssint_der_length_size(micTokenSize); + if (mic != NULL) { + /* mechListMIC [3] OCTET STRING OPTIONAL */ + f3len = k5_der_value_len(mic->length); + fields_len += k5_der_value_len(f3len); } - /* - * Add size of DER encoded: - * NegTokenTarg [ SEQUENCE ] of - * NegResult[0] ENUMERATED { - * accept_completed(0), - * accept_incomplete(1), - * reject(2) } - * supportedMech [1] MechType OPTIONAL, - * responseToken [2] OCTET STRING OPTIONAL, - * mechListMIC [3] OCTET STRING OPTIONAL - * - * size = data->length + MechListMic + SupportedMech len + - * Result Length + ASN.1 overhead - */ - NegTokenTargSize = dataLen; - dataLen += 1 + gssint_der_length_size(NegTokenTargSize); - - /* - * NegotiationToken [ CHOICE ]{ - * negTokenInit [0] NegTokenInit, - * negTokenTarg [1] NegTokenTarg } - */ - NegTokenSize = dataLen; - dataLen += 1 + gssint_der_length_size(NegTokenSize); - tlen = dataLen; - t = (unsigned char *) gssalloc_malloc(tlen); + /* Calculate the length of the sequence and choice. */ + seq_len = k5_der_value_len(fields_len); + choice_len = k5_der_value_len(seq_len); - if (t == NULL) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } + /* Allocate space and prepare a buffer. */ + t = gssalloc_malloc(choice_len); + if (t == NULL) + return (GSS_S_DEFECTIVE_TOKEN); + k5_buf_init_fixed(&buf, t, choice_len); - ptr = t; + /* Add the choice tag and begin the sequence. */ + k5_der_add_taglen(&buf, CONTEXT | 0x01, seq_len); + k5_der_add_taglen(&buf, SEQUENCE, fields_len); - /* - * Indicate that we are sending CHOICE 1 - * (NegTokenTarg) - */ - *ptr++ = CONTEXT | 0x01; - if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - *ptr++ = SEQUENCE; - if (gssint_put_der_length(NegTokenTargSize, &ptr, - tlen - (int)(ptr-t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } + /* Add the negState field. */ + k5_der_add_taglen(&buf, CONTEXT | 0x00, f0len); + k5_der_add_value(&buf, ENUMERATED, &status, 1); - /* - * First field of the NegTokenTarg SEQUENCE - * is the ENUMERATED NegResult. - */ - *ptr++ = CONTEXT; - if (gssint_put_der_length(3, &ptr, - tlen - (int)(ptr-t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } if (sendtoken == INIT_TOKEN_SEND) { - /* - * Next, is the Supported MechType - */ - *ptr++ = CONTEXT | 0x01; - if (gssint_put_der_length(mech_wanted->length + 2, - &ptr, - tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - if (put_mech_oid(&ptr, mech_wanted, - tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - } - if (data != NULL && data->length > 0) { - *ptr++ = CONTEXT | 0x02; - if (gssint_put_der_length(rspTokenSize, &ptr, - tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - if (put_input_token(&ptr, data, - tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - } - if (mechListMIC != NULL) { - *ptr++ = CONTEXT | 0x03; - if (gssint_put_der_length(micTokenSize, &ptr, - tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - if (put_input_token(&ptr, mechListMIC, - tlen - (int)(ptr - t)) < 0) { - ret = GSS_S_DEFECTIVE_TOKEN; - goto errout; - } - } - ret = GSS_S_COMPLETE; -errout: - if (ret != GSS_S_COMPLETE) { - if (t) - free(t); - } else { - outbuf->length = ptr - t; - outbuf->value = (void *) t; - } - - return (ret); -} - -/* determine size of token */ -static int -g_token_size(gss_OID_const mech, unsigned int body_size) -{ - int hdrsize; - - /* - * Initialize the header size to the - * MECH_OID byte + the bytes needed to indicate the - * length of the OID + the OID itself. - * - * 0x06 [MECHLENFIELD] MECHDATA - */ - hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length; - - /* - * Now add the bytes needed for the initial header - * token bytes: - * 0x60 + [DER_LEN] + HDRSIZE - */ - hdrsize += 1 + gssint_der_length_size(body_size + hdrsize); - - return (hdrsize + body_size); -} - -/* - * generate token header. - * - * Use DER Definite Length method per RFC2478 - * Use of indefinite length encoding will not be compatible - * with Microsoft or others that actually follow the spec. - */ -static int -g_make_token_header(gss_OID_const mech, - unsigned int body_size, - unsigned char **buf, - unsigned int totallen) -{ - int ret = 0; - unsigned int hdrsize; - unsigned char *p = *buf; - - hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length; - - *(*buf)++ = HEADER_ID; - if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen))) - return (ret); - - *(*buf)++ = MECH_OID; - if ((ret = gssint_put_der_length(mech->length, buf, - totallen - (int)(p - *buf)))) - return (ret); - TWRITE_STR(*buf, mech->elements, mech->length); - return (0); -} - -/* - * NOTE: This checks that the length returned by - * gssint_get_der_length() is not greater than the number of octets - * remaining, even though gssint_get_der_length() already checks, in - * theory. - */ -static int -g_get_tag_and_length(unsigned char **buf, int tag, - unsigned int buflen, unsigned int *outlen) -{ - unsigned char *ptr = *buf; - int ret = -1; /* pessimists, assume failure ! */ - unsigned int encoded_len; - int tmplen = 0; - - *outlen = 0; - if (buflen > 1 && *ptr == tag) { - ptr++; - tmplen = gssint_get_der_length(&ptr, buflen - 1, - &encoded_len); - if (tmplen < 0) { - ret = -1; - } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) { - ret = -1; - } else - ret = 0; + /* Add the supportedMech field. */ + k5_der_add_taglen(&buf, CONTEXT | 0x01, f1len); + k5_der_add_value(&buf, MECH_OID, mech_wanted->elements, + mech_wanted->length); } - *outlen = tmplen; - *buf = ptr; - return (ret); -} - -static int -g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size) -{ - unsigned char *buf = *buf_in; - unsigned char *endptr = buf + cur_size; - int seqsize; - int ret = 0; - unsigned int bytes; - - /* - * Verify this is a NegotiationToken type token - * - check for a0(context specific identifier) - * - get length and verify that enoughd ata exists - */ - if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &bytes) < 0 || - bytes == 0) - return (G_BAD_TOK_HEADER); - cur_size = bytes; /* should indicate bytes remaining */ - - /* - * Verify the next piece, it should identify this as - * a strucure of type NegTokenInit. - */ - if (*buf++ == SEQUENCE) { - if ((seqsize = gssint_get_der_length(&buf, cur_size - 1, &bytes)) <= 0) - return (G_BAD_TOK_HEADER); - /* - * Make sure we have the entire buffer as described - */ - if (seqsize > endptr - buf) - return (G_BAD_TOK_HEADER); - } else { - return (G_BAD_TOK_HEADER); + if (token != NULL && token->length > 0) { + /* Add the mechToken field. */ + k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len); + k5_der_add_value(&buf, OCTET_STRING, token->value, + token->length); } - cur_size = seqsize; /* should indicate bytes remaining */ - - /* - * Verify that the first blob is a sequence of mechTypes - */ - if (*buf++ == CONTEXT) { - if ((seqsize = gssint_get_der_length(&buf, cur_size - 1, &bytes)) < 0) - return (G_BAD_TOK_HEADER); - /* - * Make sure we have the entire buffer as described - */ - if (seqsize > endptr - buf) - return (G_BAD_TOK_HEADER); - } else { - return (G_BAD_TOK_HEADER); + if (mic != NULL) { + /* Add the mechListMIC field. */ + k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len); + k5_der_add_value(&buf, OCTET_STRING, mic->value, mic->length); } - /* - * At this point, *buf should be at the beginning of the - * DER encoded list of mech types that are to be negotiated. - */ - *buf_in = buf; - - return (ret); + assert(buf.len == choice_len); + outbuf->length = choice_len; + outbuf->value = t; + return (0); } -/* verify token header. */ +/* Advance in past the [APPLICATION 0] tag and thisMech field of an + * InitialContextToken encoding, checking that thisMech matches mech. */ static int -g_verify_token_header(gss_OID_const mech, - unsigned int *body_size, - unsigned char **buf_in, - int tok_type, - unsigned int toksize) +verify_token_header(struct k5input *in, gss_OID_const mech) { - unsigned char *buf = *buf_in; - int seqsize; - gss_OID_desc toid; - int ret = 0; - unsigned int bytes; + gss_OID_desc oid; + struct k5input field; - if (toksize-- < 1) + if (!k5_der_get_value(in, HEADER_ID, in)) return (G_BAD_TOK_HEADER); - - if (*buf++ != HEADER_ID) + if (!k5_der_get_value(in, MECH_OID, &field)) return (G_BAD_TOK_HEADER); - if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0) - return (G_BAD_TOK_HEADER); - - if ((seqsize + bytes) != toksize) - return (G_BAD_TOK_HEADER); - - if (toksize-- < 1) - return (G_BAD_TOK_HEADER); - - - if (*buf++ != MECH_OID) - return (G_BAD_TOK_HEADER); - - if (toksize-- < 1) - return (G_BAD_TOK_HEADER); - - toid.length = *buf++; - - if (toksize < toid.length) - return (G_BAD_TOK_HEADER); - else - toksize -= toid.length; - - toid.elements = buf; - buf += toid.length; - - if (!g_OID_equal(&toid, mech)) - ret = G_WRONG_MECH; - - /* - * G_WRONG_MECH is not returned immediately because it's more important - * to return G_BAD_TOK_HEADER if the token header is in fact bad - */ - if (toksize < 2) - return (G_BAD_TOK_HEADER); - else - toksize -= 2; - - if (!ret) { - *buf_in = buf; - *body_size = toksize; - } - - return (ret); + oid.length = field.len; + oid.elements = (uint8_t *)field.ptr; + return g_OID_equal(&oid, mech) ? 0 : G_WRONG_MECH; } /* -- cgit v1.1