/* 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;
{
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 */
/* 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;
}
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;
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);
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;
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
/* 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;
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.
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 */
/* 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;
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;
}
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; i<ctx->enc->length; i++)
- /*SUPPRESS 113*/
- ctx->enc->contents[i] ^= 0xf0;
+ for (i=0; i<ctx->enc->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;
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,
/* 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; i<ctx->nctypes; 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)
if (ret_flags)
*ret_flags = ctx->gss_flags;
- ctx->established = 1;
*context_handle = ctx;
*output_token = token;
*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! */
*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;
* 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);
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;
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; i<desired_mechs->count; 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);
}
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;
/* 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 */
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));
}
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);
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,
#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;
/* 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;
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)
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
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
* 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
*
*/
{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 }
};
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;
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;
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<nctypes; i++) {
+ ptr[0] = (ctypes[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;
/* 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,
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,
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);
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;
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);
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; i<ctx->enc->length; i++)
- /*SUPPRESS 113*/
- ctx->enc->contents[i] ^= 0xf0;
+ if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
+ return(code);
+ for (i=0; i<ctx->enc->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 */
} 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;
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,
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 */
/* 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) {
}
if (ret_flags)
- *ret_flags = KG_IMPLFLAGS(req_flags);
+ *ret_flags = ctx->gss_flags;
if (actual_mech_type)
*actual_mech_type = mech_type;
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);
}
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);
}
}
- 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)) {
* 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 */
#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;
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;
* $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;
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);
*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);
+}
* 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)) {
* 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)
size_t required;
krb5_octet *bp;
size_t remain;
+ int i;
required = 0;
bp = *buffer;
&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;
KV5M_AUTH_CONTEXT,
(krb5_pointer) ctx->auth_context,
&bp, &remain);
+
+ for (i=0; i<ctx->nctypes; 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);
krb5_int32 ibuf;
krb5_octet *bp;
size_t remain;
+ int i;
bp = *buffer;
remain = *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))) {
(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; i<ctx->nctypes; 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)) &&
--- /dev/null
+#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; i<field_length; i++) {
+ if (token_length < 4)
+ goto defective;
+
+ (*ctypes)[i] = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) |
+ (ptr[2]<<8) | ptr[3]);
+ ptr += 4;
+ token_length -= 4;
+ }
+
+ do {
+ if (token_length < 4)
+ goto defective;
+ opt_id = (ptr[0]<<8) | ptr[1];
+ field_length = (ptr[2]<<8) | ptr[3];
+ ptr += 4;
+ token_length -= 4;
+
+ if (token_length < field_length)
+ goto defective;
+
+ for (i=0; i<noptions; i++) {
+ if (options[i].option_id = opt_id) {
+ options[i].length = field_length;
+ options[i].data = ptr;
+ }
+ break;
+ }
+
+ ptr += field_length;
+ token_length -= field_length;
+ } while (opt_id);
+
+ 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;
+
+ kmsg->length = 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; j<i; j++)
+ if (c1[i] == c1[j])
+ break;
+ if (j<i)
+ continue;
+ /* check if c1[i] is in c2. If it is, keep it by swapping
+ it into c1[count] and incrementing count. If count < i, then
+ that field has already been looked at and skipped as
+ not intersecting, which is ok. */
+
+ for (j=0; j<nc2; j++)
+ if (c1[i] == c2[j])
+ break;
+ if ((j<nc2) && (count != i)) {
+ tmp = c1[count];
+ c1[count] = c1[i];
+ c1[i] = tmp;
+ }
+ count++;
+ }
+
+ *nc1 = count;
+}
+
{
krb5_context context;
krb5_gss_ctx_id_rec *ctx;
- OM_uint32 cfsize;
- OM_uint32 ohlen;
+ krb5_error_code code;
if (GSS_ERROR(kg_get_context(minor_status, &context)))
return(GSS_S_FAILURE);
return(GSS_S_NO_CONTEXT);
}
- /* 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 (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);
}