OM_uint32 gss_decapsulate_token(gss_const_buffer_t input_token, gss_const_OID token_oid, gss_buffer_t output_token) { OM_uint32 minor; unsigned int body_size = 0; unsigned char *buf_in; if (input_token == GSS_C_NO_BUFFER || token_oid == GSS_C_NO_OID) return GSS_S_CALL_INACCESSIBLE_READ; if (output_token == GSS_C_NO_BUFFER) return GSS_S_CALL_INACCESSIBLE_WRITE; buf_in = input_token->value; minor = g_verify_token_header(token_oid, &body_size, &buf_in, -1, input_token->length, G_VFY_TOKEN_HDR_WRAPPER_REQUIRED); if (minor != 0) return GSS_S_DEFECTIVE_TOKEN; output_token->value = malloc(body_size); if (output_token->value == NULL) return GSS_S_FAILURE; memcpy(output_token->value, buf_in, body_size); output_token->length = body_size; return GSS_S_COMPLETE; }
static krb5_boolean iakerb_is_iakerb_token(const gss_buffer_t token) { krb5_error_code code; unsigned int bodysize = token->length; unsigned char *ptr = token->value; code = g_verify_token_header(gss_mech_iakerb, &bodysize, &ptr, IAKERB_TOK_PROXY, token->length, 0); return (code == 0); }
/* * Split a STREAM | SIGN_DATA | DATA into * HEADER | SIGN_DATA | DATA | PADDING | TRAILER */ static OM_uint32 kg_unseal_stream_iov(OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, int *conf_state, gss_qop_t *qop_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { unsigned char *ptr; unsigned int bodysize; OM_uint32 code = 0, major_status = GSS_S_FAILURE; krb5_context context = ctx->k5_context; int conf_req_flag, toktype2; int i = 0, j; gss_iov_buffer_desc *tiov = NULL; gss_iov_buffer_t stream, data = NULL; gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer; assert(toktype == KG_TOK_WRAP_MSG); if (toktype != KG_TOK_WRAP_MSG || (ctx->gss_flags & GSS_C_DCE_STYLE)) { code = EINVAL; goto cleanup; } stream = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM); assert(stream != NULL); ptr = (unsigned char *)stream->buffer.value; code = g_verify_token_header(ctx->mech_used, &bodysize, &ptr, -1, stream->buffer.length, 0); if (code != 0) { major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } if (bodysize < 2) { *minor_status = (OM_uint32)G_BAD_TOK_HEADER; return GSS_S_DEFECTIVE_TOKEN; } toktype2 = load_16_be(ptr); ptr += 2; bodysize -= 2; tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc)); if (tiov == NULL) { code = ENOMEM; goto cleanup; } /* HEADER */ theader = &tiov[i++]; theader->type = GSS_IOV_BUFFER_TYPE_HEADER; theader->buffer.value = stream->buffer.value; theader->buffer.length = ptr - (unsigned char *)stream->buffer.value; if (bodysize < 14 || stream->buffer.length != theader->buffer.length + bodysize) { major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } theader->buffer.length += 14; /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */ for (j = 0; j < iov_count; j++) { OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type); if (type == GSS_IOV_BUFFER_TYPE_DATA) { if (data != NULL) { /* only a single DATA buffer can appear */ code = EINVAL; goto cleanup; } data = &iov[j]; tdata = &tiov[i]; } if (type == GSS_IOV_BUFFER_TYPE_DATA || type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) tiov[i++] = iov[j]; } if (data == NULL) { /* a single DATA buffer must be present */ code = EINVAL; goto cleanup; } /* PADDING | TRAILER */ tpadding = &tiov[i++]; tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING; tpadding->buffer.length = 0; tpadding->buffer.value = NULL; ttrailer = &tiov[i++]; ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER; switch (toktype2) { case KG2_TOK_MIC_MSG: case KG2_TOK_WRAP_MSG: case KG2_TOK_DEL_CTX: { size_t ec, rrc; krb5_enctype enctype; unsigned int k5_headerlen = 0; unsigned int k5_trailerlen = 0; if (ctx->have_acceptor_subkey) enctype = ctx->acceptor_subkey->keyblock.enctype; else enctype = ctx->subkey->keyblock.enctype; conf_req_flag = ((ptr[0] & FLAG_WRAP_CONFIDENTIAL) != 0); ec = conf_req_flag ? load_16_be(ptr + 2) : 0; rrc = load_16_be(ptr + 4); if (rrc != 0) { if (!gss_krb5int_rotate_left((unsigned char *)stream->buffer.value + 16, stream->buffer.length - 16, rrc)) { code = ENOMEM; goto cleanup; } store_16_be(0, ptr + 4); /* set RRC to zero */ } if (conf_req_flag) { code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); if (code != 0) goto cleanup; theader->buffer.length += k5_headerlen; /* length validated later */ } /* no PADDING for CFX, EC is used instead */ code = krb5_c_crypto_length(context, enctype, conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, &k5_trailerlen); if (code != 0) goto cleanup; ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + k5_trailerlen; ttrailer->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - ttrailer->buffer.length; break; } case KG_TOK_MIC_MSG: case KG_TOK_WRAP_MSG: case KG_TOK_DEL_CTX: theader->buffer.length += ctx->cksum_size + kg_confounder_size(context, ctx->enc->keyblock.enctype); /* * we can't set the padding accurately until decryption; * kg_fixup_padding_iov() will take care of this */ tpadding->buffer.length = 1; tpadding->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - 1; /* no TRAILER for pre-CFX */ ttrailer->buffer.length = 0; ttrailer->buffer.value = NULL; break; default: code = (OM_uint32)G_BAD_TOK_HEADER; major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; break; } /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/ /* Old: GSS-Header | Conf | Data | Pad | */ /* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */ /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/ /* validate lengths */ if (stream->buffer.length < theader->buffer.length + tpadding->buffer.length + ttrailer->buffer.length) { code = (OM_uint32)KRB5_BAD_MSIZE; major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } /* setup data */ tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length - tpadding->buffer.length - theader->buffer.length; assert(data != NULL); if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(tdata, tdata->buffer.length); if (code != 0) goto cleanup; memcpy(tdata->buffer.value, (unsigned char *)stream->buffer.value + theader->buffer.length, tdata->buffer.length); } else tdata->buffer.value = (unsigned char *)stream->buffer.value + theader->buffer.length; assert(i <= iov_count + 2); major_status = kg_unseal_iov_token(&code, ctx, conf_state, qop_state, tiov, i, toktype); if (major_status == GSS_S_COMPLETE) *data = *tdata; else kg_release_iov(tdata, 1); cleanup: if (tiov != NULL) free(tiov); *minor_status = code; return major_status; }
/* * Caller must provide TOKEN | DATA | PADDING | TRAILER, except * for DCE in which case it can just provide TOKEN | DATA (must * guarantee that DATA is padded) */ static OM_uint32 kg_unseal_iov_token(OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, int *conf_state, gss_qop_t *qop_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { krb5_error_code code; krb5_context context = ctx->k5_context; unsigned char *ptr; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; size_t input_length; unsigned int bodysize; int toktype2; header = kg_locate_header_iov(iov, iov_count, toktype); if (header == NULL) { *minor_status = EINVAL; return GSS_S_FAILURE; } padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); ptr = (unsigned char *)header->buffer.value; input_length = header->buffer.length; if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0 && toktype == KG_TOK_WRAP_MSG) { size_t data_length, assoc_data_length; kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); input_length += data_length - assoc_data_length; if (padding != NULL) input_length += padding->buffer.length; if (trailer != NULL) input_length += trailer->buffer.length; } code = g_verify_token_header(ctx->mech_used, &bodysize, &ptr, -1, input_length, 0); if (code != 0) { *minor_status = code; return GSS_S_DEFECTIVE_TOKEN; } if (bodysize < 2) { *minor_status = (OM_uint32)G_BAD_TOK_HEADER; return GSS_S_DEFECTIVE_TOKEN; } toktype2 = load_16_be(ptr); ptr += 2; bodysize -= 2; switch (toktype2) { case KG2_TOK_MIC_MSG: case KG2_TOK_WRAP_MSG: case KG2_TOK_DEL_CTX: code = gss_krb5int_unseal_v3_iov(context, minor_status, ctx, iov, iov_count, conf_state, qop_state, toktype); break; case KG_TOK_MIC_MSG: case KG_TOK_WRAP_MSG: case KG_TOK_DEL_CTX: code = kg_unseal_v1_iov(context, minor_status, ctx, iov, iov_count, (size_t)(ptr - (unsigned char *)header->buffer.value), conf_state, qop_state, toktype); break; default: *minor_status = (OM_uint32)G_BAD_TOK_HEADER; code = GSS_S_DEFECTIVE_TOKEN; break; } if (code != 0) save_error_info(*minor_status, context); return code; }
/* * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP */ static krb5_error_code iakerb_parse_token(iakerb_ctx_id_t ctx, int initialContextToken, const gss_buffer_t token, krb5_data *realm, krb5_data **cookie, krb5_data *request) { krb5_error_code code; krb5_iakerb_header *iah = NULL; unsigned int bodysize, lenlen; int length; unsigned char *ptr; int flags = 0; krb5_data data; if (token == GSS_C_NO_BUFFER || token->length == 0) { code = KRB5_BAD_MSIZE; goto cleanup; } 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); if (code != 0) goto cleanup; data.data = (char *)ptr; if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) { 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); code = decode_krb5_iakerb_header(&data, &iah); if (code != 0) goto cleanup; if (realm != NULL) { *realm = iah->target_realm; iah->target_realm.data = NULL; } if (cookie != NULL) { *cookie = iah->cookie; iah->cookie = NULL; } request->data = (char *)ptr; request->length = bodysize; assert(request->data + request->length == (char *)token->value + token->length); cleanup: krb5_free_iakerb_header(ctx->k5c, iah); return code; }
/* * spkm3_read_token() * * only SPKM_MIC_TOK with md5 intg-alg is supported */ u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, /* checksum */ struct xdr_buf *message_buffer, /* signbuf */ int toktype) { s32 code; struct xdr_netobj wire_cksum = {.len =0, .data = NULL}; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; unsigned char *ptr = (unsigned char *)read_token->data; unsigned char *cksum; int bodysize, md5elen; int mic_hdrlen; u32 ret = GSS_S_DEFECTIVE_TOKEN; dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len); if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, &bodysize, &ptr, read_token->len)) goto out; /* decode the token */ if (toktype == SPKM_MIC_TOK) { if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum))) goto out; if (*cksum++ != 0x03) { dprintk("RPC: spkm3_read_token BAD checksum type\n"); goto out; } md5elen = *cksum++; cksum++; /* move past the zbit */ if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16)) goto out; /* HARD CODED FOR MD5 */ /* compute the checksum of the message. * ptr + 2 = start of header piece of checksum * mic_hdrlen + 2 = length of header piece of checksum */ ret = GSS_S_DEFECTIVE_TOKEN; code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2, mic_hdrlen + 2, message_buffer, 0, &md5cksum); if (code) goto out; dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n", wire_cksum.len); dprintk(" md5cksum.data\n"); print_hexl((u32 *) md5cksum.data, 16, 0); dprintk(" cksum.data:\n"); print_hexl((u32 *) wire_cksum.data, wire_cksum.len, 0); ret = GSS_S_BAD_SIG; code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len); if (code) goto out; } else { dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype); goto out; } /* XXX: need to add expiration and sequencing */ ret = GSS_S_COMPLETE; out: kfree(md5cksum.data); kfree(wire_cksum.data); return ret; }
/* * spkm3_read_token() * * only SPKM_MIC_TOK with md5 intg-alg is supported */ u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, /* checksum */ struct xdr_buf *message_buffer, /* signbuf */ int toktype) { s32 checksum_type; s32 code; struct xdr_netobj wire_cksum = {.len =0, .data = NULL}; char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; unsigned char *ptr = (unsigned char *)read_token->data; unsigned char *cksum; int bodysize, md5elen; int mic_hdrlen; u32 ret = GSS_S_DEFECTIVE_TOKEN; if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, &bodysize, &ptr, read_token->len)) goto out; /* decode the token */ if (toktype != SPKM_MIC_TOK) { dprintk("RPC: BAD SPKM3 token type: %d\n", toktype); goto out; } if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum))) goto out; if (*cksum++ != 0x03) { dprintk("RPC: spkm3_read_token BAD checksum type\n"); goto out; } md5elen = *cksum++; cksum++; /* move past the zbit */ if (!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16)) goto out; /* HARD CODED FOR MD5 */ /* compute the checksum of the message. * ptr + 2 = start of header piece of checksum * mic_hdrlen + 2 = length of header piece of checksum */ ret = GSS_S_DEFECTIVE_TOKEN; if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) { dprintk("RPC: gss_spkm3_seal: unsupported I-ALG " "algorithm\n"); goto out; } checksum_type = CKSUMTYPE_HMAC_MD5; code = make_spkm3_checksum(checksum_type, &ctx->derived_integ_key, ptr + 2, mic_hdrlen + 2, message_buffer, 0, &md5cksum); if (code) goto out; ret = GSS_S_BAD_SIG; code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len); if (code) { dprintk("RPC: bad MIC checksum\n"); goto out; } ret = GSS_S_COMPLETE; out: kfree(wire_cksum.data); return ret; }
u32 krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *read_token, struct xdr_netobj *message_buffer, int *qop_state, int toktype) { s32 code; int tmsglen = 0; int conflen = 0; int signalg; int sealalg; struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; struct xdr_netobj cksum; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; struct xdr_netobj plaind; char *data_ptr; s32 now; unsigned char *plain = NULL; int cksum_len = 0; int plainlen = 0; int direction; s32 seqnum; unsigned char *ptr = (unsigned char *)read_token->data; int bodysize; u32 ret = GSS_S_DEFECTIVE_TOKEN; dprintk("RPC: krb5_read_token\n"); if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; if (toktype == KG_TOK_WRAP_MSG) { message_buffer->len = 0; message_buffer->data = NULL; } /* get the sign and seal algorithms */ signalg = ptr[0] + (ptr[1] << 8); sealalg = ptr[2] + (ptr[3] << 8); /* Sanity checks */ if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) goto out; if (((toktype != KG_TOK_WRAP_MSG) && (sealalg != 0xffff)) || ((toktype == KG_TOK_WRAP_MSG) && (sealalg == 0xffff))) goto out; /* in the current spec, there is only one valid seal algorithm per key type, so a simple comparison is ok */ if ((toktype == KG_TOK_WRAP_MSG) && !(sealalg == ctx->sealalg)) goto out; /* there are several mappings of seal algorithms to sign algorithms, but few enough that we can try them all. */ if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || (ctx->sealalg == SEAL_ALG_DES3KD && signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; /* starting with a single alg */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: cksum_len = 8; break; default: goto out; } if (toktype == KG_TOK_WRAP_MSG) tmsglen = bodysize - (14 + cksum_len); /* get the token parameters */ /* decode the message, if WRAP */ if (toktype == KG_TOK_WRAP_MSG) { dprintk("RPC: krb5_read_token KG_TOK_WRAP_MSG\n"); plain = kmalloc(tmsglen, GFP_KERNEL); ret = GSS_S_FAILURE; if (plain == NULL) goto out; code = krb5_decrypt(ctx->enc, NULL, ptr + 14 + cksum_len, plain, tmsglen); if (code) goto out; plainlen = tmsglen; conflen = crypto_tfm_alg_blocksize(ctx->enc); token.len = tmsglen - conflen - plain[tmsglen - 1]; if (token.len) { token.data = kmalloc(token.len, GFP_KERNEL); if (token.data == NULL) goto out; memcpy(token.data, plain + conflen, token.len); } } else if (toktype == KG_TOK_MIC_MSG) { dprintk("RPC: krb5_read_token KG_TOK_MIC_MSG\n"); token = *message_buffer; plain = token.data; plainlen = token.len; } else { token.len = 0; token.data = NULL; plain = token.data; plainlen = token.len; } dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, plainlen); /* compute the checksum of the message */ /* initialize the the cksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: checksum_type = CKSUMTYPE_RSA_MD5; break; default: ret = GSS_S_DEFECTIVE_TOKEN; goto out; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: dprintk("RPC krb5_read_token SGN_ALG_DES_MAC_MD5\n"); /* compute the checksum of the message. * 8 = bytes of token body to be checksummed according to spec */ data_ptr = kmalloc(8 + plainlen, GFP_KERNEL); ret = GSS_S_FAILURE; if (!data_ptr) goto out; memcpy(data_ptr, ptr - 2, 8); memcpy(data_ptr + 8, plain, plainlen); plaind.len = 8 + plainlen; plaind.data = data_ptr; code = krb5_make_checksum(checksum_type, &plaind, &md5cksum); kfree(data_ptr); if (code) goto out; code = krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, 16); if (code) goto out; if (signalg == 0) cksum.len = 8; else cksum.len = 16; cksum.data = md5cksum.data + 16 - cksum.len; dprintk ("RPC: krb5_read_token: memcmp digest cksum.len %d:\n", cksum.len); dprintk(" md5cksum.data\n"); print_hexl((u32 *) md5cksum.data, 16, 0); dprintk(" cksum.data:\n"); print_hexl((u32 *) cksum.data, cksum.len, 0); { u32 *p; (u8 *) p = ptr + 14; dprintk(" ptr+14:\n"); print_hexl(p, cksum.len, 0); } code = memcmp(cksum.data, ptr + 14, cksum.len); break; default: ret = GSS_S_DEFECTIVE_TOKEN; goto out; } ret = GSS_S_BAD_SIG; if (code) goto out; /* it got through unscathed. Make sure the context is unexpired */ if (toktype == KG_TOK_WRAP_MSG) *message_buffer = token; if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; now = jiffies; ret = GSS_S_CONTEXT_EXPIRED; if (now > ctx->endtime) goto out; /* do sequencing checks */ ret = GSS_S_BAD_SIG; if ((code = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum))) goto out; if ((ctx->initiate && direction != 0xff) || (!ctx->initiate && direction != 0)) goto out; ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); if (toktype == KG_TOK_WRAP_MSG) { if (plain) kfree(plain); if (ret && token.data) kfree(token.data); } return ret; }