* PERFORMANCE OF THIS SOFTWARE.
*/
+#include "k5-int.h"
#include "gssapiP_krb5.h"
#include <memory.h>
int i;
krb5_error_code code;
krb5_address addr, *paddr;
- krb5_authenticator *authdat;
+ krb5_authenticator *authdat = 0;
krb5_checksum md5;
- krb5_principal name;
- int gss_flags;
+ krb5_principal name = NULL;
+ int gss_flags = 0;
+ int decode_req_message = 0;
krb5_gss_ctx_id_rec *ctx;
krb5_enctype enctype;
krb5_timestamp now;
krb5_data option;
krb5_auth_context auth_context_cred = NULL;
const gss_OID_desc *mech_used = NULL;
-
+ OM_uint32 major_status = GSS_S_FAILURE;
+ krb5_error krb_error_data;
+ krb5_data scratch;
if (GSS_ERROR(kg_get_context(minor_status, &context)))
return(GSS_S_FAILURE);
*src_name = (gss_name_t) NULL;
output_token->length = 0;
output_token->value = NULL;
+ token.value = 0;
+ md5.contents = 0;
+
if (mech_type)
*mech_type = GSS_C_NULL_OID;
/* return a bogus cred 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)) {
+ 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
sptr = (char *) ptr;
TREAD_STR(sptr, ap_req.data, ap_req.length);
+ decode_req_message = 1;
/* construct the sender_addr */
/* decode the message */
if ((code = krb5_rd_req(context, &auth_context, &ap_req, cred->princ,
- cred->keytab, NULL, &ticket))) {
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ cred->keytab, NULL, &ticket)))
+ goto fail;
krb5_auth_con_getauthenticator(context, auth_context, &authdat);
if ((authdat->authenticator->subkey == NULL) ||
(authdat->ticket->enc_part2 == NULL)) {
- krb5_free_tkt_authent(authdat);
- *minor_status = KG_NO_SUBKEY;
- return(GSS_S_FAILURE);
+ code = KG_NO_SUBKEY;
+ goto fail;
}
#endif
if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
(authdat->checksum->length < 24)) {
- krb5_free_authenticator(context, authdat);
- *minor_status = 0;
- return(GSS_S_BAD_BINDINGS);
+ code = 0;
+ major_status = GSS_S_BAD_BINDINGS;
+ goto fail;
}
/*
TREAD_INT(ptr, tmp, bigend);
if (tmp != krb5_checksum_size(context, CKSUMTYPE_RSA_MD5)) {
- xfree(md5.contents);
- krb5_free_authenticator(context, authdat);
- *minor_status = KG_BAD_LENGTH;
- return(GSS_S_FAILURE);
+ code = KG_BAD_LENGTH;
+ goto fail;
}
}
/* 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))) {
- krb5_free_authenticator(context, authdat);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ bigend)))
+ goto fail;
TREAD_STR(ptr, ptr2, md5.length);
if (memcmp(ptr2, md5.contents, md5.length) != 0) {
- xfree(md5.contents);
- krb5_free_authenticator(context, authdat);
- *minor_status = 0;
- return(GSS_S_BAD_BINDINGS);
+ code = 0;
+ major_status = GSS_S_BAD_BINDINGS;
+ goto fail;
}
xfree(md5.contents);
+ md5.contents = 0;
TREAD_INT(ptr, gss_flags, bigend);
+ decode_req_message = 0;
/* if the checksum length > 24, there are options to process */
call to rd_and_store_for_creds() and clear its flags */
if ((code = krb5_auth_con_init(context,
- &auth_context_cred))) {
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ &auth_context_cred)))
+ goto fail;
krb5_auth_con_setflags(context, auth_context_cred, 0);
if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
== NULL) {
- *minor_status = ENOMEM;
- return(GSS_S_FAILURE);
+ code = ENOMEM;
+ goto fail;
}
memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
ctx->seed_init = 0;
ctx->big_endian = bigend;
- if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) {
- xfree(ctx);
- krb5_free_authenticator(context, authdat);
- *minor_status = code;
- return(GSS_S_FAILURE);
+ /* 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;
}
+
+ if ((code = krb5_copy_principal(context, cred->princ, &ctx->here)))
+ goto fail;
- code = krb5_copy_principal(context, authdat->client, &ctx->there);
+ if ((code = krb5_copy_principal(context, authdat->client, &ctx->there)))
+ goto fail;
/* done with authdat */
krb5_free_authenticator(context, authdat);
-
- if (code) {
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ authdat = 0;
if ((code = krb5_auth_con_getremotesubkey(context, auth_context,
- &ctx->subkey))) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ &ctx->subkey)))
+ 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))) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ &ctx->subkey)))
+ goto fail;
}
if (ctx->subkey == NULL) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
/* this isn't a very good error, but it's not clear to me this
can actually happen */
- *minor_status = KRB5KDC_ERR_NULL_KEY;
- return(GSS_S_FAILURE);
+ code = KRB5KDC_ERR_NULL_KEY;
+ goto fail;
}
switch(ctx->subkey->enctype) {
break;
#endif
default:
- return GSS_S_FAILURE;
+ code = KRB5_BAD_ENCTYPE;
+ goto fail;
}
/* fill in the encryption descriptors */
krb5_use_enctype(context, &ctx->enc.eblock, enctype);
ctx->enc.processed = 0;
- if (code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc.key)) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc.key)))
+ goto fail;
+
for (i=0; i<ctx->enc.key->length; i++)
/*SUPPRESS 113*/
ctx->enc.key->contents[i] ^= 0xf0;
krb5_use_enctype(context, &ctx->seq.eblock, enctype);
ctx->seq.processed = 0;
- if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq.key))) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq.key)))
+ 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))) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ if ((code = krb5_timeofday(context, &now)))
+ goto fail;
if (ctx->endtime < now) {
- krb5_free_principal(context, ctx->there);
- krb5_free_principal(context, ctx->here);
- xfree(ctx);
- *minor_status = 0;
- return(GSS_S_CREDENTIALS_EXPIRED);
+ code = 0;
+ major_status = GSS_S_CREDENTIALS_EXPIRED;
+ goto fail;
}
g_order_init(&(ctx->seqstate), ctx->seq_recv,
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))) {
- (void)krb5_gss_delete_sec_context(minor_status,
- (gss_ctx_id_t *) &ctx, NULL);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ 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) {
- (void)krb5_gss_delete_sec_context(minor_status,
- (gss_ctx_id_t *) &ctx, NULL);
- *minor_status = ENOMEM;
- return(GSS_S_FAILURE);
+ code = ENOMEM;
+ goto fail;
}
ptr = token.value;
g_make_token_header((gss_OID) mech_used, ap_rep.length,
/* set the return arguments */
if (src_name) {
- if ((code = krb5_copy_principal(context, ctx->there, &name))) {
- if (token.value)
- xfree(token.value);
- (void)krb5_gss_delete_sec_context(minor_status,
- (gss_ctx_id_t *) &ctx, NULL);
- *minor_status = code;
- return(GSS_S_FAILURE);
+ 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;
}
}
*ret_flags = ctx->gss_flags;
ctx->established = 1;
-
- /* intern the src_name */
-
- if (src_name)
- if (! kg_save_name((gss_name_t) name)) {
- krb5_free_principal(context, name);
- if (token.value)
- xfree(token.value);
- (void)krb5_gss_delete_sec_context(minor_status,
- (gss_ctx_id_t *) &ctx, NULL);
- *minor_status = (OM_uint32) G_VALIDATE_FAILED;
- return(GSS_S_FAILURE);
- }
-
- /* intern the context handle */
-
- if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
- if (src_name) {
- (void) kg_delete_name((gss_name_t) name);
- krb5_free_principal(context, name);
- }
- if (token.value)
- xfree(token.value);
- (void)krb5_gss_delete_sec_context(minor_status,
- (gss_ctx_id_t *) &ctx, NULL);
- *minor_status = (OM_uint32) G_VALIDATE_FAILED;
- return(GSS_S_FAILURE);
- }
-
*context_handle = ctx;
-
*output_token = token;
if (src_name)
*minor_status = 0;
return(GSS_S_COMPLETE);
+
+fail:
+ if (authdat)
+ krb5_free_authenticator(context, authdat);
+ if (ctx)
+ (void) krb5_gss_delete_sec_context(minor_status,
+ (gss_ctx_id_t *) &ctx, NULL);
+ if (token.value)
+ xfree(token.value);
+ if (name) {
+ (void) kg_delete_name((gss_name_t) name);
+ krb5_free_principal(context, name);
+ }
+ if (md5.contents)
+ xfree(md5.contents);
+
+ *minor_status = code;
+
+ /*
+ * If decode_req_message is set, then we need to decode the ap_req
+ * message to determine whether or not to send a response token.
+ * We need to do this because for some errors we won't be able to
+ * decode the authenticator to read out the gss_flags field.
+ */
+ if (decode_req_message) {
+ 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 (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;
+
+ code = krb5_mk_error(context, &krb_error_data, &scratch);
+ if (code)
+ return (major_status);
+
+ token.length = g_token_size((gss_OID) mech_used, scratch.length);
+ 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);
+
+ TWRITE_STR(ptr, scratch.data, scratch.length);
+ xfree(scratch.data);
+
+ *output_token = token;
+ }
+ return (major_status);
}
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,
char *sptr;
krb5_data ap_rep;
krb5_ap_rep_enc_part *ap_rep_data;
+ krb5_error *krb_error;
/* validate the context handle */
/*SUPPRESS 29*/
if ((err = g_verify_token_header((gss_OID) mech_type, &(ap_rep.length),
&ptr, KG_TOK_CTX_AP_REP,
input_token->length))) {
- *minor_status = err;
- return(GSS_S_DEFECTIVE_TOKEN);
+ 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 */
+
+ 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);
+ goto fail;
+ } else {
+ *minor_status = err;
+ return(GSS_S_DEFECTIVE_TOKEN);
+ }
}
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,
- &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))) {
- (void)krb5_gss_delete_sec_context(minor_status,
- context_handle, NULL);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
- }
+ /* decode the 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;
+ }
/* store away the sequence number */
ctx->seq_recv = ap_rep_data->seq_number;
/* set returns */
if (time_rec) {
- if ((code = krb5_timeofday(context, &now))) {
- (void)krb5_gss_delete_sec_context(minor_status,
- (gss_ctx_id_t) ctx, NULL);
- *minor_status = code;
- return(GSS_S_FAILURE);
- }
+ if ((code = krb5_timeofday(context, &now)))
+ goto fail;
*time_rec = ctx->endtime - now;
}
}
return(GSS_S_COMPLETE);
+
+fail:
+ (void)krb5_gss_delete_sec_context(minor_status,
+ (gss_ctx_id_t) ctx, NULL);
+ *minor_status = code;
+ return(GSS_S_FAILURE);
}