svn rev #21591: branches/mskrb-integ/src/ include/ include/krb5/ lib/krb5/asn.1/ ...

lhoward@MIT.EDU lhoward at MIT.EDU
Fri Dec 26 00:19:38 EST 2008


http://src.mit.edu/fisheye/changelog/krb5/?cs=21591
Commit By: lhoward
Log Message:
Implement RFC 4537 in libkrb5. If the AP_OPTS_ETYPE_NEGOTIATION flag is
passed to krb5_mk_req(), then a EtypeList constructed from the
auth_context or krb5 context list of permitted enctypes will be sent.
AP_OPTS_ETYPE_NEGOTIATION will be returned by krb5_rd_req() in
ap_req_options if a subkey of a different enctype should be negotiated.

AP_OPTS_ETYPE_NEGOTIATION is only valid with mutual authentication.



Changed Files:
U   branches/mskrb-integ/src/include/k5-int.h
U   branches/mskrb-integ/src/include/krb5/krb5.hin
U   branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_decode.c
U   branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_encode.c
U   branches/mskrb-integ/src/lib/krb5/asn.1/krb5_decode.c
U   branches/mskrb-integ/src/lib/krb5/krb/auth_con.c
U   branches/mskrb-integ/src/lib/krb5/krb/auth_con.h
U   branches/mskrb-integ/src/lib/krb5/krb/gen_subkey.c
U   branches/mskrb-integ/src/lib/krb5/krb/kfree.c
U   branches/mskrb-integ/src/lib/krb5/krb/mk_rep.c
U   branches/mskrb-integ/src/lib/krb5/krb/mk_req_ext.c
U   branches/mskrb-integ/src/lib/krb5/krb/rd_rep.c
U   branches/mskrb-integ/src/lib/krb5/krb/rd_req_dec.c
Modified: branches/mskrb-integ/src/include/k5-int.h
===================================================================
--- branches/mskrb-integ/src/include/k5-int.h	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/include/k5-int.h	2008-12-26 05:19:33 UTC (rev 21591)
@@ -315,7 +315,7 @@
 
 /* RFC 4537 */
 typedef struct _krb5_etype_list {
-	unsigned int	length;
+	int		length;
 	krb5_enctype	*etypes;
 } krb5_etype_list;
 
@@ -1230,6 +1230,8 @@
 	(krb5_context, krb5_pa_server_referral_data * );
 void KRB5_CALLCONV krb5_free_pa_pac_req
 	(krb5_context, krb5_pa_pac_req * );
+void KRB5_CALLCONV krb5_free_etype_list
+	(krb5_context, krb5_etype_list * );
 
 /* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
 #include "com_err.h"
@@ -1566,6 +1568,9 @@
 krb5_error_code encode_krb5_pa_pac_req
 	(const krb5_pa_pac_req * , krb5_data **);
 
+krb5_error_code encode_krb5_etype_list
+	(const krb5_etype_list * , krb5_data **);
+
 /*************************************************************************
  * End of prototypes for krb5_encode.c
  *************************************************************************/
@@ -1722,6 +1727,9 @@
 krb5_error_code decode_krb5_pa_pac_req
 	(const krb5_data *, krb5_pa_pac_req **);
 
+krb5_error_code decode_krb5_etype_list
+	(const krb5_data *, krb5_etype_list **);
+
 struct _krb5_key_data;		/* kdb.h */
 
 struct ldap_seqof_key_data {
@@ -1892,7 +1900,8 @@
 
 krb5_error_code
 krb5int_generate_and_save_subkey (krb5_context, krb5_auth_context,
-				  krb5_keyblock * /* Old keyblock, not new!  */);
+				  krb5_keyblock * /* Old keyblock, not new!  */,
+				  krb5_enctype);
 
 /* set and change password helpers */
 
@@ -2413,6 +2422,11 @@
 krb5_error_code krb5_generate_subkey
 	(krb5_context,
 		const krb5_keyblock *, krb5_keyblock **);
+krb5_error_code krb5_generate_subkey_extended
+	(krb5_context,
+		const krb5_keyblock *,
+		krb5_enctype,
+		krb5_keyblock **);
 krb5_error_code krb5_generate_seq_number
 	(krb5_context,
 		const krb5_keyblock *, krb5_ui_4 *);

