From ad6ab2ec4aaa7e816649126de09f99630f417738 Mon Sep 17 00:00:00 2001 From: Marc Horowitz Date: Wed, 5 Aug 1998 06:04:55 +0000 Subject: [PATCH] add code to implement a new krb5 v2 gssapi mechanism. this implementation is complete and functional, but the draft spec and the code do not yet completely match. git-svn-id: svn://anonsvn.mit.edu/krb5/branches/marc-3des@10776 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/gssapi/krb5/accept_sec_context.c | 935 +++++++++++++++-------- src/lib/gssapi/krb5/acquire_cred.c | 53 +- src/lib/gssapi/krb5/canon_name.c | 17 +- src/lib/gssapi/krb5/delete_sec_context.c | 3 + src/lib/gssapi/krb5/disp_status.c | 10 +- src/lib/gssapi/krb5/gssapiP_krb5.h | 43 +- src/lib/gssapi/krb5/gssapi_err_krb5.et | 1 + src/lib/gssapi/krb5/gssapi_krb5.c | 13 + src/lib/gssapi/krb5/gssapi_krb5.h | 3 + src/lib/gssapi/krb5/init_sec_context.c | 572 +++++++++++--- src/lib/gssapi/krb5/inq_cred.c | 27 +- src/lib/gssapi/krb5/inq_names.c | 3 +- src/lib/gssapi/krb5/k5seal.c | 269 ++++++- src/lib/gssapi/krb5/k5unseal.c | 495 +++++++++++- src/lib/gssapi/krb5/rel_oid.c | 3 +- src/lib/gssapi/krb5/ser_sctx.c | 38 +- src/lib/gssapi/krb5/util_ctxsetup.c | 182 +++++ src/lib/gssapi/krb5/wrap_size_limit.c | 82 +- 18 files changed, 2234 insertions(+), 515 deletions(-) create mode 100644 src/lib/gssapi/krb5/util_ctxsetup.c diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index 28f4bac6b0..d293220334 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -58,9 +58,8 @@ rd_req_keyproc(krb5_pointer keyprocarg, krb5_principal server, /* Decode, decrypt and store the forwarded creds in the local ccache. */ static krb5_error_code -rd_and_store_for_creds(context, auth_context, inbuf, out_cred) +rd_and_store_for_creds(context, inbuf, out_cred) krb5_context context; - krb5_auth_context auth_context; krb5_data *inbuf; krb5_gss_cred_id_t *out_cred; { @@ -69,10 +68,16 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred) krb5_ccache ccache; krb5_gss_cred_id_t cred = NULL; extern krb5_cc_ops krb5_mcc_ops; + krb5_auth_context auth_context = NULL; - if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) + if ((retval = krb5_auth_con_init(context, &auth_context))) return(retval); + krb5_auth_con_setflags(context, auth_context, 0); + + if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) + goto cleanup; + /* Lots of kludging going on here... Some day the ccache interface will be rewritten though */ @@ -91,47 +96,51 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred) /* generate a delegated credential handle */ if (out_cred) { - /* allocate memory for a cred_t... */ - if (!(cred = - (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) { - retval = ENOMEM; /* out of memory? */ - goto cleanup; - } + /* allocate memory for a cred_t... */ + if (!(cred = + (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) { + retval = ENOMEM; /* out of memory? */ + goto cleanup; + } - /* zero it out... */ - memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); + /* zero it out... */ + memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); - /* copy the client principle into it... */ - if ((retval = - krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) { - retval = ENOMEM; /* out of memory? */ - xfree(cred); /* clean up memory on failure */ - cred = NULL; - goto cleanup; - } - - cred->usage = GSS_C_INITIATE; /* we can't accept with this */ - /* cred->princ already set */ - cred->actual_mechs = gss_mech_set_krb5_both; /* both mechs work */ - cred->prerfc_mech = cred->rfc_mech = 1; /* Ibid. */ - cred->keytab = NULL; /* no keytab associated with this... */ - cred->ccache = ccache; /* but there is a credential cache */ - cred->tgt_expire = creds[0]->times.endtime; /* store the end time */ + /* copy the client principle into it... */ + if ((retval = + krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) { + retval = ENOMEM; /* out of memory? */ + xfree(cred); /* clean up memory on failure */ + cred = NULL; + goto cleanup; + } + + cred->usage = GSS_C_INITIATE; /* we can't accept with this */ + /* cred->princ already set */ + cred->prerfc_mech = 1; /* this cred will work with all three mechs */ + cred->rfc_mech = 1; + cred->rfcv2_mech = 1; + cred->keytab = NULL; /* no keytab associated with this... */ + cred->ccache = ccache; /* but there is a credential cache */ + cred->tgt_expire = creds[0]->times.endtime; /* store the end time */ } /* If there were errors, there might have been a memory leak - if (!cred) - if ((retval = krb5_cc_close(context, ccache))) - goto cleanup; - */ + if (!cred) + if ((retval = krb5_cc_close(context, ccache))) + goto cleanup; + */ cleanup: krb5_free_tgt_creds(context, creds); if (!cred && ccache) - (void)krb5_cc_close(context, ccache); + (void)krb5_cc_close(context, ccache); if (out_cred) - *out_cred = cred; /* return credential */ + *out_cred = cred; /* return credential */ + + if (auth_context) + krb5_auth_con_free(context, auth_context); return retval; } @@ -161,14 +170,14 @@ krb5_gss_accept_sec_context(minor_status, context_handle, size_t md5len; int bigend; krb5_gss_cred_id_t cred = 0; - krb5_data ap_req; + krb5_data ap_rep, ap_req, mic; int i; krb5_error_code code; krb5_address addr, *paddr; krb5_authenticator *authdat = 0; - krb5_checksum md5; + krb5_checksum reqcksum; krb5_principal name = NULL; - int gss_flags = 0; + krb5_ui_4 gss_flags = 0; int decode_req_message = 0; krb5_gss_ctx_id_rec *ctx = 0; krb5_enctype enctype; @@ -178,14 +187,18 @@ krb5_gss_accept_sec_context(minor_status, context_handle, krb5_auth_context auth_context = NULL; krb5_ticket * ticket = NULL; int option_id; - krb5_data option; - krb5_auth_context auth_context_cred = NULL; + krb5_data option, cksumdata; const gss_OID_desc *mech_used = NULL; OM_uint32 major_status = GSS_S_FAILURE; krb5_error krb_error_data; krb5_data scratch; gss_cred_id_t cred_handle = NULL; krb5_gss_cred_id_t deleg_cred = NULL; + int token_length; + int gsskrb5_vers; + int nctypes; + krb5_cksumtype *ctypes; + struct kg2_option fwcred; if (GSS_ERROR(kg_get_context(minor_status, &context))) return(GSS_S_FAILURE); @@ -197,7 +210,10 @@ krb5_gss_accept_sec_context(minor_status, context_handle, output_token->length = 0; output_token->value = NULL; token.value = 0; - md5.contents = 0; + reqcksum.contents = 0; + mic.data = 0; + ap_req.data = 0; + ap_rep.data = 0; if (mech_type) *mech_type = GSS_C_NULL_OID; @@ -205,12 +221,6 @@ krb5_gss_accept_sec_context(minor_status, context_handle, if (delegated_cred_handle) *delegated_cred_handle = GSS_C_NO_CREDENTIAL; - /* stash this now, for later. */ - if (code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len)) { - *minor_status = code; - return(GSS_S_FAILURE); - } - /* * Context handle must be unspecified. Actually, it must be * non-established, but currently, accept_sec_context never returns @@ -224,18 +234,19 @@ krb5_gss_accept_sec_context(minor_status, context_handle, /* handle default cred handle */ if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) { - major_status = krb5_gss_acquire_cred(&code, GSS_C_NO_NAME, - GSS_C_INDEFINITE, GSS_C_NO_OID_SET, - GSS_C_ACCEPT, &cred_handle, - NULL, NULL); - if (major_status != GSS_S_COMPLETE) - goto fail; - } else - cred_handle = verifier_cred_handle; + major_status = krb5_gss_acquire_cred(&code, GSS_C_NO_NAME, + GSS_C_INDEFINITE, GSS_C_NO_OID_SET, + GSS_C_ACCEPT, &cred_handle, + NULL, NULL); + if (major_status != GSS_S_COMPLETE) + goto fail; + } else { + cred_handle = verifier_cred_handle; + } - major_status = krb5_gss_validate_cred(minor_status, verifier_cred_handle); + major_status = krb5_gss_validate_cred(&code, verifier_cred_handle); if (GSS_ERROR(major_status)) - goto fail; + goto fail; cred = (krb5_gss_cred_id_t) cred_handle; @@ -243,9 +254,9 @@ krb5_gss_accept_sec_context(minor_status, context_handle, if ((cred->usage != GSS_C_ACCEPT) && (cred->usage != GSS_C_BOTH)) { - code = 0; - major_status = GSS_S_NO_CRED; - goto fail; + code = 0; + major_status = GSS_S_NO_CRED; + goto fail; } /* verify the token's integrity, and leave the token in ap_req. @@ -253,60 +264,98 @@ krb5_gss_accept_sec_context(minor_status, context_handle, ptr = (unsigned char *) input_token->value; - if ((err = g_verify_token_header((gss_OID) gss_mech_krb5, &(ap_req.length), - &ptr, KG_TOK_CTX_AP_REQ, - input_token->length))) { - /* - * Previous versions of this library used the old mech_id - * and some broken behavior (wrong IV on checksum - * encryption). We support the old mech_id for - * compatibility, and use it to decide when to use the - * old behavior. - */ - if (err != G_WRONG_MECH || - (code = g_verify_token_header((gss_OID) gss_mech_krb5_old, - &(ap_req.length), - &ptr, KG_TOK_CTX_AP_REQ, - input_token->length))) { - major_status = GSS_S_DEFECTIVE_TOKEN; - goto fail; - } else { -#if 0 /* Don't restrict mechanisms when accepting contexts */ - if (! cred->prerfc_mech) { - code = G_WRONG_MECH; - major_status = GSS_S_DEFECTIVE_TOKEN; - goto fail; - } -#endif - mech_used = gss_mech_krb5_old; - } - } else { + if (!(code = g_verify_token_header((gss_OID) gss_mech_krb5, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length))) { #if 0 /* Don't restrict mechanisms when accepting contexts */ - if (! cred->rfc_mech) { - code = G_WRONG_MECH; - major_status = GSS_S_DEFECTIVE_TOKEN; - goto fail; - } + if (! cred->rfc_mech) { + code = G_WRONG_MECH; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } +#endif + mech_used = gss_mech_krb5; + gsskrb5_vers = 1000; + } else if ((code == G_WRONG_MECH) && + !(code = g_verify_token_header((gss_OID) gss_mech_krb5_old, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length))) { + /* + * Previous versions of this library used the old mech_id + * and some broken behavior (wrong IV on checksum + * encryption). We support the old mech_id for + * compatibility, and use it to decide when to use the + * old behavior. + */ +#if 0 /* Don't restrict mechanisms when accepting contexts */ + if (! cred->prerfc_mech) { + code = G_WRONG_MECH; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } +#endif + mech_used = gss_mech_krb5_old; + gsskrb5_vers = 1000; + } else if ((code == G_WRONG_MECH) && + !(code = g_verify_token_header((gss_OID) gss_mech_krb5_v2, + &token_length, + &ptr, KG2_TOK_INITIAL, + input_token->length))) { +#if 0 /* Don't restrict mechanisms when accepting contexts */ + if (! cred->rfcv2_mech) { + code = G_WRONG_MECH; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } #endif - mech_used = gss_mech_krb5; + mech_used = gss_mech_krb5_v2; + gsskrb5_vers = 2000; + } else { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; } - sptr = (char *) ptr; - TREAD_STR(sptr, ap_req.data, ap_req.length); - decode_req_message = 1; + if (gsskrb5_vers == 2000) { + /* gss krb5 v2 */ + + fwcred.option_id = KRB5_GSS_FOR_CREDS_OPTION; + fwcred.data = NULL; + + if (GSS_ERROR(major_status = + kg2_parse_token(&code, ptr, token_length, + &gss_flags, &nctypes, &ctypes, + delegated_cred_handle?1:0, + &fwcred, &ap_req, NULL))) { + goto fail; + } + + gss_flags = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]; + + gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; + if there's a delegation, we'll + set it below */ + } else { + /* gss krb5 v1 */ + + sptr = (char *) ptr; + TREAD_STR(sptr, ap_req.data, ap_req.length); + decode_req_message = 1; + } /* construct the sender_addr */ if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) && (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) { - /* XXX is this right? */ - addr.addrtype = ADDRTYPE_INET; - addr.length = input_chan_bindings->initiator_address.length; - addr.contents = input_chan_bindings->initiator_address.value; + /* XXX is this right? */ + addr.addrtype = ADDRTYPE_INET; + addr.length = input_chan_bindings->initiator_address.length; + addr.contents = input_chan_bindings->initiator_address.value; - paddr = &addr; + paddr = &addr; } else { - paddr = NULL; + paddr = NULL; } /* decode the AP_REQ message */ @@ -314,13 +363,18 @@ krb5_gss_accept_sec_context(minor_status, context_handle, /* decode the message */ if ((code = krb5_auth_con_init(context, &auth_context))) { - *minor_status = code; - return(GSS_S_FAILURE); + major_status = GSS_S_FAILURE; + goto fail; } if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) { - *minor_status = code; - return(GSS_S_FAILURE); + major_status = GSS_S_FAILURE; + goto fail; } + if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = krb5_rd_req(context, &auth_context, &ap_req, cred->princ, cred->keytab, NULL, &ticket))) { major_status = GSS_S_FAILURE; @@ -335,137 +389,142 @@ krb5_gss_accept_sec_context(minor_status, context_handle, if ((authdat->authenticator->subkey == NULL) || (authdat->ticket->enc_part2 == NULL)) { code = KG_NO_SUBKEY; + major_status = GSS_S_FAILURE; goto fail; } #endif - /* verify that the checksum is correct */ + if (gsskrb5_vers == 2000) { + bigend = 1; + } else { + /* gss krb5 v1 */ + + /* stash this now, for later. */ + if (code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, + &md5len)) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* verify that the checksum is correct */ - /* - The checksum may be either exactly 24 bytes, in which case - no options are specified, or greater than 24 bytes, in which case - one or more options are specified. Currently, the only valid - option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ). - */ - - if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) || - (authdat->checksum->length < 24)) { + /* + The checksum may be either exactly 24 bytes, in which case + no options are specified, or greater than 24 bytes, in which case + one or more options are specified. Currently, the only valid + option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ). + */ + + if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) || + (authdat->checksum->length < 24)) { code = 0; major_status = GSS_S_BAD_BINDINGS; goto fail; - } - - /* - "Be liberal in what you accept, and - conservative in what you send" - -- rfc1123 - - This code will let this acceptor interoperate with an initiator - using little-endian or big-endian integer encoding. - */ + } - ptr = (unsigned char *) authdat->checksum->contents; - bigend = 0; + /* + "Be liberal in what you accept, and + conservative in what you send" + -- rfc1123 - TREAD_INT(ptr, tmp, bigend); + This code will let this acceptor interoperate with an initiator + using little-endian or big-endian integer encoding. + */ - if (tmp != md5len) { ptr = (unsigned char *) authdat->checksum->contents; - bigend = 1; + bigend = 0; TREAD_INT(ptr, tmp, bigend); if (tmp != md5len) { - major_status = GSS_S_FAILURE; - code = KG_BAD_LENGTH; - goto fail; + ptr = (unsigned char *) authdat->checksum->contents; + bigend = 1; + + TREAD_INT(ptr, tmp, bigend); + + if (tmp != md5len) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } } - } - /* at this point, bigend is set according to the initiator's byte order */ + /* at this point, bigend is set according to the initiator's + byte order */ - if ((code = kg_checksum_channel_bindings(context, input_chan_bindings, &md5, - bigend))) { - major_status = GSS_S_BAD_BINDINGS; - goto fail; - } + if ((code = kg_checksum_channel_bindings(context, input_chan_bindings, + &reqcksum, bigend))) { + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } - TREAD_STR(ptr, ptr2, md5.length); - if (memcmp(ptr2, md5.contents, md5.length) != 0) { + TREAD_STR(ptr, ptr2, reqcksum.length); + if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) { code = 0; major_status = GSS_S_BAD_BINDINGS; goto fail; - } - - xfree(md5.contents); - md5.contents = 0; - - TREAD_INT(ptr, gss_flags, bigend); - gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if there's - a delegation, we'll set it below */ - decode_req_message = 0; - - /* if the checksum length > 24, there are options to process */ + } - if(authdat->checksum->length > 24) { + xfree(reqcksum.contents); + reqcksum.contents = 0; - i = authdat->checksum->length - 24; + TREAD_INT(ptr, gss_flags, bigend); + gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if + there's a delegation, we'll set + it below */ + decode_req_message = 0; - while(i>0) { + /* if the checksum length > 24, there are options to process */ - TREAD_INT16(ptr, option_id, bigend); + if(authdat->checksum->length > 24) { - switch(option_id) { + i = authdat->checksum->length - 24; - case KRB5_GSS_FOR_CREDS_OPTION: + while(i>0) { - TREAD_INT16(ptr, option.length, bigend); + TREAD_INT16(ptr, option_id, bigend); - /* have to use ptr2, since option.data is wrong type and - macro uses ptr as both lvalue and rvalue */ + switch(option_id) { - TREAD_STR(ptr, ptr2, bigend); - option.data = (char FAR *) ptr2; + case KRB5_GSS_FOR_CREDS_OPTION: - /* get a temporary auth_context structure for the - call to rd_and_store_for_creds() and clear its flags */ + TREAD_INT16(ptr, option.length, bigend); - if ((code = krb5_auth_con_init(context, - &auth_context_cred))) { - major_status = GSS_S_FAILURE; - goto fail; - } + /* have to use ptr2, since option.data is wrong type and + macro uses ptr as both lvalue and rvalue */ - krb5_auth_con_setflags(context, auth_context_cred, 0); + TREAD_STR(ptr, ptr2, bigend); + option.data = (char FAR *) ptr2; - /* store the delegated credential */ + /* store the delegated credential */ - rd_and_store_for_creds(context, auth_context_cred, - &option, - (delegated_cred_handle) ? - &deleg_cred : NULL); + if (code = rd_and_store_for_creds(context, &option, + (delegated_cred_handle) ? + &deleg_cred : NULL)) { + major_status = GSS_S_FAILURE; + goto fail; + } - i -= option.length + 4; + i -= option.length + 4; - krb5_auth_con_free(context, auth_context_cred); + gss_flags |= GSS_C_DELEG_FLAG; /* got a delegation */ - gss_flags |= GSS_C_DELEG_FLAG; /* got a delegation */ + break; - break; + /* default: */ + /* unknown options aren't an error */ - /* default: */ - /* unknown options aren't an error */ + } /* switch */ + } /* while */ + } /* if */ + } - } /* switch */ - } /* while */ - } /* if */ - /* create the ctx struct and start filling it in */ if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) == NULL) { - major_status = GSS_S_FAILURE; code = ENOMEM; + major_status = GSS_S_FAILURE; goto fail; } @@ -476,78 +535,174 @@ krb5_gss_accept_sec_context(minor_status, context_handle, ctx->gss_flags = KG_IMPLFLAGS(gss_flags); ctx->seed_init = 0; ctx->big_endian = bigend; - - major_status = GSS_S_FAILURE; + ctx->gsskrb5_version = gsskrb5_vers; /* Intern the ctx pointer so that delete_sec_context works */ if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) { - code = G_VALIDATE_FAILED; - xfree(ctx); - ctx = 0; - goto fail; + xfree(ctx); + ctx = 0; + + code = G_VALIDATE_FAILED; + major_status = GSS_S_FAILURE; + goto fail; } - - if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) - goto fail; - if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) - goto fail; + if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) { + major_status = GSS_S_FAILURE; + goto fail; + } - /* done with authdat */ - krb5_free_authenticator(context, authdat); - authdat = 0; + if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) { + major_status = GSS_S_FAILURE; + goto fail; + } if ((code = krb5_auth_con_getremotesubkey(context, auth_context, - &ctx->subkey))) - goto fail; + &ctx->subkey))) { + major_status = GSS_S_FAILURE; + goto fail; + } /* use the session key if the subkey isn't present */ if (ctx->subkey == NULL) { if ((code = krb5_auth_con_getkey(context, auth_context, - &ctx->subkey))) - goto fail; + &ctx->subkey))) { + major_status = GSS_S_FAILURE; + goto fail; + } } if (ctx->subkey == NULL) { /* this isn't a very good error, but it's not clear to me this can actually happen */ + major_status = GSS_S_FAILURE; code = KRB5KDC_ERR_NULL_KEY; goto fail; } - switch(ctx->subkey->enctype) { - case ENCTYPE_DES_CBC_MD5: - case ENCTYPE_DES_CBC_CRC: - enctype = ENCTYPE_DES_CBC_RAW; - ctx->signalg = 0; - ctx->cksum_size = 8; - ctx->sealalg = 0; - break; + if (gsskrb5_vers == 2000) { + int cblen; + krb5_boolean valid; + + /* intersect the token ctypes with the local ctypes */ + + if (code = krb5_c_keyed_checksum_types(context, ctx->subkey->enctype, + &ctx->nctypes, &ctx->ctypes)) + goto fail; + + if (nctypes == 0) { + code = KRB5_CRYPTO_INTERNAL; + goto fail; + } + + kg2_intersect_ctypes(&ctx->nctypes, ctx->ctypes, nctypes, ctypes); + + if (nctypes == 0) { + code = KG_NO_CTYPES; + goto fail; + } + + /* process the delegated cred, if any */ + + if (fwcred.data) { + krb5_data option; + + option.length = fwcred.length; + option.data = fwcred.data; + + if (code = rd_and_store_for_creds(context, &option, &deleg_cred)) { + major_status = GSS_S_FAILURE; + goto fail; + } + + gss_flags |= GSS_C_DELEG_FLAG; /* got a delegation */ + } + + /* construct the checksum buffer */ + + cblen = sizeof(krb5_ui_2)*5; + if (input_chan_bindings) + cblen += (input_chan_bindings->initiator_address.length+ + input_chan_bindings->acceptor_address.length+ + input_chan_bindings->application_data.length); + + cksumdata.length = cblen + ((char *)(ap_req.data-2) - (char *)(ptr-2)); + + if ((cksumdata.data = (char *) malloc(cksumdata.length)) == NULL) { + code = ENOMEM; + major_status = GSS_S_FAILURE; + goto fail; + } + + ptr2 = cksumdata.data; + + if (input_chan_bindings) { + TWRITE_INT(ptr2, input_chan_bindings->initiator_addrtype, 1); + TWRITE_BUF(ptr2, input_chan_bindings->initiator_address, 1); + TWRITE_INT(ptr2, input_chan_bindings->acceptor_addrtype, 1); + TWRITE_BUF(ptr2, input_chan_bindings->acceptor_address, 1); + TWRITE_BUF(ptr2, input_chan_bindings->application_data, 1); + } else { + memset(ptr2, 0, cblen); + ptr2 += cblen; + } + + memcpy(ptr2, ptr-2, ((char *)(ap_req.data-2) - (char *)(ptr-2))); + + if (code = krb5_c_verify_checksum(context, ctx->subkey, + KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, + &cksumdata, authdat->checksum, + &valid)) { + major_status = GSS_S_FAILURE; + goto fail; + } + + if (!valid) { + code = 0; + major_status = GSS_S_BAD_SIG; + goto fail; + } + } else { + /* gss krb5 v1 */ + + switch(ctx->subkey->enctype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_CRC: + enctype = ENCTYPE_DES_CBC_RAW; + ctx->signalg = 0; + ctx->cksum_size = 8; + ctx->sealalg = 0; + break; #if 0 - case ENCTYPE_DES3_CBC_MD5: - enctype = ENCTYPE_DES3_CBC_RAW; - ctx->signalg = 3; - ctx->cksum_size = 16; - ctx->sealalg = 1; - break; + case ENCTYPE_DES3_CBC_MD5: + enctype = ENCTYPE_DES3_CBC_RAW; + ctx->signalg = 3; + ctx->cksum_size = 16; + ctx->sealalg = 1; + break; #endif - default: - code = KRB5_BAD_ENCTYPE; - goto fail; - } + default: + code = KRB5_BAD_ENCTYPE; + goto fail; + } - /* fill in the encryption descriptors */ + /* fill in the encryption descriptors */ - if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) + if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) { + major_status = GSS_S_FAILURE; goto fail; + } - for (i=0; ienc->length; i++) - /*SUPPRESS 113*/ - ctx->enc->contents[i] ^= 0xf0; + for (i=0; ienc->length; i++) + /*SUPPRESS 113*/ + ctx->enc->contents[i] ^= 0xf0; - if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) + if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) { + major_status = GSS_S_FAILURE; goto fail; + } + } ctx->endtime = ticket->enc_part2->times.endtime; ctx->krb_flags = ticket->enc_part2->flags; @@ -556,13 +711,15 @@ krb5_gss_accept_sec_context(minor_status, context_handle, krb5_auth_con_getremoteseqnumber(context, auth_context, &ctx->seq_recv); - if ((code = krb5_timeofday(context, &now))) - goto fail; + if ((code = krb5_timeofday(context, &now))) { + major_status = GSS_S_FAILURE; + goto fail; + } if (ctx->endtime < now) { - code = 0; - major_status = GSS_S_CREDENTIALS_EXPIRED; - goto fail; + code = 0; + major_status = GSS_S_CREDENTIALS_EXPIRED; + goto fail; } g_order_init(&(ctx->seqstate), ctx->seq_recv, @@ -575,40 +732,153 @@ krb5_gss_accept_sec_context(minor_status, context_handle, /* generate an AP_REP if necessary */ if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { - krb5_data ap_rep; - unsigned char * ptr; - if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) - goto fail; - - krb5_auth_con_getlocalseqnumber(context, auth_context, &ctx->seq_send); - token.length = g_token_size((gss_OID) mech_used, ap_rep.length); - - if ((token.value = (unsigned char *) xmalloc(token.length)) == NULL) { - code = ENOMEM; - goto fail; - } - ptr = token.value; - g_make_token_header((gss_OID) mech_used, ap_rep.length, - &ptr, KG_TOK_CTX_AP_REP); - - TWRITE_STR(ptr, ap_rep.data, ap_rep.length); - xfree(ap_rep.data); + unsigned char * ptr; + if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + krb5_auth_con_getlocalseqnumber(context, auth_context, + &ctx->seq_send); + + /* the reply token hasn't been sent yet, but that's ok. */ + ctx->established = 1; + + if (ctx->gsskrb5_version == 2000) { + krb5_ui_4 tok_flags; + + tok_flags = + (ctx->gss_flags & GSS_C_DELEG_FLAG)?KG2_RESP_FLAG_DELEG_OK:0; + + cksumdata.length = 8 + 4*ctx->nctypes + 4; + + if ((cksumdata.data = (char *) malloc(cksumdata.length)) == NULL) { + code = ENOMEM; + major_status = GSS_S_FAILURE; + goto fail; + } + + /* construct the token fields */ + + ptr = cksumdata.data; + + ptr[0] = (KG2_TOK_RESPONSE >> 8) & 0xff; + ptr[1] = KG2_TOK_RESPONSE & 0xff; + + ptr[2] = (tok_flags >> 24) & 0xff; + ptr[3] = (tok_flags >> 16) & 0xff; + ptr[4] = (tok_flags >> 8) & 0xff; + ptr[5] = tok_flags & 0xff; + + ptr[6] = (ctx->nctypes >> 8) & 0xff; + ptr[7] = ctx->nctypes & 0xff; + + ptr += 8; + + for (i=0; inctypes; i++) { + ptr[i] = (ctx->ctypes[i] >> 24) & 0xff; + ptr[i+1] = (ctx->ctypes[i] >> 16) & 0xff; + ptr[i+2] = (ctx->ctypes[i] >> 8) & 0xff; + ptr[i+3] = ctx->ctypes[i] & 0xff; + + ptr += 4; + } + + memset(ptr, 0, 4); + + /* make the MIC token */ + + { + gss_buffer_desc text, token; + + text.length = cksumdata.length; + text.value = cksumdata.data; + + /* ctx->seq_send must be set before this call */ + + if (GSS_ERROR(major_status = + krb5_gss_get_mic(&code, ctx, + GSS_C_QOP_DEFAULT, + &text, &token))) + goto fail; + + mic.length = token.length; + mic.data = token.value; + } + + token.length = g_token_size((gss_OID) mech_used, + (cksumdata.length-2)+4+ap_rep.length+ + mic.length); + + if ((token.value = (unsigned char *) xmalloc(token.length)) + == NULL) { + code = ENOMEM; + major_status = GSS_S_FAILURE; + goto fail; + } + ptr = token.value; + g_make_token_header((gss_OID) mech_used, + (cksumdata.length-2)+4+ap_rep.length+mic.length, + &ptr, KG2_TOK_RESPONSE); + + memcpy(ptr, cksumdata.data+2, cksumdata.length-2); + ptr += cksumdata.length-2; + + ptr[0] = (ap_rep.length >> 8) & 0xff; + ptr[1] = ap_rep.length & 0xff; + memcpy(ptr+2, ap_rep.data, ap_rep.length); + + ptr += (2+ap_rep.length); + + ptr[0] = (mic.length >> 8) & 0xff; + ptr[1] = mic.length & 0xff; + memcpy(ptr+2, mic.data, mic.length); + + ptr += (2+mic.length); + + /* gss krb5 v2 */ + } else { + /* gss krb5 v1 */ + + token.length = g_token_size((gss_OID) mech_used, ap_rep.length); + + if ((token.value = (unsigned char *) xmalloc(token.length)) + == NULL) { + major_status = GSS_S_FAILURE; + code = ENOMEM; + goto fail; + } + ptr = token.value; + g_make_token_header((gss_OID) mech_used, ap_rep.length, + &ptr, KG_TOK_CTX_AP_REP); + + TWRITE_STR(ptr, ap_rep.data, ap_rep.length); + xfree(ap_rep.data); + + ctx->established = 1; + + } } else { - token.length = 0; - token.value = NULL; - ctx->seq_send = ctx->seq_recv; + token.length = 0; + token.value = NULL; + ctx->seq_send = ctx->seq_recv; + + ctx->established = 1; } /* set the return arguments */ if (src_name) { - if ((code = krb5_copy_principal(context, ctx->there, &name))) - goto fail; - /* intern the src_name */ - if (! kg_save_name((gss_name_t) name)) { - code = G_VALIDATE_FAILED; - goto fail; - } + if ((code = krb5_copy_principal(context, ctx->there, &name))) { + major_status = GSS_S_FAILURE; + goto fail; + } + /* intern the src_name */ + if (! kg_save_name((gss_name_t) name)) { + code = G_VALIDATE_FAILED; + major_status = GSS_S_FAILURE; + goto fail; + } } if (mech_type) @@ -620,7 +890,6 @@ krb5_gss_accept_sec_context(minor_status, context_handle, if (ret_flags) *ret_flags = ctx->gss_flags; - ctx->established = 1; *context_handle = ctx; *output_token = token; @@ -628,12 +897,13 @@ krb5_gss_accept_sec_context(minor_status, context_handle, *src_name = (gss_name_t) name; if (delegated_cred_handle && deleg_cred) { - if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) { - code = G_VALIDATE_FAILED; - goto fail; - } + if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) { + major_status = GSS_S_FAILURE; + code = G_VALIDATE_FAILED; + goto fail; + } - *delegated_cred_handle = (gss_cred_id_t) deleg_cred; + *delegated_cred_handle = (gss_cred_id_t) deleg_cred; } /* finally! */ @@ -641,27 +911,33 @@ krb5_gss_accept_sec_context(minor_status, context_handle, *minor_status = 0; return(GSS_S_COMPLETE); -fail: + fail: if (authdat) - krb5_free_authenticator(context, authdat); + krb5_free_authenticator(context, authdat); if (ctx) - (void) krb5_gss_delete_sec_context(minor_status, - (gss_ctx_id_t *) &ctx, NULL); + (void) krb5_gss_delete_sec_context(minor_status, + (gss_ctx_id_t *) &ctx, NULL); if (token.value) - xfree(token.value); + xfree(token.value); if (name) { - (void) kg_delete_name((gss_name_t) name); - krb5_free_principal(context, name); + (void) kg_delete_name((gss_name_t) name); + krb5_free_principal(context, name); } - if (md5.contents) - xfree(md5.contents); + if (reqcksum.contents) + xfree(reqcksum.contents); if (deleg_cred) { /* free memory associated with the deleg credential */ - if (deleg_cred->ccache) - (void)krb5_cc_close(context, deleg_cred->ccache); - if (deleg_cred->princ) - krb5_free_principal(context, deleg_cred->princ); - xfree(deleg_cred); + if (deleg_cred->ccache) + (void)krb5_cc_close(context, deleg_cred->ccache); + if (deleg_cred->princ) + krb5_free_principal(context, deleg_cred->princ); + xfree(deleg_cred); } + if (ap_req.data) + xfree(ap_req.data); + if (ap_rep.data) + xfree(ap_rep.data); + if (mic.data) + xfree(mic.data); *minor_status = code; @@ -672,48 +948,75 @@ fail: * decode the authenticator to read out the gss_flags field. */ if (decode_req_message) { - krb5_ap_req * request; + krb5_ap_req * request; - if (decode_krb5_ap_req(&ap_req, &request)) - return (major_status); - if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) - gss_flags |= GSS_C_MUTUAL_FLAG; - krb5_free_ap_req(context, request); + if (decode_krb5_ap_req(&ap_req, &request)) + return (major_status); + if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) + gss_flags |= GSS_C_MUTUAL_FLAG; + krb5_free_ap_req(context, request); } if (cred && (gss_flags & GSS_C_MUTUAL_FLAG)) { - /* - * The client is expecting a response, so we can send an - * error token back - */ - memset(&krb_error_data, 0, sizeof(krb_error_data)); - - code -= ERROR_TABLE_BASE_krb5; - if (code < 0 || code > 128) - code = 60 /* KRB_ERR_GENERIC */; - - krb_error_data.error = code; - (void) krb5_us_timeofday(context, &krb_error_data.stime, - &krb_error_data.susec); - krb_error_data.server = cred->princ; + int tmsglen, toktype; + + /* + * The client is expecting a response, so we can send an + * error token back + */ + memset(&krb_error_data, 0, sizeof(krb_error_data)); + + code -= ERROR_TABLE_BASE_krb5; + if (code < 0 || code > 128) + code = 60 /* KRB_ERR_GENERIC */; + + krb_error_data.error = code; + (void) krb5_us_timeofday(context, &krb_error_data.stime, + &krb_error_data.susec); + krb_error_data.server = cred->princ; - code = krb5_mk_error(context, &krb_error_data, &scratch); - if (code) - return (major_status); + code = krb5_mk_error(context, &krb_error_data, &scratch); + if (code) + return (major_status); + + if (gsskrb5_vers == 2000) { + tmsglen = 12+scratch.length; + toktype = KG2_TOK_RESPONSE; + } else { + tmsglen = scratch.length; + toktype = KG_TOK_CTX_ERROR; + } - token.length = g_token_size((gss_OID) mech_used, scratch.length); - token.value = (unsigned char *) xmalloc(token.length); - if (!token.value) - return (major_status); + token.length = g_token_size((gss_OID) mech_used, tmsglen); + token.value = (unsigned char *) xmalloc(token.length); + if (!token.value) + return (major_status); - ptr = token.value; - g_make_token_header((gss_OID) mech_used, scratch.length, - &ptr, KG_TOK_CTX_ERROR); + ptr = token.value; + g_make_token_header((gss_OID) mech_used, tmsglen, &ptr, toktype); + + if (gsskrb5_vers == 2000) { + krb5_ui_4 flags; + + flags = KG2_RESP_FLAG_ERROR; + + ptr[0] = (flags << 24) & 0xff; + ptr[1] = (flags << 16) & 0xff; + ptr[2] = (flags << 8) & 0xff; + ptr[3] = flags & 0xff; + + memset(ptr+4, 0, 6); + + ptr[10] = (scratch.length << 8) & 0xff; + ptr[11] = scratch.length & 0xff; + + ptr += 12; + } - TWRITE_STR(ptr, scratch.data, scratch.length); - xfree(scratch.data); + TWRITE_STR(ptr, scratch.data, scratch.length); + xfree(scratch.data); - *output_token = token; + *output_token = token; } if (!verifier_cred_handle && cred_handle) { krb5_gss_release_cred(&code, cred_handle); diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index 1ca1bf31a7..eaf4c0fb6d 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -248,8 +248,7 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, size_t i; krb5_gss_cred_id_t cred; gss_OID_set ret_mechs; - const gss_OID_set_desc FAR * valid_mechs; - int req_old, req_new; + int req_old, req_new, req_v2; OM_uint32 ret; krb5_error_code code; @@ -277,27 +276,24 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, contains krb5 */ if (desired_mechs == GSS_C_NULL_OID_SET) { - valid_mechs = gss_mech_set_krb5_both; req_old = 1; req_new = 1; + req_v2 = 1; } else { req_old = 0; req_new = 0; + req_v2 = 0; for (i=0; icount; i++) { if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i]))) req_old++; if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i]))) req_new++; + if (g_OID_equal(gss_mech_krb5_v2, &(desired_mechs->elements[i]))) + req_v2++; } - if (req_old && req_new) { - valid_mechs = gss_mech_set_krb5_both; - } else if (req_old) { - valid_mechs = gss_mech_set_krb5_old; - } else if (req_new) { - valid_mechs = gss_mech_set_krb5; - } else { + if (!req_old && !req_new && !req_v2) { *minor_status = 0; return(GSS_S_BAD_MECH); } @@ -314,9 +310,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, cred->usage = cred_usage; cred->princ = NULL; - cred->actual_mechs = valid_mechs; cred->prerfc_mech = req_old; cred->rfc_mech = req_new; + cred->rfcv2_mech = req_v2; cred->keytab = NULL; cred->ccache = NULL; @@ -407,17 +403,30 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, /* create mechs */ if (actual_mechs) { - if (! g_copy_OID_set(cred->actual_mechs, &ret_mechs)) { - if (cred->ccache) - (void)krb5_cc_close(context, cred->ccache); - if (cred->keytab) - (void)krb5_kt_close(context, cred->keytab); - if (cred->princ) - krb5_free_principal(context, cred->princ); - xfree(cred); - *minor_status = ENOMEM; - return(GSS_S_FAILURE); - } + if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status, + &ret_mechs)) || + (cred->prerfc_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_old, + &ret_mechs))) || + (cred->rfc_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5, + &ret_mechs))) || + (cred->rfcv2_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_v2, + &ret_mechs)))) { + if (cred->ccache) + (void)krb5_cc_close(context, cred->ccache); + if (cred->keytab) + (void)krb5_kt_close(context, cred->keytab); + if (cred->princ) + krb5_free_principal(context, cred->princ); + xfree(cred); + /* *minor_status set above */ + return(ret); + } } /* intern the credential handle */ diff --git a/src/lib/gssapi/krb5/canon_name.c b/src/lib/gssapi/krb5/canon_name.c index 652745c7b8..688366e1f9 100644 --- a/src/lib/gssapi/krb5/canon_name.c +++ b/src/lib/gssapi/krb5/canon_name.c @@ -31,13 +31,12 @@ OM_uint32 krb5_gss_canonicalize_name(OM_uint32 *minor_status, const gss_OID mech_type, gss_name_t *output_name) { - if ((mech_type == GSS_C_NULL_OID) || - !g_OID_equal(mech_type, gss_mech_krb5)) { - if (minor_status) - *minor_status = 0; - return(GSS_S_BAD_MECH); - } - - return gss_duplicate_name(minor_status, input_name, - output_name); + if (!g_OID_equal(gss_mech_krb5_v2, mech_type) && + !g_OID_equal(gss_mech_krb5, mech_type) && + !g_OID_equal(gss_mech_krb5_old, mech_type)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + return(gss_duplicate_name(minor_status, input_name, output_name)); } diff --git a/src/lib/gssapi/krb5/delete_sec_context.c b/src/lib/gssapi/krb5/delete_sec_context.c index 766a17bc5a..28c2358906 100644 --- a/src/lib/gssapi/krb5/delete_sec_context.c +++ b/src/lib/gssapi/krb5/delete_sec_context.c @@ -101,6 +101,9 @@ krb5_gss_delete_sec_context(minor_status, context_handle, output_token) if (ctx->mech_used) gss_release_oid(minor_status, &ctx->mech_used); + if (ctx->ctypes) + xfree(ctx->ctypes); + /* Zero out context */ memset(ctx, 0, sizeof(*ctx)); xfree(ctx); diff --git a/src/lib/gssapi/krb5/disp_status.c b/src/lib/gssapi/krb5/disp_status.c index 4dc13843ce..3a6ba7b1ae 100644 --- a/src/lib/gssapi/krb5/disp_status.c +++ b/src/lib/gssapi/krb5/disp_status.c @@ -49,10 +49,12 @@ krb5_gss_display_status(minor_status, status_value, status_type, return(GSS_S_FAILURE); if ((mech_type != GSS_C_NULL_OID) && - (! g_OID_equal(gss_mech_krb5, mech_type))) { - *minor_status = 0; - return(GSS_S_BAD_MECH); - } + !g_OID_equal(gss_mech_krb5_v2, mech_type) && + !g_OID_equal(gss_mech_krb5, mech_type) && + !g_OID_equal(gss_mech_krb5_old, mech_type)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } if (status_type == GSS_C_GSS_CODE) { return(g_display_major_status(minor_status, status_value, diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 984f7078eb..bcaa4a9308 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -72,10 +72,13 @@ #define KG2_TOK_RESPONSE 0x0202 #define KG2_TOK_MIC 0x0303 #define KG2_TOK_WRAP_INTEG 0x0404 -#define KG2_TOK_WRAP_PRIVINTEG 0x0505 +#define KG2_TOK_WRAP_PRIV 0x0505 #define KRB5_GSS_FOR_CREDS_OPTION 1 +#define KG2_RESP_FLAG_ERROR 0x0001 +#define KG2_RESP_FLAG_DELEG_OK 0x0002 + /** internal types **/ typedef krb5_principal krb5_gss_name_t; @@ -84,10 +87,9 @@ typedef struct _krb5_gss_cred_id_rec { /* name/type of credential */ gss_cred_usage_t usage; krb5_principal princ; /* this is not interned as a gss_name_t */ - const gss_OID_set_desc *actual_mechs; int prerfc_mech; /* these are a cache of the set above */ int rfc_mech; - int k2_mech; + int rfcv2_mech; /* keytab (accept) data */ krb5_keytab keytab; @@ -113,17 +115,31 @@ typedef struct _krb5_gss_ctx_id_rec { krb5_keyblock *seq; krb5_timestamp endtime; krb5_flags krb_flags; - krb5_int32 seq_send; - krb5_int32 seq_recv; + /* XXX these used to be signed. the old spec is inspecific, and + the new spec specifies unsigned. I don't believe that the change + affects the wire encoding. */ + krb5_ui_4 seq_send; + krb5_ui_4 seq_recv; void *seqstate; int established; int big_endian; krb5_auth_context auth_context; gss_OID_desc *mech_used; + int gsskrb5_version; + int nctypes; + krb5_cksumtype *ctypes; } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t; extern void *kg_vdb; +struct kg2_option { + int option_id; /* set by caller */ + int length; /* filled in by parser */ + unsigned char *data; /* filled in by parser. points inside + passed-in token, so nothing needs to + be freed */ +}; + /* helper macros */ #define kg_save_name(name) g_save_name(&kg_vdb,name) @@ -225,6 +241,23 @@ krb5_error_code kg_ctx_internalize PROTOTYPE((krb5_context kcontext, OM_uint32 kg_get_context PROTOTYPE((OM_uint32 *minor_status, krb5_context *context)); +OM_uint32 +kg2_parse_token PROTOTYPE((OM_uint32 *minor_status, + unsigned char *ptr, + int length, + krb5_ui_4 *flags, + int *nctypes, /* OUT */ + krb5_cksumtype **ctypes, /* OUT */ + int noptions, + struct kg2_option *options, /* INOUT */ + krb5_data *kmsg, + krb5_data *mic)); + +void kg2_intersect_ctypes PROTOTYPE((int *nc1, + krb5_cksumtype *c1, + int nc2, + const krb5_cksumtype *c2)); + /** declarations of internal name mechanism functions **/ OM_uint32 krb5_gss_acquire_cred diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et index 54a126518b..3c9be6351f 100644 --- a/src/lib/gssapi/krb5/gssapi_err_krb5.et +++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et @@ -35,4 +35,5 @@ error_code KG_CRED, "Bad magic number for krb5_gss_cred_id_t" error_code KG_ENC_DESC, "Bad magic number for krb5_gss_enc_desc" error_code KG_BAD_SEQ, "Sequence number in token is corrupt" error_code KG_EMPTY_CCACHE, "Credential cache is empty" +error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types" end diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c index c0942c39a6..592ddfbf86 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -43,6 +43,9 @@ * The OID of the proposed standard krb5 mechanism is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * krb5(2) = 1.2.840.113554.1.2.2 + * The OID of the proposed standard krb5 v2 mechanism is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5v2(3) = 1.2.840.113554.1.2.3 * */ @@ -58,8 +61,13 @@ const gss_OID_desc krb5_gss_oid_array[] = { {5, "\053\005\001\005\002"}, /* this is the official, rfc-specified OID */ {9, "\052\206\110\206\367\022\001\002\002"}, + /* these two are name type OID's */ {10, "\052\206\110\206\367\022\001\002\002\001"}, {10, "\052\206\110\206\367\022\001\002\002\002"}, + /* this is the v2 assigned OID */ + {9, "\052\206\110\206\367\022\001\002\003"}, + /* this is the official, rfc-specified OID again */ + {9, "\052\206\110\206\367\022\001\002\002"}, { 0, 0 } }; @@ -67,16 +75,21 @@ const gss_OID_desc * const gss_mech_krb5_old = krb5_gss_oid_array+0; const gss_OID_desc * const gss_mech_krb5 = krb5_gss_oid_array+1; const gss_OID_desc * const gss_nt_krb5_name = krb5_gss_oid_array+2; const gss_OID_desc * const gss_nt_krb5_principal = krb5_gss_oid_array+3; +const gss_OID_desc * const gss_mech_krb5_v2 = krb5_gss_oid_array+4; static const gss_OID_set_desc oidsets[] = { {1, (gss_OID) krb5_gss_oid_array+0}, {1, (gss_OID) krb5_gss_oid_array+1}, {2, (gss_OID) krb5_gss_oid_array+0}, + {1, (gss_OID) krb5_gss_oid_array+4}, + {2, (gss_OID) krb5_gss_oid_array+4}, }; const gss_OID_set_desc * const gss_mech_set_krb5_old = oidsets+0; const gss_OID_set_desc * const gss_mech_set_krb5 = oidsets+1; const gss_OID_set_desc * const gss_mech_set_krb5_both = oidsets+2; +const gss_OID_set_desc * const gss_mech_set_krb5_v2 = oidsets+3; +const gss_OID_set_desc * const gss_mech_set_krb5_v1v2 = oidsets+4; void *kg_vdb = NULL; diff --git a/src/lib/gssapi/krb5/gssapi_krb5.h b/src/lib/gssapi/krb5/gssapi_krb5.h index 63ac530f31..e4eccbb429 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.h +++ b/src/lib/gssapi/krb5/gssapi_krb5.h @@ -32,9 +32,12 @@ extern const gss_OID_desc * const gss_mech_krb5; extern const gss_OID_desc * const gss_mech_krb5_old; +extern const gss_OID_desc * const gss_mech_krb5_v2; extern const gss_OID_set_desc * const gss_mech_set_krb5; extern const gss_OID_set_desc * const gss_mech_set_krb5_old; extern const gss_OID_set_desc * const gss_mech_set_krb5_both; +extern const gss_OID_set_desc * const gss_mech_set_krb5_v2; +extern const gss_OID_set_desc * const gss_mech_set_krb5_v1v2; extern const gss_OID_desc * const gss_nt_krb5_name; extern const gss_OID_desc * const gss_nt_krb5_principal; diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index 160550688b..97e2b74cd5 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -33,8 +33,257 @@ int krb5_gss_dbg_client_expcreds = 0; static krb5_error_code -make_ap_req(context, auth_context, cred, server, now, endtime, chan_bindings, - req_flags, krb_flags, mech_type, token) +make_ap_req_v2(context, auth_context, cred, server, now, endtime, + chan_bindings, req_flags, krb_flags, mech_type, + ret_nctypes, ret_ctypes, token) + krb5_context context; + krb5_auth_context * auth_context; + krb5_gss_cred_id_t cred; + krb5_principal server; + krb5_timestamp now; + krb5_timestamp *endtime; + gss_channel_bindings_t chan_bindings; + OM_uint32 *req_flags; + krb5_flags *krb_flags; + gss_OID mech_type; + int *ret_nctypes; + krb5_cksumtype **ret_ctypes; + gss_buffer_t token; +{ + krb5_flags mk_req_flags = 0; + krb5_int32 con_flags; + krb5_error_code code; + krb5_creds in_creds, *out_creds = 0; + krb5_data credmsg, cksumdata, ap_req; + int i, tlen, cblen, nctypes; + krb5_cksumtype *ctypes; + unsigned char *t, *ptr; + + credmsg.data = 0; + cksumdata.data = 0; + ap_req.data = 0; + ctypes = 0; + + /* this probably isn't necessary */ + if (*auth_context) + krb5_auth_con_free(context, *auth_context); + + *auth_context = 0; + + /* create the option data if necessary */ + + if (*req_flags & GSS_C_DELEG_FLAG) { + /* first get KRB_CRED message, so we know its length */ + + /* clear the time check flag that was set in krb5_auth_con_init() */ + krb5_auth_con_getflags(context, *auth_context, &con_flags); + krb5_auth_con_setflags(context, *auth_context, + con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); + + code = krb5_fwd_tgt_creds(context, *auth_context, 0, + cred->princ, server, cred->ccache, 1, + &credmsg); + + /* turn KRB5_AUTH_CONTEXT_DO_TIME back on */ + krb5_auth_con_setflags(context, *auth_context, con_flags); + + if (code) { + /* don't fail here; just don't accept/do the delegation + request */ + *req_flags &= ~GSS_C_DELEG_FLAG; + } else { + if (credmsg.length > KRB5_INT16_MAX) { + krb5_free_data_contents(context, &credmsg); + return(KRB5KRB_ERR_FIELD_TOOLONG); + } + } + } else { + credmsg.length = 0; + } + + /* + * Get the credential, for the session key etype + */ + + memset((char *) &in_creds, 0, sizeof(krb5_creds)); + + if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client))) + goto cleanup; + if ((code = krb5_copy_principal(context, server, &in_creds.server))) + goto cleanup; + in_creds.times.endtime = *endtime; + + if ((code = krb5_get_credentials(context, 0, cred->ccache, + &in_creds, &out_creds))) + goto cleanup; + + /* + * Enforce a stricter limit (without timeskew forgiveness at the + * boundaries) because accept_sec_context code is also similarly + * non-forgiving. + */ + if (!krb5_gss_dbg_client_expcreds && out_creds->times.endtime < now) { + code = KRB5KRB_AP_ERR_TKT_EXPIRED; + goto cleanup; + } + + /* construct the list of compatible cksum types */ + + if (code = krb5_c_keyed_checksum_types(context, + out_creds->keyblock.enctype, + &nctypes, &ctypes)) + goto cleanup; + + if (nctypes == 0) { + code = KRB5_CRYPTO_INTERNAL; + goto cleanup; + } + + /* construct the checksum fields */ + + cblen = sizeof(krb5_ui_2)*5; + if (chan_bindings) + cblen += (chan_bindings->initiator_address.length+ + chan_bindings->acceptor_address.length+ + chan_bindings->application_data.length); + + cksumdata.length = cblen + 8 + 4*nctypes + 4; + if (credmsg.length) + cksumdata.length += 4 + credmsg.length; + + if ((cksumdata.data = (char *) malloc(cksumdata.length)) == NULL) + goto cleanup; + + /* helper macros. This code currently depends on a long being 32 + bits, and htonl dtrt. */ + + ptr = cksumdata.data; + + if (chan_bindings) { + TWRITE_INT(ptr, chan_bindings->initiator_addrtype, 1); + TWRITE_BUF(ptr, chan_bindings->initiator_address, 1); + TWRITE_INT(ptr, chan_bindings->acceptor_addrtype, 1); + TWRITE_BUF(ptr, chan_bindings->acceptor_address, 1); + TWRITE_BUF(ptr, chan_bindings->application_data, 1); + } else { + memset(ptr, 0, cblen); + ptr += cblen; + } + + /* construct the token fields */ + + ptr[0] = (KG2_TOK_INITIAL >> 8) & 0xff; + ptr[1] = KG2_TOK_INITIAL & 0xff; + + ptr[2] = (*req_flags >> 24) & 0xff; + ptr[3] = (*req_flags >> 16) & 0xff; + ptr[4] = (*req_flags >> 8) & 0xff; + ptr[5] = *req_flags & 0xff; + + ptr[6] = (nctypes >> 8) & 0xff; + ptr[7] = nctypes & 0xff; + + ptr += 8; + + for (i=0; i> 24) & 0xff; + ptr[1] = (ctypes[i] >> 16) & 0xff; + ptr[2] = (ctypes[i] >> 8) & 0xff; + ptr[3] = ctypes[i] & 0xff; + + ptr += 4; + } + + if (credmsg.length) { + ptr[0] = (KRB5_GSS_FOR_CREDS_OPTION >> 8) & 0xff; + ptr[1] = KRB5_GSS_FOR_CREDS_OPTION & 0xff; + + ptr[2] = (credmsg.length >> 8) & 0xff; + ptr[3] = credmsg.length & 0xff; + + ptr += 4; + + memcpy(ptr, credmsg.data, credmsg.length); + + ptr += credmsg.length; + } + + memset(ptr, 0, 4); + + /* call mk_req. subkey and ap_req need to be used or destroyed */ + + mk_req_flags = AP_OPTS_USE_SUBKEY; + + if (*req_flags & GSS_C_MUTUAL_FLAG) + mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED; + + if ((code = krb5_mk_req_extended(context, auth_context, mk_req_flags, + &cksumdata, out_creds, &ap_req))) + goto cleanup; + + /* store the interesting stuff from creds and authent */ + *endtime = out_creds->times.endtime; + *krb_flags = out_creds->ticket_flags; + + /* build up the token */ + + /* allocate space for the token */ + tlen = g_token_size((gss_OID) mech_type, + (cksumdata.length-(2+cblen))+2+ap_req.length); + + if ((t = (unsigned char *) xmalloc(tlen)) == NULL) { + code = ENOMEM; + goto cleanup; + } + + ptr = t; + + g_make_token_header((gss_OID) mech_type, + (cksumdata.length-(2+cblen))+2+ap_req.length, + &ptr, KG2_TOK_INITIAL); + + /* skip over the channel bindings and the token id */ + memcpy(ptr, cksumdata.data+cblen+2, cksumdata.length-(cblen+2)); + ptr += cksumdata.length-(cblen+2); + ptr[0] = (ap_req.length >> 8) & 0xff; + ptr[1] = ap_req.length & 0xff; + ptr += 2; + memcpy(ptr, ap_req.data, ap_req.length); + + /* pass allocated data back */ + + *ret_nctypes = nctypes; + *ret_ctypes = ctypes; + + token->length = tlen; + token->value = (void *) t; + + code = 0; + +cleanup: + if (code) { + if (*auth_context) + krb5_auth_con_free(context, *auth_context); + } + + if (ctypes) + krb5_free_cksumtypes(context, ctypes); + if (out_creds) + krb5_free_creds(context, out_creds); + krb5_free_cred_contents(context, &in_creds); + if (credmsg.data) + free(credmsg.data); + if (ap_req.data) + free(ap_req.data); + if (cksumdata.data) + free(cksumdata.data); + + return(code); +} + +static krb5_error_code +make_ap_req_v1(context, auth_context, cred, server, now, endtime, + chan_bindings, req_flags, krb_flags, mech_type, token) krb5_context context; krb5_auth_context * auth_context; krb5_gss_cred_id_t cred; @@ -142,15 +391,16 @@ make_ap_req(context, auth_context, cred, server, now, endtime, chan_bindings, /* fill in the necessary fields in creds */ memset((char *) &in_creds, 0, sizeof(krb5_creds)); + if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client))) goto cleanup; if ((code = krb5_copy_principal(context, server, &in_creds.server))) goto cleanup; - in_creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; in_creds.times.endtime = *endtime; + in_creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; /* - * Get the credential..., I don't know in 0 is a good value for the + * Get the credential..., I don't know if 0 is a good value for the * kdcoptions */ if ((code = krb5_get_credentials(context, 0, cred->ccache, @@ -222,10 +472,6 @@ cleanup: return (code); } -#define IS_KRB_ERROR(dat)\ - ((dat) && (dat)->length && ((dat)->data[0] == 0x7e ||\ - (dat)->data[0] == 0x5e)) - OM_uint32 krb5_gss_init_sec_context(minor_status, claimant_cred_handle, context_handle, target_name, mech_type, @@ -253,8 +499,10 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, krb5_timestamp now; krb5_enctype enctype; gss_buffer_desc token; - int i; - int err; + int gsskrb5_vers; + int i, err; + krb5_ui_4 resp_flags, field_length, opt_id; + OM_uint32 major_status; if (GSS_ERROR(kg_get_context(minor_status, &context))) return(GSS_S_FAILURE); @@ -289,15 +537,33 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, err = 0; if (mech_type == GSS_C_NULL_OID) { - mech_type = cred->rfc_mech?gss_mech_krb5:gss_mech_krb5_old; + if (cred->rfcv2_mech) { + mech_type = gss_mech_krb5_v2; + gsskrb5_vers = 2000; + } else if (cred->rfc_mech) { + mech_type = gss_mech_krb5; + gsskrb5_vers = 1000; + } else if (cred->prerfc_mech) { + mech_type = gss_mech_krb5_old; + gsskrb5_vers = 1000; + } else { + err = 1; + } + } else if (g_OID_equal(mech_type, gss_mech_krb5_v2)) { + if (!cred->rfcv2_mech) + err = 1; + gsskrb5_vers = 2000; } else if (g_OID_equal(mech_type, gss_mech_krb5)) { if (!cred->rfc_mech) err = 1; + gsskrb5_vers = 1000; } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) { if (!cred->prerfc_mech) err = 1; - } else + gsskrb5_vers = 1000; + } else { err = 1; + } if (err) { *minor_status = 0; @@ -351,6 +617,9 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, ctx->seed_init = 0; ctx->big_endian = 0; /* all initiators do little-endian, as per spec */ ctx->seqstate = 0; + ctx->gsskrb5_version = gsskrb5_vers; + ctx->nctypes = 0; + ctx->ctypes = 0; if ((code = krb5_timeofday(context, &now))) { free(ctx); @@ -377,59 +646,94 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, return(GSS_S_FAILURE); } - if ((code = make_ap_req(context, &(ctx->auth_context), cred, - ctx->there, now, &ctx->endtime, - input_chan_bindings, - &ctx->gss_flags, &ctx->krb_flags, mech_type, - &token))) { - krb5_free_principal(context, ctx->here); - krb5_free_principal(context, ctx->there); - xfree(ctx); - *minor_status = code; - - if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || - (code == KG_EMPTY_CCACHE)) - return GSS_S_NO_CRED; - if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) - return GSS_S_CREDENTIALS_EXPIRED; - return(GSS_S_FAILURE); - } - - krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &ctx->seq_send); - krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &ctx->subkey); - - /* fill in the encryption descriptors */ - - switch(ctx->subkey->enctype) { - case ENCTYPE_DES_CBC_MD5: - case ENCTYPE_DES_CBC_CRC: - enctype = ENCTYPE_DES_CBC_RAW; - ctx->signalg = 0; - ctx->cksum_size = 8; - ctx->sealalg = 0; - break; + if (ctx->gsskrb5_version == 2000) { + /* gsskrb5 v2 */ + + ctx->gss_flags & ~GSS_C_DELEG_FLAG; + + if ((code = make_ap_req_v2(context, &(ctx->auth_context), cred, + ctx->there, now, &ctx->endtime, + input_chan_bindings, + &ctx->gss_flags, &ctx->krb_flags, + mech_type, &ctx->nctypes, &ctx->ctypes, + &token))) { + krb5_free_principal(context, ctx->here); + krb5_free_principal(context, ctx->there); + xfree(ctx); + *minor_status = code; + + if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || + (code == KG_EMPTY_CCACHE)) + return GSS_S_NO_CRED; + if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) + return GSS_S_CREDENTIALS_EXPIRED; + return(GSS_S_FAILURE); + } + + krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, + &ctx->seq_send); + krb5_auth_con_getlocalsubkey(context, ctx->auth_context, + &ctx->subkey); + } else { + /* gsskrb5 v1 */ + + if ((code = make_ap_req_v1(context, &(ctx->auth_context), cred, + ctx->there, now, &ctx->endtime, + input_chan_bindings, + &ctx->gss_flags, &ctx->krb_flags, + mech_type, + &token))) { + krb5_free_principal(context, ctx->here); + krb5_free_principal(context, ctx->there); + xfree(ctx); + *minor_status = code; + + if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || + (code == KG_EMPTY_CCACHE)) + return GSS_S_NO_CRED; + if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) + return GSS_S_CREDENTIALS_EXPIRED; + return(GSS_S_FAILURE); + } + + krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, + &ctx->seq_send); + krb5_auth_con_getlocalsubkey(context, ctx->auth_context, + &ctx->subkey); + + /* fill in the encryption descriptors */ + + switch(ctx->subkey->enctype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_CRC: + enctype = ENCTYPE_DES_CBC_RAW; + ctx->signalg = 0; + ctx->cksum_size = 8; + ctx->sealalg = 0; + break; #if 0 - case ENCTYPE_DES3_CBC_MD5: - enctype = ENCTYPE_DES3_CBC_RAW; - ctx->signalg = 3; - ctx->cksum_size = 16; - ctx->sealalg = 1; - break; + case ENCTYPE_DES3_CBC_MD5: + enctype = ENCTYPE_DES3_CBC_RAW; + ctx->signalg = 3; + ctx->cksum_size = 16; + ctx->sealalg = 1; + break; #endif - default: - return GSS_S_FAILURE; - } + default: + return GSS_S_FAILURE; + } - /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */ + /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */ - if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) - return(code); - for (i=0; ienc->length; i++) - /*SUPPRESS 113*/ - ctx->enc->contents[i] ^= 0xf0; + if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) + return(code); + for (i=0; ienc->length; i++) + /*SUPPRESS 113*/ + ctx->enc->contents[i] ^= 0xf0; - if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) - return(code); + if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) + return(code); + } /* at this point, the context is constructed and valid, hence, releaseable */ @@ -489,7 +793,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, } else { unsigned char *ptr; char *sptr; - krb5_data ap_rep; + krb5_data ap_rep, mic; krb5_ap_rep_enc_part *ap_rep_data; krb5_error *krb_error; @@ -508,11 +812,8 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, if ((ctx->established) || (((gss_cred_id_t) cred) != claimant_cred_handle) || ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) { - (void)krb5_gss_delete_sec_context(minor_status, - context_handle, NULL); - /* XXX this minor status is wrong if an arg was changed */ - *minor_status = KG_CONTEXT_ESTABLISHED; - return(GSS_S_FAILURE); + code = KG_CONTEXT_ESTABLISHED; + goto fail; } if (! krb5_principal_compare(context, ctx->there, @@ -534,47 +835,104 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, ptr = (unsigned char *) input_token->value; - if ((err = g_verify_token_header((gss_OID) mech_type, &(ap_rep.length), - &ptr, KG_TOK_CTX_AP_REP, - input_token->length))) { + if (ctx->gsskrb5_version == 2000) { + int token_length; + int nctypes; + krb5_cksumtype *ctypes; + + /* gsskrb5 v2 */ + + if ((err = g_verify_token_header((gss_OID) mech_type, + &token_length, + &ptr, KG2_TOK_RESPONSE, + input_token->length))) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = err; + return(GSS_S_DEFECTIVE_TOKEN); + } + + if (GSS_ERROR(major_status = + kg2_parse_token(minor_status, ptr, token_length, + &resp_flags, &nctypes, &ctypes, + 0, NULL, &ap_rep, &mic))) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + return(major_status); + } + + kg2_intersect_ctypes(&ctx->nctypes, ctx->ctypes, nctypes, ctypes); + + if (ctx->nctypes == 0) { + code = KG_NO_CTYPES; + goto fail; + } + + if (resp_flags & KG2_RESP_FLAG_ERROR) { + if (code = krb5_rd_error(context, &ap_rep, &krb_error)) + goto fail; + + if (krb_error->error) + code = krb_error->error + ERROR_TABLE_BASE_krb5; + else + code = 0; + + krb5_free_error(context, krb_error); + + goto fail; + } + + if (resp_flags & KG2_RESP_FLAG_DELEG_OK) + ctx->gss_flags |= GSS_C_DELEG_FLAG; + + /* drop through to ap_rep handling */ + } else { + /* gsskrb5 v1 */ + + if ((err = g_verify_token_header((gss_OID) mech_type, + &(ap_rep.length), + &ptr, KG_TOK_CTX_AP_REP, + input_token->length))) { if (g_verify_token_header((gss_OID) mech_type, &(ap_rep.length), &ptr, KG_TOK_CTX_ERROR, input_token->length) == 0) { - /* Handle a KRB_ERROR message from the server */ + /* Handle a KRB_ERROR message from the server */ - sptr = (char *) ptr; /* PC compiler bug */ - TREAD_STR(sptr, ap_rep.data, ap_rep.length); + sptr = (char *) ptr; /* PC compiler bug */ + TREAD_STR(sptr, ap_rep.data, ap_rep.length); - code = krb5_rd_error(context, &ap_rep, &krb_error); - if (code) - goto fail; - if (krb_error->error) - code = krb_error->error + ERROR_TABLE_BASE_krb5; - else - code = 0; - krb5_free_error(context, krb_error); + code = krb5_rd_error(context, &ap_rep, &krb_error); + if (code) goto fail; + if (krb_error->error) + code = krb_error->error + ERROR_TABLE_BASE_krb5; + else + code = 0; + krb5_free_error(context, krb_error); + goto fail; } else { - *minor_status = err; - return(GSS_S_DEFECTIVE_TOKEN); + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); } - } + } - sptr = (char *) ptr; /* PC compiler bug */ - TREAD_STR(sptr, ap_rep.data, ap_rep.length); + sptr = (char *) ptr; /* PC compiler bug */ + TREAD_STR(sptr, ap_rep.data, ap_rep.length); + } /* decode the ap_rep */ - if ((code = krb5_rd_rep(context,ctx->auth_context,&ap_rep, + if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep, &ap_rep_data))) { - /* - * XXX A hack for backwards compatiblity. - * To be removed in 1999 -- proven - */ - krb5_auth_con_setuseruserkey(context,ctx->auth_context,ctx->subkey); - if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep, - &ap_rep_data))) - goto fail; + /* + * XXX A hack for backwards compatiblity. + * To be removed in 1999 -- proven + */ + krb5_auth_con_setuseruserkey(context, ctx->auth_context, + ctx->subkey); + if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep, + &ap_rep_data))) + goto fail; } /* store away the sequence number */ @@ -589,6 +947,26 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, /* set established */ ctx->established = 1; + if (ctx->gsskrb5_version == 2000) { + gss_buffer_desc mic_data, mic_token; + + /* start with the token id */ + mic_data.value = ptr-2; + /* end before the ap-rep length */ + mic_data.length = ((char*)(ap_rep.data-2)-(char*)(ptr-2)); + + mic_token.length = mic.length; + mic_token.value = mic.data; + + if (GSS_ERROR(major_status = + krb5_gss_verify_mic(minor_status, *context_handle, + &mic_data, &mic_token, NULL))) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + return(major_status); + } + } + /* set returns */ if (time_rec) { @@ -598,7 +976,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, } if (ret_flags) - *ret_flags = KG_IMPLFLAGS(req_flags); + *ret_flags = ctx->gss_flags; if (actual_mech_type) *actual_mech_type = mech_type; @@ -612,8 +990,8 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, return(GSS_S_COMPLETE); fail: - (void)krb5_gss_delete_sec_context(minor_status, - (gss_ctx_id_t) ctx, NULL); + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); + *minor_status = code; return(GSS_S_FAILURE); } diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c index ee5d436c19..e1963c4c1e 100644 --- a/src/lib/gssapi/krb5/inq_cred.c +++ b/src/lib/gssapi/krb5/inq_cred.c @@ -39,6 +39,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, krb5_deltat lifetime; krb5_principal ret_name; gss_OID_set mechs; + OM_uint32 ret; if (GSS_ERROR(kg_get_context(minor_status, &context))) return(GSS_S_FAILURE); @@ -84,12 +85,26 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, } } - if (mechanisms) - if (! g_copy_OID_set(cred->actual_mechs, &mechs)) { - krb5_free_principal(context, ret_name); - *minor_status = ENOMEM; - return(GSS_S_FAILURE); - } + if (mechanisms) { + if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status, + &mechs)) || + (cred->prerfc_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_old, + &mechs))) || + (cred->rfc_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5, + &mechs))) || + (cred->rfcv2_mech && + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_v2, + &mechs)))) { + krb5_free_principal(context, ret_name); + /* *minor_status set above */ + return(ret); + } + } if (name) { if (! kg_save_name((gss_name_t) ret_name)) { diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c index 9c5f474509..01a1994301 100644 --- a/src/lib/gssapi/krb5/inq_names.c +++ b/src/lib/gssapi/krb5/inq_names.c @@ -43,10 +43,11 @@ krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types) * We only know how to handle our own mechanism. */ if ((mechanism != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5_v2, mechanism) && !g_OID_equal(gss_mech_krb5, mechanism) && !g_OID_equal(gss_mech_krb5_old, mechanism)) { *minor_status = 0; - return(GSS_S_FAILURE); + return(GSS_S_BAD_MECH); } /* We're okay. Create an empty OID set */ diff --git a/src/lib/gssapi/krb5/k5seal.c b/src/lib/gssapi/krb5/k5seal.c index 1194a4d088..d515f3433e 100644 --- a/src/lib/gssapi/krb5/k5seal.c +++ b/src/lib/gssapi/krb5/k5seal.c @@ -23,9 +23,233 @@ #include "gssapiP_krb5.h" static krb5_error_code -make_seal_token(context, enc, seq, seqnum, direction, text, token, - signalg, cksum_size, sealalg, encrypt, toktype, - bigend, oid) +make_priv_token_v2 PROTOTYPE((krb5_context context, + krb5_keyblock *subkey, + krb5_int32 *seqnum, + int direction, + gss_buffer_t text, + gss_buffer_t token, + gss_OID oid)); + +static krb5_error_code +make_priv_token_v2(context, subkey, seqnum, direction, text, token, oid) + krb5_context context; + krb5_keyblock *subkey; + krb5_int32 *seqnum; + int direction; + gss_buffer_t text; + gss_buffer_t token; + gss_OID oid; +{ + krb5_data plain; + krb5_enc_data cipher; + krb5_error_code code; + size_t enclen; + int tlen; + unsigned char *t, *ptr; + + plain.data = 0; + cipher.ciphertext.data = 0; + t = 0; + + plain.length = 7+text->length; + if ((plain.data = (void *) malloc(plain.length)) == NULL) { + code = ENOMEM; + goto cleanup; + } + + plain.data[0] = (*seqnum >> 24) & 0xff; + plain.data[1] = (*seqnum >> 16) & 0xff; + plain.data[2] = (*seqnum >> 8) & 0xff; + plain.data[3] = *seqnum & 0xff; + + plain.data[4] = direction?0:0xff; + + plain.data[5] = (text->length >> 8) & 0xff; + plain.data[6] = text->length & 0xff; + + memcpy(plain.data+7, text->value, text->length); + + if (code = krb5_c_encrypt_length(context, subkey->enctype, + plain.length, &enclen)) + goto cleanup; + + tlen = g_token_size((gss_OID) oid, 2+enclen); + + if ((t = (unsigned char *) xmalloc(tlen)) == NULL) + return(ENOMEM); + + ptr = t; + + g_make_token_header((gss_OID) oid, 2+enclen, &ptr, + KG2_TOK_WRAP_PRIV); + + ptr[0] = (enclen >> 8) & 0xff; + ptr[1] = enclen & 0xff; + + cipher.ciphertext.length = enclen; + cipher.ciphertext.data = ptr+2; + + if (code = krb5_c_encrypt(context, subkey, + KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV, + 0, &plain, &cipher)) + goto cleanup; + + /* that's it. return the token */ + + (*seqnum)++; + + token->length = tlen; + token->value = (void *) t; + + code = 0; + +cleanup: + if (plain.data) + free(plain.data); + if (t) + free(t); + + return(code); +} + +static krb5_error_code +make_integ_token_v2 PROTOTYPE((krb5_context context, + krb5_keyblock *subkey, + krb5_cksumtype ctype, + krb5_int32 *seqnum, + int direction, + gss_buffer_t text, + gss_buffer_t token, + int toktype, + gss_OID oid)); + +static krb5_error_code +make_integ_token_v2(context, subkey, ctype, seqnum, direction, text, token, + toktype, oid) + krb5_context context; + krb5_keyblock *subkey; + krb5_cksumtype ctype; + krb5_int32 *seqnum; + int direction; + gss_buffer_t text; + gss_buffer_t token; + int toktype; + gss_OID oid; +{ + krb5_error_code code; + int tmp, tlen; + unsigned char *t, *ptr; + krb5_data plain; + krb5_checksum cksum; + + plain.data = 0; + t = 0; + cksum.contents = 0; + + /* assemble the checksum buffer and compute the checksum */ + + plain.length = 7+text->length; + + if ((plain.data = (char *) malloc(plain.length)) == NULL) + goto cleanup; + + plain.data[0] = (*seqnum >> 24) & 0xff; + plain.data[1] = (*seqnum >> 16) & 0xff; + plain.data[2] = (*seqnum >> 8) & 0xff; + plain.data[3] = *seqnum & 0xff; + + plain.data[4] = direction?0:0xff; + + plain.data[5] = (text->length >> 8) & 0xff; + plain.data[6] = text->length & 0xff; + + memcpy(plain.data+7, text->value, text->length); + + if (code = krb5_c_make_checksum(context, ctype, subkey, + (toktype == KG2_TOK_WRAP_INTEG)? + KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG: + KRB5_KEYUSAGE_GSS_TOK_MIC, + &plain, &cksum)) + goto cleanup; + + /* assemble the token itself */ + + if (toktype == KG2_TOK_WRAP_INTEG) + tmp = 4+(7+text->length)+2+cksum.length; + else + tmp = 4+(5)+2+cksum.length; + + tlen = g_token_size((gss_OID) oid, tmp); + + if ((t = (unsigned char *) xmalloc(tlen)) == NULL) + return(ENOMEM); + + ptr = t; + + g_make_token_header((gss_OID) oid, tmp, &ptr, toktype); + + ptr[0] = (ctype >> 24) & 0xff; + ptr[1] = (ctype >> 16) & 0xff; + ptr[2] = (ctype >> 8) & 0xff; + ptr[3] = ctype & 0xff; + + ptr += 4; + + if (toktype == KG2_TOK_WRAP_INTEG) { + memcpy(ptr, plain.data, 7+text->length); + ptr += 7+text->length; + } else { + memcpy(ptr, plain.data, 5); + ptr += 5; + } + + ptr[0] = (cksum.length >> 8) & 0xff; + ptr[1] = cksum.length & 0xff; + ptr += 2; + + memcpy(ptr, cksum.contents, cksum.length); + + /* that's it. return the token */ + + (*seqnum)++; + + token->length = tlen; + token->value = (void *) t; + + code = 0; + +cleanup: + if (plain.data) + free(plain.data); + if (cksum.contents) + krb5_free_checksum_contents(context, &cksum); + if (t) + free(t); + + return(code); +} + +static krb5_error_code +make_seal_token_v1 PROTOTYPE((krb5_context context, + krb5_keyblock *enc, + krb5_keyblock *seq, + krb5_int32 *seqnum, + int direction, + gss_buffer_t text, + gss_buffer_t token, + int signalg, + int cksum_size, + int sealalg, + int encrypt, + int toktype, + int bigend, + gss_OID oid)); + +static krb5_error_code +make_seal_token_v1(context, enc, seq, seqnum, direction, text, token, + signalg, cksum_size, sealalg, encrypt, toktype, + bigend, oid) krb5_context context; krb5_keyblock *enc; krb5_keyblock *seq; @@ -329,17 +553,42 @@ kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req, return(GSS_S_FAILURE); } - if ((code = make_seal_token(context, &ctx->enc, &ctx->seq, - &ctx->seq_send, ctx->initiate, - input_message_buffer, output_message_buffer, - ctx->signalg, ctx->cksum_size, ctx->sealalg, - conf_req_flag, toktype, ctx->big_endian, - ctx->mech_used))) { + if (ctx->gsskrb5_version == 2000) { + if (toktype == KG_TOK_WRAP_MSG) { + if (conf_req_flag) + toktype = KG2_TOK_WRAP_PRIV; + else + toktype = KG2_TOK_WRAP_INTEG; + } else { + toktype = KG2_TOK_MIC; + } + + if (conf_req_flag) { + code = make_priv_token_v2(context, ctx->subkey, &ctx->seq_send, + ctx->initiate, input_message_buffer, + output_message_buffer, ctx->mech_used); + } else { + code = make_integ_token_v2(context, ctx->subkey, ctx->ctypes[0], + &ctx->seq_send, ctx->initiate, + input_message_buffer, + output_message_buffer, toktype, + ctx->mech_used); + } + } else { + code = make_seal_token_v1(context, ctx->enc, ctx->seq, + &ctx->seq_send, ctx->initiate, + input_message_buffer, output_message_buffer, + ctx->signalg, ctx->cksum_size, ctx->sealalg, + conf_req_flag, toktype, ctx->big_endian, + ctx->mech_used); + } + + if (code) { *minor_status = code; return(GSS_S_FAILURE); } - if ((toktype == KG_TOK_SEAL_MSG) && conf_state) + if (conf_state) *conf_state = conf_req_flag; *minor_status = 0; diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c index e60348fced..2bfd3d7611 100644 --- a/src/lib/gssapi/krb5/k5unseal.c +++ b/src/lib/gssapi/krb5/k5unseal.c @@ -27,31 +27,399 @@ * $Id$ */ +static OM_uint32 +kg2_verify_mic(context, minor_status, ctx, ptr, bodysize, + text, qop_state) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t text; + gss_qop_t *qop_state; +{ + size_t cksumlen; + krb5_error_code code; + krb5_data plain; + krb5_cksumtype tctype; + krb5_ui_4 tseqnum; + int tdirection; + krb5_checksum cksum; + krb5_boolean ckvalid; + krb5_timestamp now; + OM_uint32 retval; + + plain.data = 0; + cksum.contents = 0; + + /* verify the header */ + + if (bodysize < 11) { + free(plain.data); + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + /* allocate the checksum buffer */ + + plain.length = 7+text->length; + + if ((plain.data = (char *) malloc(plain.length)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + /* suck out the body parts from the token */ + + tctype = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) | + (ptr[2]<<8) | ptr[3]); + ptr += 4; + + memcpy(plain.data, ptr, 5); + tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); + ptr += 4; + tdirection = ptr[0]; + ptr += 1; + + cksum.length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 11; + + if (cksum.length != bodysize) { + free(plain.data); + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + cksum.contents = ptr; + cksum.checksum_type = tctype; + + /* finish assembling the checksum buffer and compute the checksum */ + + plain.data[5] = (text->length >> 8) & 0xff; + plain.data[6] = text->length & 0xff; + + memcpy(plain.data+7, text->value, text->length); + + if (code = krb5_c_verify_checksum(context, ctx->subkey, + KRB5_KEYUSAGE_GSS_TOK_MIC, + &plain, &cksum, &ckvalid)) { + free(plain.data); + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (!ckvalid) { + free(plain.data); + *minor_status = 0; + return(GSS_S_BAD_SIG); + } + + /* check context expiry */ + + if ((code = krb5_timeofday(context, &now))) { + free(plain.data); + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (now > ctx->endtime) { + free(plain.data); + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } + + /* do sequencing checks */ + + if ((ctx->initiate && tdirection != 0xff) || + (!ctx->initiate && tdirection != 0)) { + free(plain.data); + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + retval = g_order_check(&(ctx->seqstate), tseqnum); + + free(plain.data); + + if (retval) { + *minor_status = 0; + return(retval); + } + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +static OM_uint32 +kg2_unwrap_integ(context, minor_status, ctx, ptr, bodysize, output, qop_state) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t output; + gss_qop_t *qop_state; +{ + krb5_error_code code; + OM_uint32 retval; + krb5_ui_4 tseqnum; + int tdirection; + int tmsglen; + unsigned char *tmsg; + krb5_data plain; + krb5_checksum tcksum; + krb5_boolean ckvalid; + krb5_timestamp now; + + output->length = 0; + output->value = NULL; + + /* read the body parts out of the message */ + + if (bodysize < 11) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tcksum.checksum_type = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) | + (ptr[2]<<8) | ptr[3]); + ptr += 4; + + plain.data = ptr; + + tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); + ptr += 4; + tdirection = ptr[0]; + ptr += 1; + + tmsglen = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 11; + + if (bodysize < tmsglen) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tmsg = ptr; + ptr += tmsglen; + bodysize -= tmsglen; + + plain.length = ((char*)ptr) - ((char *)plain.data); + + tcksum.length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + + if (bodysize != tcksum.length) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tcksum.contents = ptr; + + /* verify the MIC */ + + if (code = krb5_c_verify_checksum(context, ctx->subkey, + KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG, + &plain, &tcksum, &ckvalid)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (!ckvalid) { + *minor_status = 0; + return(GSS_S_BAD_SIG); + } + + /* check context expiry */ + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (now > ctx->endtime) { + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } + + /* do sequencing checks */ + + if ((ctx->initiate && tdirection != 0xff) || + (!ctx->initiate && tdirection != 0)) { + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + if (retval = g_order_check(&(ctx->seqstate), tseqnum)) { + *minor_status = 0; + return(retval); + } + + if ((output->value = (void *) malloc(tmsglen)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + memcpy(output->value, tmsg, tmsglen); + output->length = tmsglen; + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +static OM_uint32 +kg2_unwrap_priv(context, minor_status, ctx, ptr, bodysize, output, qop_state) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t output; + gss_qop_t *qop_state; +{ + krb5_error_code code; + OM_uint32 retval; + krb5_enc_data cipher; + krb5_data plain; + krb5_ui_4 tseqnum; + int tdirection; + int tmsglen; + unsigned char *tmsg; + krb5_timestamp now; + + output->length = 0; + output->value = NULL; + + /* read the body parts out of the message */ + + if (bodysize < 2) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + cipher.ciphertext.length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 2; + + if (bodysize != cipher.ciphertext.length) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + cipher.ciphertext.data = ptr; + cipher.enctype = ENCTYPE_UNKNOWN; + + plain.length = cipher.ciphertext.length; + if ((plain.data = (char *) malloc(plain.length)) == NULL) { + *minor_status = 0; + return(GSS_S_FAILURE); + } + + /* decrypt (and implicitly verify) the encrypted data */ + + if (code = krb5_c_decrypt(context, ctx->subkey, + KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV, + 0, &cipher, &plain)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + /* parse out the encrypted fields */ + + ptr = plain.data; + bodysize = plain.length; + + if (bodysize < 7) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); + ptr += 4; + tdirection = ptr[0]; + ptr += 1; + + tmsglen = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 7; + + if (bodysize != tmsglen) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tmsg = ptr; + + /* check context expiry */ + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (now > ctx->endtime) { + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } + + /* do sequencing checks */ + + if ((ctx->initiate && tdirection != 0xff) || + (!ctx->initiate && tdirection != 0)) { + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + if (retval = g_order_check(&(ctx->seqstate), tseqnum)) { + *minor_status = 0; + return(retval); + } + + if ((output->value = (void *) malloc(tmsglen)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + memcpy(output->value, tmsg, tmsglen); + output->length = tmsglen; + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX - conf_state is only valid if SEAL. - */ + conf_state is only valid if SEAL. */ OM_uint32 -kg_unseal(context, minor_status, context_handle, input_token_buffer, - message_buffer, conf_state, qop_state, toktype) +kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + conf_state, qop_state, toktype) krb5_context context; OM_uint32 *minor_status; - gss_ctx_id_t context_handle; - gss_buffer_t input_token_buffer; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; gss_buffer_t message_buffer; int *conf_state; int *qop_state; int toktype; { - krb5_gss_ctx_id_rec *ctx; krb5_error_code code; - int bodysize; int tmsglen; int conflen = 0; int signalg; int sealalg; gss_buffer_desc token; - unsigned char *ptr; krb5_checksum cksum; krb5_checksum desmac; krb5_checksum md5cksum; @@ -72,32 +440,6 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, message_buffer->value = NULL; } - /* validate the context handle */ - if (! kg_validate_ctx_id(context_handle)) { - *minor_status = (OM_uint32) G_VALIDATE_FAILED; - return(GSS_S_NO_CONTEXT); - } - - ctx = (krb5_gss_ctx_id_rec *) context_handle; - - if (! ctx->established) { - *minor_status = KG_CTX_INCOMPLETE; - return(GSS_S_NO_CONTEXT); - } - - /* parse the token, leave the data in message_buffer, setting conf_state */ - - /* verify the header */ - - ptr = (unsigned char *) input_token_buffer->value; - - if ((err = g_verify_token_header((gss_OID) ctx->mech_used, &bodysize, - &ptr, toktype, - input_token_buffer->length))) { - *minor_status = err; - return(GSS_S_DEFECTIVE_TOKEN); - } - /* get the sign and seal algorithms */ signalg = ptr[0] + (ptr[1]<<8); @@ -423,3 +765,86 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, *minor_status = 0; return(retval); } + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +OM_uint32 +kg_unseal(context, minor_status, context_handle, input_token_buffer, + message_buffer, conf_state, qop_state, toktype) + krb5_context context; + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_token_buffer; + gss_buffer_t message_buffer; + int *conf_state; + int *qop_state; + int toktype; +{ + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + int err; + OM_uint32 retval; + + /* validate the context handle */ + if (! kg_validate_ctx_id(context_handle)) { + *minor_status = (OM_uint32) G_VALIDATE_FAILED; + return(GSS_S_NO_CONTEXT); + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (! ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* parse the token, leave the data in message_buffer, setting conf_state */ + + /* verify the header */ + + ptr = (unsigned char *) input_token_buffer->value; + + if (ctx->gsskrb5_version == 2000) { + if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, KG2_TOK_MIC, + input_token_buffer->length))) { + return(kg2_verify_mic(context, minor_status, ctx, ptr, bodysize, + message_buffer, qop_state)); + } else if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, + KG2_TOK_WRAP_INTEG, + input_token_buffer->length))) { + if (GSS_ERROR(retval = kg2_unwrap_integ(context, minor_status, + ctx, ptr, bodysize, + message_buffer, qop_state))) + return(retval); + + *conf_state = 0; + return(GSS_S_COMPLETE); + } else if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, + KG2_TOK_WRAP_PRIV, + input_token_buffer->length))) { + if (GSS_ERROR(retval = kg2_unwrap_priv(context, minor_status, + ctx, ptr, bodysize, + message_buffer, qop_state))) + return(retval); + + *conf_state = 1; + return(GSS_S_COMPLETE); + } + } else { + if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, toktype, + input_token_buffer->length))) { + return(kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, + message_buffer, conf_state, qop_state, + toktype)); + } + } + + *minor_status = err; + return(GSS_S_DEFECTIVE_TOKEN); +} diff --git a/src/lib/gssapi/krb5/rel_oid.c b/src/lib/gssapi/krb5/rel_oid.c index f35727e3f1..afb2171b86 100644 --- a/src/lib/gssapi/krb5/rel_oid.c +++ b/src/lib/gssapi/krb5/rel_oid.c @@ -63,7 +63,8 @@ krb5_gss_internal_release_oid(minor_status, oid) * return GSS_S_CONTINUE_NEEDED for any OIDs it does not recognize. */ - if ((*oid != gss_mech_krb5) && + if ((*oid != gss_mech_krb5_v2) && + (*oid != gss_mech_krb5) && (*oid != gss_mech_krb5_old) && (*oid != gss_nt_krb5_name) && (*oid != gss_nt_krb5_principal)) { diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c index fba8feaa3f..36a16d426e 100644 --- a/src/lib/gssapi/krb5/ser_sctx.c +++ b/src/lib/gssapi/krb5/ser_sctx.c @@ -230,12 +230,15 @@ kg_ctx_size(kcontext, arg, sizep) * krb5_int32 for seq_recv. * krb5_int32 for established. * krb5_int32 for big_endian. + * krb5_int32 for gsskrb5_version. + * krb5_int32 for nctypes. * krb5_int32 for trailer. */ kret = EINVAL; if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { - required = 14*sizeof(krb5_int32); + required = 16*sizeof(krb5_int32); required += sizeof(ctx->seed); + required += ctx->nctypes*sizeof(krb5_int32); kret = 0; if (!kret && ctx->here) @@ -302,6 +305,7 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) size_t required; krb5_octet *bp; size_t remain; + int i; required = 0; bp = *buffer; @@ -342,6 +346,10 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) &bp, &remain); (void) krb5_ser_pack_int32((krb5_int32) ctx->big_endian, &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->gsskrb5_version, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->nctypes, + &bp, &remain); /* Now dynamic data */ kret = 0; @@ -389,6 +397,13 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) KV5M_AUTH_CONTEXT, (krb5_pointer) ctx->auth_context, &bp, &remain); + + for (i=0; inctypes; i++) { + if (!kret) { + kret = krb5_ser_pack_int32((krb5_int32) ctx->ctypes[i], + &bp, &remain); + } + } if (!kret) { (void) krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain); @@ -415,6 +430,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) krb5_int32 ibuf; krb5_octet *bp; size_t remain; + int i; bp = *buffer; remain = *lenremain; @@ -457,6 +473,10 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) ctx->established = (int) ibuf; (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); ctx->big_endian = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->gsskrb5_version = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->nctypes = (int) ibuf; if ((kret = kg_oid_internalize(kcontext, &ctx->mech_used, &bp, &remain))) { @@ -517,6 +537,22 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) (krb5_pointer *) &ctx->auth_context, &bp, &remain); + if (!kret) { + if (ctx->nctypes) { + if ((ctx->ctypes = (krb5_cksumtype *) + malloc(ctx->nctypes*sizeof(krb5_cksumtype))) == NULL){ + kret = ENOMEM; + } + + for (i=0; inctypes; i++) { + if (!kret) { + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->ctypes[i] = (krb5_cksumtype) ibuf; + } + } + } + } + /* Get trailer */ if (!kret && !(kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)) && diff --git a/src/lib/gssapi/krb5/util_ctxsetup.c b/src/lib/gssapi/krb5/util_ctxsetup.c new file mode 100644 index 0000000000..3113c6a769 --- /dev/null +++ b/src/lib/gssapi/krb5/util_ctxsetup.c @@ -0,0 +1,182 @@ +#include "gssapiP_krb5.h" + +/* from the token, flags is stored directly. nctypes/ctypes is + allocated and returns the length and list of ctypes in the token. + noptions/options lists all the options which the caller cares + about. Those which are present in the token are filled in; the + order and length are not changed. If an error is returned, the + option list is in an indeterminate state. */ + +OM_uint32 +kg2_parse_token(minor_status, ptr, token_length, flags, nctypes, ctypes, + noptions, options, kmsg, mic) + OM_uint32 *minor_status; + unsigned char *ptr; + int token_length; + krb5_ui_4 *flags; + int *nctypes; /* OUT */ + krb5_cksumtype **ctypes; /* OUT */ + int noptions; + struct kg2_option *options; /* INOUT */ + krb5_data *kmsg; + krb5_data *mic; +{ + int field_length, i; + int opt_id; + + *ctypes = 0; + + /* read the flags */ + + if (token_length < 4) + goto defective; + *flags = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]; + ptr += 4; + token_length -= 4; + + /* read out the token list */ + + if (token_length < 2) + goto defective; + field_length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + token_length -= 2; + + *nctypes = field_length; + + if (*nctypes == 0) { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + + if ((*ctypes = (krb5_cksumtype *) + malloc((*nctypes) * sizeof(krb5_cksumtype))) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + for (i=0; ilength = field_length; + kmsg->data = ptr; + + ptr += field_length; + token_length -= field_length; + + /* if there's anything left, assume it's a mic. the mic isn't + necessarily present */ + + if (mic && token_length) { + if (token_length < 2) + goto defective; + field_length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + token_length -= 2; + + if (token_length < field_length) + goto defective; + + mic->length = field_length; + mic->data = ptr; + + ptr += field_length; + token_length -= field_length; + } else if (mic) { + mic->length = 0; + mic->data = ptr; + } + + if (token_length) + goto defective; + + return(GSS_S_COMPLETE); + +defective: + if (*ctypes) + free(*ctypes); + + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); +} + +/* nc1/c1 will be modified to contain the intersection of the + two lists. */ + +void +kg2_intersect_ctypes(nc1, c1, nc2, c2) + int *nc1; + krb5_cksumtype *c1; + int nc2; + const krb5_cksumtype *c2; +{ + int i, j, count; + krb5_cksumtype tmp; + + count = 0; + + for (i=0; i<*nc1; i++) { + /* first, check to make sure that c1[i] isn't a duplicate in c1 */ + for (j=0; jenc) : 0; - ohlen = g_token_size((gss_OID) ctx->mech_used, - (unsigned int) cfsize + ctx->cksum_size + 14); + if (ctx->gsskrb5_version == 2000) { + if (conf_req_flag) { + /* this is pretty gross. take the max output, and call + krb5_c_encrypt_length to see how much overhead is added + on. subtract that much, and see if it fits in the + requested space. If not, start subtracting 1 until it + does. This doesn't necessarily give us the optimal + packing, but I think that's ok (I could start adding 1 + until I went over, but that seems like it's not worth + the effort). This is probably O(blocksize), but that's + never going to be large. */ - if (ohlen < req_output_size) + OM_uint32 headerlen, plainlen; + size_t enclen; + + headerlen = g_token_size((gss_OID) ctx->mech_used, 2); + plainlen = req_output_size - headerlen; + + if (code = krb5_c_encrypt_length(context, ctx->enc->enctype, + plainlen, &enclen)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + plainlen -= plainlen - (enclen - plainlen); + + if (code = krb5_c_encrypt_length(context, ctx->enc->enctype, + plainlen, &enclen)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + while (headerlen + enclen > req_output_size) { + plainlen--; + + if (code = krb5_c_encrypt_length(context, ctx->enc->enctype, + plainlen, &enclen)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + } + + /* subtract off the fixed size inside the encrypted part */ + + plainlen -= 7; + + *max_input_size = plainlen; + } else { + size_t cksumlen; + OM_uint32 headerlen; + + if (code = krb5_c_checksum_length(context, ctx->ctypes[0], + &cksumlen)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + headerlen = g_token_size((gss_OID) ctx->mech_used, 13 + cksumlen); + + *max_input_size = req_output_size - headerlen; + } + } else { + OM_uint32 cfsize; + OM_uint32 ohlen; + + /* Calculate the token size and subtract that from the output size */ + cfsize = (conf_req_flag) ? kg_confounder_size(context, ctx->enc) : 0; + ohlen = g_token_size((gss_OID) ctx->mech_used, + (unsigned int) cfsize + ctx->cksum_size + 14); + + if (ohlen < req_output_size) /* * Cannot have trailer length that will cause us to pad over * our length */ *max_input_size = (req_output_size - ohlen - 1) & (~7); - else + else *max_input_size = 0; + } + *minor_status = 0; return(GSS_S_COMPLETE); } -- 2.47.2