Modified: branches/mskrb-integ/src/include/krb5/krb5.hin
===================================================================
--- branches/mskrb-integ/src/include/krb5/krb5.hin	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/include/krb5/krb5.hin	2008-12-26 05:19:33 UTC (rev 21591)
@@ -838,10 +838,10 @@
 /* #define	AP_OPTS_RESERVED	0x00000010 */
 /* #define	AP_OPTS_RESERVED	0x00000008 */
 /* #define	AP_OPTS_RESERVED	0x00000004 */
-/* #define	AP_OPTS_RESERVED	0x00000002 */
-#define AP_OPTS_USE_SUBKEY	0x00000001
+#define	AP_OPTS_ETYPE_NEGOTIATION	0x00000002
+#define AP_OPTS_USE_SUBKEY		0x00000001
 
-#define AP_OPTS_WIRE_MASK	0xfffffff0
+#define AP_OPTS_WIRE_MASK		0xfffffff0
 
 /* definitions for ad_type fields. */
 #define	AD_TYPE_RESERVED	0x8000
@@ -2502,13 +2502,13 @@
 krb5_error_code KRB5_CALLCONV
 krb5_decode_authdata_container(krb5_context context,
     krb5_authdatatype type,
-    const krb5_authdata *if_relevant,
+    const krb5_authdata *container,
     krb5_authdata ***authdata);
 krb5_error_code KRB5_CALLCONV
 krb5_encode_authdata_container(krb5_context context,
     krb5_authdatatype type,
     krb5_authdata * const*authdata,
-    krb5_authdata ***if_relevant_p);
+    krb5_authdata ***container);
 
 /*
  * Windows PAC

Modified: branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_decode.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_decode.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_decode.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -1226,6 +1226,15 @@
     cleanup();
 }
 
+asn1_error_code asn1_decode_etype_list(asn1buf *buf, krb5_etype_list *val)
+{
+    setup();
+    { begin_structure();
+	end_structure();
+    }
+    cleanup();
+}
+
 #ifndef DISABLE_PKINIT
 /* PKINIT */
 

Modified: branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_encode.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_encode.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/asn.1/asn1_k_encode.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -1173,6 +1173,10 @@
 DEFSEQTYPE(pa_pac_request, krb5_pa_pac_req, pa_pac_request_fields, 0);
 #endif
 
+/* RFC 4537 */
+DEFFIELDTYPE(etype_list, krb5_etype_list, 
+             FIELDOF_SEQOF_INT32(krb5_etype_list, int32_ptr, etypes, length, -1));
+
 /* Exported complete encoders -- these produce a krb5_data with
    the encoding in the correct byte order.  */
 
@@ -1237,6 +1241,7 @@
 MAKE_FULL_ENCODER(encode_krb5_pa_for_user, pa_for_user);
 MAKE_FULL_ENCODER(encode_krb5_pa_svr_referral_data, pa_svr_referral_data);
 MAKE_FULL_ENCODER(encode_krb5_pa_server_referral_data, pa_server_referral_data);
+MAKE_FULL_ENCODER(encode_krb5_etype_list, etype_list);
 
 
 
@@ -1244,7 +1249,6 @@
 
 
 
-
 #ifndef DISABLE_PKINIT
 /*
  * PKINIT

Modified: branches/mskrb-integ/src/lib/krb5/asn.1/krb5_decode.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/asn.1/krb5_decode.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/asn.1/krb5_decode.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -987,6 +987,17 @@
     cleanup(free);
 }
 
+krb5_error_code decode_krb5_etype_list(const krb5_data *code, krb5_etype_list **rep)
+{
+    setup_buf_only();
+    alloc_field(*rep, krb5_etype_list);
+
+    retval = asn1_decode_sequence_of_enctype(&buf, &(*rep)->length, &(*rep)->etypes);
+    if (retval) clean_return(retval);
+
+    cleanup(free);
+}
+
 #ifndef DISABLE_PKINIT
 krb5_error_code decode_krb5_pa_pk_as_req(const krb5_data *code, krb5_pa_pk_as_req **rep)
 {

Modified: branches/mskrb-integ/src/lib/krb5/krb/auth_con.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/auth_con.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/auth_con.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -34,8 +34,9 @@
 
     (*auth_context)->req_cksumtype = context->default_ap_req_sumtype;
     (*auth_context)->safe_cksumtype = context->default_safe_sumtype;
-    (*auth_context) -> checksum_func = NULL;
+    (*auth_context)->checksum_func = NULL;
     (*auth_context)->checksum_func_data = NULL;
+    (*auth_context)->negotiated_etype = ENCTYPE_NULL;
     (*auth_context)->magic = KV5M_AUTH_CONTEXT;
     return 0;
 }

Modified: branches/mskrb-integ/src/lib/krb5/krb/auth_con.h
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/auth_con.h	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/auth_con.h	2008-12-26 05:19:33 UTC (rev 21591)
@@ -21,8 +21,9 @@
     krb5_pointer	i_vector;		/* mk_priv, rd_priv only */
     krb5_rcache		rcache;
     krb5_enctype      * permitted_etypes;	/* rd_req */
-  krb5_mk_req_checksum_func checksum_func;
-  void *checksum_func_data;
+    krb5_mk_req_checksum_func checksum_func;
+    void *checksum_func_data;
+    krb5_enctype	negotiated_etype;
 };
 
 

Modified: branches/mskrb-integ/src/lib/krb5/krb/gen_subkey.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/gen_subkey.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/gen_subkey.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -40,7 +40,10 @@
 }
 
 krb5_error_code
-krb5_generate_subkey(krb5_context context, const krb5_keyblock *key, krb5_keyblock **subkey)
+krb5_generate_subkey_extended(krb5_context context,
+			      const krb5_keyblock *key,
+			      krb5_enctype enctype,
+			      krb5_keyblock **subkey)
 {
     krb5_error_code retval;
     krb5_data seed;
@@ -52,10 +55,16 @@
     if ((*subkey = (krb5_keyblock *) malloc(sizeof(krb5_keyblock))) == NULL)
 	return(ENOMEM);
 
-    if ((retval = krb5_c_make_random_key(context, key->enctype, *subkey))) {
+    if ((retval = krb5_c_make_random_key(context, enctype, *subkey))) {
 	krb5_xfree(*subkey);
 	return(retval);
     }
 
     return(0);
 }
+
+krb5_error_code
+krb5_generate_subkey(krb5_context context, const krb5_keyblock *key, krb5_keyblock **subkey)
+{
+    return krb5_generate_subkey_extended(context, key, key->enctype, subkey);
+}

Modified: branches/mskrb-integ/src/lib/krb5/krb/kfree.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/kfree.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/kfree.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -799,3 +799,13 @@
     krb5_xfree(req);
 }
 
+void KRB5_CALLCONV
+krb5_free_etype_list(krb5_context context,
+		     krb5_etype_list *etypes)
+{
+    if (etypes != NULL) {
+	if (etypes->etypes != NULL)
+	    krb5_xfree(etypes->etypes);
+	krb5_xfree(etypes);
+    }
+}

Modified: branches/mskrb-integ/src/lib/krb5/krb/mk_rep.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/mk_rep.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/mk_rep.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -95,8 +95,11 @@
     if (dce_style)
 	repl.subkey = NULL;
     else if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) {
+	assert(auth_context->negotiated_etype != ENCTYPE_NULL);
+
 	retval = krb5int_generate_and_save_subkey (context, auth_context,
-						   auth_context->keyblock);
+						   auth_context->keyblock,
+						   auth_context->negotiated_etype);
 	if (retval)
 	    return retval;
 	repl.subkey = auth_context->send_subkey;

Modified: branches/mskrb-integ/src/lib/krb5/krb/mk_req_ext.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/mk_req_ext.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/mk_req_ext.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -64,16 +64,25 @@
  returns system errors
 */
 
+static krb5_error_code
+make_etype_list(krb5_context context,
+		krb5_enctype *permitted_etypes,
+		krb5_enctype tkt_enctype,
+		krb5_authdata ***authdata);
+
 static krb5_error_code 
 krb5_generate_authenticator (krb5_context,
 				       krb5_authenticator *, krb5_principal,
 				       krb5_checksum *, krb5_keyblock *,
-				       krb5_ui_4, krb5_authdata ** );
+				       krb5_ui_4, krb5_authdata **,
+				       krb5_enctype *permitted_etypes,
+				       krb5_enctype tkt_enctype);
 
 krb5_error_code
 krb5int_generate_and_save_subkey (krb5_context context,
 				  krb5_auth_context auth_context,
-				  krb5_keyblock *keyblock)
+				  krb5_keyblock *keyblock,
+				  krb5_enctype enctype)
 {
     /* Provide some more fodder for random number code.
        This isn't strong cryptographically; the point here is not
@@ -92,7 +101,8 @@
 
     if (auth_context->send_subkey)
 	krb5_free_keyblock(context, auth_context->send_subkey);
-    if ((retval = krb5_generate_subkey(context, keyblock, &auth_context->send_subkey)))
+    if ((retval = krb5_generate_subkey_extended(context, keyblock, enctype,
+						&auth_context->send_subkey)))
 	return retval;
 
     if (auth_context->recv_subkey)
@@ -116,18 +126,23 @@
     krb5_checksum	  checksum;
     krb5_checksum	  *checksump = 0;
     krb5_auth_context	  new_auth_context;
+    krb5_enctype	  *permitted_etypes = NULL;
 
     krb5_ap_req request;
     krb5_data *scratch = 0;
     krb5_data *toutbuf;
 
     request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
-    request.authenticator.ciphertext.data = 0;
+    request.authenticator.ciphertext.data = NULL;
     request.ticket = 0;
     
     if (!in_creds->ticket.length) 
 	return(KRB5_NO_TKT_SUPPLIED);
 
+    if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) &&
+	!(ap_req_options & AP_OPTS_MUTUAL_REQUIRED))
+	return(EINVAL);
+
     /* we need a native ticket */
     if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
 	return(retval);
@@ -174,7 +189,8 @@
 
     if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
 	retval = krb5int_generate_and_save_subkey (context, *auth_context,
-						   &in_creds->keyblock);
+						   &in_creds->keyblock,
+						   in_creds->keyblock.enctype);
 	if (retval)
 	    goto cleanup;
     }
@@ -205,12 +221,23 @@
 	goto cleanup_cksum;
     }
 
+    if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) {
+	if ((*auth_context)->permitted_etypes == NULL) {
+	    retval = krb5_get_permitted_enctypes(context, &permitted_etypes);
+	    if (retval)
+		goto cleanup_cksum;
+	} else
+	    permitted_etypes = (*auth_context)->permitted_etypes;
+    }
+
     if ((retval = krb5_generate_authenticator(context,
 					      (*auth_context)->authentp,
-					      (in_creds)->client, checksump,
+					      in_creds->client, checksump,
 					      (*auth_context)->send_subkey,
 					      (*auth_context)->local_seq_number,
-					      (in_creds)->authdata)))
+					      in_creds->authdata,
+					      permitted_etypes,
+					      in_creds->keyblock.enctype)))
 	goto cleanup_cksum;
 	
     /* encode the authenticator */
@@ -223,7 +250,6 @@
      */
     (*auth_context)->authentp->client = NULL;
     (*auth_context)->authentp->checksum = NULL;
-    (*auth_context)->authentp->authorization_data = NULL;
 
     /* call the encryption routine */
     if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
@@ -242,6 +268,9 @@
       free(checksump->contents);
 
 cleanup:
+    if (permitted_etypes &&
+	permitted_etypes != (*auth_context)->permitted_etypes)
+	krb5_xfree(permitted_etypes);
     if (request.ticket)
 	krb5_free_ticket(context, request.ticket);
     if (request.authenticator.ciphertext.data) {
@@ -261,7 +290,9 @@
 krb5_generate_authenticator(krb5_context context, krb5_authenticator *authent,
 			    krb5_principal client, krb5_checksum *cksum,
 			    krb5_keyblock *key, krb5_ui_4 seq_number,
-			    krb5_authdata **authorization)
+			    krb5_authdata **authorization,
+			    krb5_enctype *permitted_etypes,
+			    krb5_enctype tkt_enctype)
 {
     krb5_error_code retval;
     
@@ -274,7 +305,114 @@
     } else
 	authent->subkey = 0;
     authent->seq_number = seq_number;
-    authent->authorization_data = authorization;
+    authent->authorization_data = NULL;
 
+    if (authorization != NULL) {
+	retval = krb5_copy_authdata(context, authorization,
+				    &authent->authorization_data);
+	if (retval)
+	    return retval;
+    }
+    if (permitted_etypes != NULL) {
+	retval = make_etype_list(context, permitted_etypes, tkt_enctype,
+				 &authent->authorization_data);
+	if (retval)
+	    return retval;
+    }
+
     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
 }
+
+/* RFC 4537 */
+static krb5_error_code
+make_etype_list(krb5_context context,
+		krb5_enctype *permitted_etypes,
+		krb5_enctype tkt_enctype,
+		krb5_authdata ***authdata)
+{
+    krb5_error_code code;
+    krb5_etype_list etypes;
+    krb5_data *enc_etype_list;
+    krb5_data *ad_if_relevant;
+    krb5_authdata *etype_adata[2], etype_adatum, **adata;
+    int i;
+
+    etypes.etypes = permitted_etypes;
+
+    for (etypes.length = 0;
+	 etypes.etypes[etypes.length] != ENCTYPE_NULL;
+	 etypes.length++)
+	;
+
+    /*
+     * RFC 4537:
+     * If the enctype of the ticket session key is included in the enctype
+     * list sent by the client, it SHOULD be the last on the list.
+     */
+    for (i = 0; i < etypes.length; i++) {
+	if (etypes.etypes[i] == tkt_enctype) {
+	    krb5_enctype etype;
+
+	    etype = etypes.etypes[etypes.length - 1];
+	    etypes.etypes[etypes.length - 1] = tkt_enctype;
+	    etypes.etypes[i] = etype;
+	    break;
+	}
+    }
+
+    code = encode_krb5_etype_list(&etypes, &enc_etype_list);
+    if (code) {
+	return code;
+    }
+
+    etype_adatum.magic = KV5M_AUTHDATA;
+    etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
+    etype_adatum.length = enc_etype_list->length;
+    etype_adatum.contents = (krb5_octet *)enc_etype_list->data;
+
+    etype_adata[0] = &etype_adatum;
+    etype_adata[1] = NULL;
+
+    /* Wrap in AD-IF-RELEVANT container */
+    code = encode_krb5_authdata(etype_adata, &ad_if_relevant);
+    if (code) {
+	krb5_free_data(context, enc_etype_list);
+	return code;
+    }
+
+    krb5_free_data(context, enc_etype_list);
+
+    adata = *authdata;
+    if (adata == NULL) {
+	adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *));
+	i = 0;
+    } else {
+	for (i = 0; adata[i] != NULL; i++)
+	    ;
+
+	adata = (krb5_authdata **)realloc(*authdata,
+					  (i + 2) * sizeof(krb5_authdata *));
+    }
+    if (adata == NULL) {
+	krb5_free_data(context, ad_if_relevant);
+	return ENOMEM;
+    }
+
+    adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata));
+    if (adata[i] == NULL) {
+	krb5_free_data(context, ad_if_relevant);
+	return ENOMEM;
+    }
+    adata[i]->magic = KV5M_AUTHDATA;
+    adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+    adata[i]->length = ad_if_relevant->length;
+    adata[i]->contents = (krb5_octet *)ad_if_relevant->data;
+    krb5_xfree(ad_if_relevant); /* contents owned by adata[i] */
+
+    adata[i + 1] = NULL;
+
+    *authdata = adata;
+
+    return 0;
+}
+

Modified: branches/mskrb-integ/src/lib/krb5/krb/rd_rep.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/rd_rep.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/rd_rep.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -129,6 +129,8 @@
 	    krb5_free_keyblock(context, auth_context->send_subkey);
 	    auth_context->send_subkey = NULL;
 	}
+	/* not used for anything yet */
+	auth_context->negotiated_etype = (*repl)->subkey->enctype;
     }
 
     /* Get remote sequence number */

Modified: branches/mskrb-integ/src/lib/krb5/krb/rd_req_dec.c
===================================================================
--- branches/mskrb-integ/src/lib/krb5/krb/rd_req_dec.c	2008-12-25 22:43:41 UTC (rev 21590)
+++ branches/mskrb-integ/src/lib/krb5/krb/rd_req_dec.c	2008-12-26 05:19:33 UTC (rev 21591)
@@ -62,6 +62,19 @@
 static krb5_error_code decrypt_authenticator
 	(krb5_context, const krb5_ap_req *, krb5_authenticator **,
 	 int);
+static krb5_error_code
+decode_etype_list(krb5_context context,
+		  const krb5_authenticator *authp,
+		  krb5_enctype **desired_etypes,
+		  int *desired_etypes_len);
+static krb5_error_code
+negotiate_etype(krb5_context context,
+		const krb5_enctype *desired_etypes,
+		int desired_etypes_len,
+		int mandatory_etypes_index,
+		const krb5_enctype *permitted_etypes,
+		int permitted_etypes_len,
+		krb5_enctype *negotiated_etype);
 
 krb5_error_code
 krb5int_check_clockskew(krb5_context context, krb5_timestamp date)
@@ -172,8 +185,13 @@
 			krb5_ticket **ticket, int check_valid_flag)
 {
     krb5_error_code 	  retval = 0;
-    krb5_principal_data princ_data;
-    
+    krb5_principal_data	princ_data;
+    krb5_enctype         *desired_etypes = NULL;
+    int			  desired_etypes_len = 0;
+    int			  rfc4537_etypes_len = 0;
+    krb5_enctype         *permitted_etypes = NULL;
+    int			  permitted_etypes_len = 0;
+ 
     req->ticket->enc_part2 = NULL;
     if (server && krb5_is_referral_realm(&server->realm)) {
 	char *realm;
@@ -340,126 +358,59 @@
       }
     }
 
-    /* check if the various etypes are permitted */
+    /* read RFC 4537 etype list from sender */
+    retval = decode_etype_list(context,
+			       (*auth_context)->authentp,
+			       &desired_etypes,
+			       &rfc4537_etypes_len);
+    if (retval != 0)
+	goto cleanup;
 
-    if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) {
-	/* no etype check needed */;
-    } else if ((*auth_context)->permitted_etypes == NULL) {
-	int etype;
-        size_t size_etype_enc = 3 * sizeof(krb5_enctype); /* upto three types */
-        size_t size_etype_bool = 3 * sizeof(krb5_boolean);
-        krb5_etypes_permitted etypes;
-        memset(&etypes, 0, sizeof etypes);
+    if (desired_etypes == NULL)
+	desired_etypes = (krb5_enctype *)calloc(4, sizeof(krb5_enctype));
+    else
+	desired_etypes = (krb5_enctype *)realloc(desired_etypes,
+						 (rfc4537_etypes_len + 4) *
+						    sizeof(krb5_enctype));
+    if (desired_etypes == NULL) {
+	retval = ENOMEM;
+	goto cleanup;
+    }
 
-        etypes.etype = (krb5_enctype*) malloc(size_etype_enc);
-	if (etypes.etype == NULL) {
-	    retval = ENOMEM;
-	    goto cleanup;
-	}
-        etypes.etype_ok = (krb5_boolean*) malloc(size_etype_bool);
-	if (etypes.etype_ok == NULL) {
-	    retval = ENOMEM;
-	    free(etypes.etype);
-	    goto cleanup;
-	}
-        memset(etypes.etype, 0, size_etype_enc);
-        memset(etypes.etype_ok, 0, size_etype_bool);
+    desired_etypes_len = rfc4537_etypes_len;
 
-        etypes.etype[etypes.etype_count++] = req->ticket->enc_part.enctype;
-        etypes.etype[etypes.etype_count++] = req->ticket->enc_part2->session->enctype;
-        if ((*auth_context)->authentp->subkey) {
-            etypes.etype[etypes.etype_count++] = (*auth_context)->authentp->subkey->enctype;
-        }
+    if ((*auth_context)->authentp->subkey != NULL)
+	desired_etypes[desired_etypes_len++] = (*auth_context)->authentp->subkey->enctype;
+    desired_etypes[desired_etypes_len++] = req->ticket->enc_part2->session->enctype;
+    desired_etypes[desired_etypes_len++] = req->ticket->enc_part.enctype;
+    desired_etypes[desired_etypes_len] = ENCTYPE_NULL;
 
-        retval = krb5_is_permitted_enctype_ext(context, &etypes);
-
-#if 0
-	/* check against the default set */
-	if ((!krb5_is_permitted_enctype(context,
-					etype = req->ticket->enc_part.enctype)) ||
-	    (!krb5_is_permitted_enctype(context,
-					etype = req->ticket->enc_part2->session->enctype)) ||
-	    (((*auth_context)->authentp->subkey) &&
-	     !krb5_is_permitted_enctype(context,
-					etype = (*auth_context)->authentp->subkey->enctype))) {
-#endif
-        if  (retval == 0  /* all etypes are not permitted */ ||
-              (!etypes.etype_ok[0] || !etypes.etype_ok[1] ||
-              (((*auth_context)->authentp->subkey) && !etypes.etype_ok[etypes.etype_count-1]))) {
-            char enctype_name[30];
-            retval = KRB5_NOPERM_ETYPE;
-
-            if (!etypes.etype_ok[0]) {
-                etype = etypes.etype[1];
-            } else if (!etypes.etype_ok[1]) {
-                etype = etypes.etype[1];
-            } else {
-                etype = etypes.etype[2];
-            }
-	    free(etypes.etype);
-	    free(etypes.etype_ok);
-
-	    if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
-		krb5_set_error_message(context, retval,
-				       "Encryption type %s not permitted",
-				       enctype_name);
-	    goto cleanup;
+    if (((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) == 0) {
+	if ((*auth_context)->permitted_etypes != NULL) {
+	    permitted_etypes = (*auth_context)->permitted_etypes;
+	} else {
+	    retval = krb5_get_permitted_enctypes(context, &permitted_etypes);
+	    if (retval != 0)
+		goto cleanup;
 	}
-	free(etypes.etype);
-	free(etypes.etype_ok);
+	for (permitted_etypes_len = 0;
+	     permitted_etypes[permitted_etypes_len] != ENCTYPE_NULL;
+	     permitted_etypes_len++)
+	    ;
     } else {
-	/* check against the set in the auth_context */
-	int i;
-
-	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
-	    if ((*auth_context)->permitted_etypes[i] ==
-		req->ticket->enc_part.enctype)
-		break;
-	if (!(*auth_context)->permitted_etypes[i]) {
-	    char enctype_name[30];
-	    retval = KRB5_NOPERM_ETYPE;
-	    if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
-				       enctype_name, sizeof(enctype_name)) == 0)
-		krb5_set_error_message(context, retval,
-				       "Encryption type %s not permitted",
-				       enctype_name);
-	    goto cleanup;
-	}
-	
-	for (i=0; (*auth_context)->permitted_etypes[i]; i++)
-	    if ((*auth_context)->permitted_etypes[i] ==
-		req->ticket->enc_part2->session->enctype)
-		break;
-	if (!(*auth_context)->permitted_etypes[i]) {
-	    char enctype_name[30];
-	    retval = KRB5_NOPERM_ETYPE;
-	    if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
-				       enctype_name, sizeof(enctype_name)) == 0)
-		krb5_set_error_message(context, retval,
-				       "Encryption type %s not permitted",
-				       enctype_name);
-	    goto cleanup;
-	}
-	
-	if ((*auth_context)->authentp->subkey) {
-	    for (i=0; (*auth_context)->permitted_etypes[i]; i++)
-		if ((*auth_context)->permitted_etypes[i] ==
-		    (*auth_context)->authentp->subkey->enctype)
-		    break;
-	    if (!(*auth_context)->permitted_etypes[i]) {
-		char enctype_name[30];
-		retval = KRB5_NOPERM_ETYPE;
-		if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype,
-					   enctype_name,
-					   sizeof(enctype_name)) == 0)
-		    krb5_set_error_message(context, retval,
-					   "Encryption type %s not permitted",
-					   enctype_name);
-		goto cleanup;
-	    }
-	}
+	permitted_etypes = NULL;
+	permitted_etypes_len = 0;
     }
 
+    /* check if the various etypes are permitted */
+    retval = negotiate_etype(context,
+			     desired_etypes, desired_etypes_len,
+			     rfc4537_etypes_len,
+			     permitted_etypes, permitted_etypes_len,
+			     &(*auth_context)->negotiated_etype);
+    if (retval != 0)
+	goto cleanup;
+
     (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number;
     if ((*auth_context)->authentp->subkey) {
 	if ((retval = krb5_copy_keyblock(context,
@@ -498,11 +449,20 @@
     if (ticket)
    	if ((retval = krb5_copy_ticket(context, req->ticket, ticket)))
 	    goto cleanup;
-    if (ap_req_options)
+    if (ap_req_options) {
     	*ap_req_options = req->ap_options;
+	if ((*auth_context)->negotiated_etype != (*auth_context)->keyblock->enctype)
+	    *ap_req_options |= AP_OPTS_ETYPE_NEGOTIATION;
+    }
+
     retval = 0;
     
 cleanup:
+    if (desired_etypes != NULL)
+	krb5_xfree(desired_etypes);
+    if (permitted_etypes != NULL &&
+	permitted_etypes != (*auth_context)->permitted_etypes)
+	krb5_xfree(permitted_etypes);
     if (server == &princ_data)
 	krb5_free_default_realm(context, princ_data.realm.data);
     if (retval) {
@@ -581,3 +541,124 @@
 }
 #endif
 
+static krb5_error_code
+negotiate_etype(krb5_context context,
+		const krb5_enctype *desired_etypes,
+		int desired_etypes_len,
+		int mandatory_etypes_index,
+		const krb5_enctype *permitted_etypes,
+		int permitted_etypes_len,
+		krb5_enctype *negotiated_etype)
+{
+    int i, j;
+
+    *negotiated_etype = ENCTYPE_NULL;
+
+    for (i = mandatory_etypes_index; i < desired_etypes_len; i++) {
+	krb5_boolean permitted = FALSE;
+
+	for (j = 0; j < permitted_etypes_len; j++) {
+	    if (desired_etypes[i] == permitted_etypes[j]) {
+		permitted = TRUE;
+		break;
+	    }
+	}
+
+	if (permitted == FALSE) {
+	    char enctype_name[30];
+
+	    if (krb5_enctype_to_string(desired_etypes[i],
+				       enctype_name,
+				       sizeof(enctype_name)) == 0)
+		krb5_set_error_message(context, KRB5_NOPERM_ETYPE,
+				       "Encryption type %s not permitted",
+				       enctype_name);
+		return KRB5_NOPERM_ETYPE;
+	}
+    }
+
+    for (j = 0; j < permitted_etypes_len; j++) {
+	for (i = 0; i < desired_etypes_len; i++) {
+	    if (desired_etypes[i] == permitted_etypes[j]) {
+		*negotiated_etype = permitted_etypes[j];
+		return 0;
+	    }
+	}
+    }
+
+    /*NOTREACHED*/
+    return KRB5_NOPERM_ETYPE;
+}
+
+static krb5_error_code
+decode_etype_list(krb5_context context,
+		  const krb5_authenticator *authp,
+		  krb5_enctype **desired_etypes,
+		  int *desired_etypes_len)
+{
+    krb5_error_code code;
+    krb5_authdata **ad_if_relevant = NULL;
+    krb5_authdata *etype_adata = NULL;
+    krb5_etype_list *etype_list = NULL;
+    int i, j;
+    krb5_data data;
+
+    *desired_etypes = NULL;
+
+    if (authp->authorization_data == NULL)
+	return 0;
+
+    /*
+     * RFC 4537 says that ETYPE_NEGOTIATION auth data should be wrapped
+     * in AD_IF_RELEVANT, but we handle the case where it is mandatory.
+     */
+    for (i = 0; authp->authorization_data[i] != NULL; i++) {
+	switch (authp->authorization_data[i]->ad_type) {
+	case KRB5_AUTHDATA_IF_RELEVANT:
+	    code = krb5_decode_authdata_container(context,
+						  KRB5_AUTHDATA_IF_RELEVANT,
+						  authp->authorization_data[i],
+						  &ad_if_relevant);
+	    if (code != 0)
+		continue;
+
+	    for (j = 0; ad_if_relevant[j] != NULL; j++) {
+		if (ad_if_relevant[j]->ad_type == KRB5_AUTHDATA_ETYPE_NEGOTIATION) {
+		    etype_adata = ad_if_relevant[j];
+		    break;
+		}
+	    }
+	    if (etype_adata == NULL) {
+		krb5_free_authdata(context, ad_if_relevant);
+		ad_if_relevant = NULL;
+	    }
+	    break;
+	case KRB5_AUTHDATA_ETYPE_NEGOTIATION:
+	    etype_adata = authp->authorization_data[i];
+	    break;
+	default:
+	    break;
+	}
+	if (etype_adata != NULL)
+	    break;
+    }
+
+    if (etype_adata == NULL)
+	return 0;
+
+    data.data = (char *)etype_adata->contents;
+    data.length = etype_adata->length;
+
+    code = decode_krb5_etype_list(&data, &etype_list);
+    if (code == 0) {
+	*desired_etypes = etype_list->etypes;
+	*desired_etypes_len = etype_list->length;
+	krb5_xfree(etype_list);
+    }
+
+    if (ad_if_relevant != NULL)
+	krb5_free_authdata(context, ad_if_relevant);
+
+    return code;
+}
+




More information about the cvs-krb5 mailing list