Update: Kerberos Ticket Forwarding Patch/Update [3.2]

Jeremy Allison jra at samba.org
Fri Aug 1 20:29:46 GMT 2008


On Fri, Aug 01, 2008 at 03:49:57PM -0400, Derrick Schommer wrote:
> The latest one I just sent (passed in transmission) removes the struct in total. It's been awhile, I didn't know the krb5_mk_1cred was non-portable though. Hmm..
> 
> I also saw your use of the SMB_MALLOC, didn't know you had that :) The code I wrote came out of a in-house CIFS client I wrote a few years ago for internal testing, it only had to run in this one environment, I tried to port it as cleanly as possible but I see I've been pretty unsuccessful :-)

Ok, here's the finished version. Can you check that
it works in your environment please ?

The only thing left to do is decide how to cope
with the krb5_mk_1cred() call...

Jeremy.
-------------- next part --------------
diff --git a/source/configure.in b/source/configure.in
index 9a230de..e1c0776 100644
--- a/source/configure.in
+++ b/source/configure.in
@@ -3367,6 +3367,7 @@ if test x"$with_ads_support" != x"no"; then
   AC_CHECK_FUNC_EXT(krb5_get_init_creds_opt_free, $KRB5_LIBS)
   AC_CHECK_FUNC_EXT(krb5_get_init_creds_opt_get_error, $KRB5_LIBS)
   AC_CHECK_FUNC_EXT(krb5_enctype_to_string, $KRB5_LIBS)
+  AC_CHECK_FUNC_EXT(krb5_auth_con_set_req_cksumtype, $KRB5_LIBS)
 
   LIBS="$KRB5_LIBS $LIBS"
 
diff --git a/source/libsmb/clifsinfo.c b/source/libsmb/clifsinfo.c
index 0005c39..5e73b61 100644
--- a/source/libsmb/clifsinfo.c
+++ b/source/libsmb/clifsinfo.c
@@ -528,7 +528,7 @@ static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
 				&es->s.gss_state->gss_ctx,
 				srv_name,
 				GSS_C_NO_OID, /* default OID. */
-				GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+				GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG,
 				GSS_C_INDEFINITE,	/* requested ticket lifetime. */
 				NULL,   /* no channel bindings */
 				p_tok_in,
diff --git a/source/libsmb/clikrb5.c b/source/libsmb/clikrb5.c
index c289740..8479ca1 100644
--- a/source/libsmb/clikrb5.c
+++ b/source/libsmb/clikrb5.c
@@ -37,6 +37,31 @@
 #define KRB5_KEY_DATA(k)	((k)->contents)
 #endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
 
+
+#define GSSAPI_CHECKSUM      0x8003             /* Checksum type value for Kerberos */
+#define GSSAPI_BNDLENGTH     16                 /* Bind Length (rfc-1964 pg.3) */
+
+/*
+ We marshal into a structure that should look like the
+ following (linearized) in LE format.
+
+struct gssChecksum {
+	uint32_t   lgth;                       Number of bytes in the 'Bnd' field (always 16)
+	uint8_t    bnd[GSSAPI_BNDLENGTH];      16-byte Bnd field.
+	uint32_t   flags;                      Context-establishment flags.
+	uint16_t   dlgOpt;                     Delegation options
+	uint16_t   dlgth;                      Length of Deleg field buffer.
+	uint8_t    deleg[];                    Deleg field buffer ( one or more bytes of GSS-API data)
+};
+*/
+#define GSSAPI_CHECKSUM_SIZE (12+GSSAPI_BNDLENGTH)
+
+static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context,
+                                         krb5_auth_context *auth_context,
+                                         krb5_creds *credsp,
+                                         krb5_ccache ccache,
+                                         krb5_data *authenticator);
+
 /**************************************************************
  Wrappers around kerberos string functions that convert from
  utf8 -> unix charset and vica versa.
@@ -636,6 +661,8 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
 	bool creds_ready = False;
 	int i = 0, maxtries = 3;
 	
+	ZERO_STRUCT(in_data);
+
 	retval = smb_krb5_parse_name(context, principal, &server);
 	if (retval) {
 		DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
@@ -691,14 +718,66 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
 		*expire_time = (time_t)credsp->times.endtime;
 	}
 
-	in_data.length = 0;
+	/* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
+	 as part of the kerberos exchange. */
+	if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
+		DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n")  );
+
+		if( *auth_context == NULL ) {
+			/* Allocate if it has not yet been allocated. */
+			retval = krb5_auth_con_init( context, auth_context );
+			if (retval) {
+				DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_init failed (%s)\n",
+					error_message(retval)));
+				goto cleanup_creds;
+			}
+		}
+
+		retval = krb5_auth_con_setuseruserkey( context, *auth_context, &credsp->keyblock );
+		if (retval) {
+			DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_setuseruserkey failed (%s)\n",
+				error_message(retval)));
+			goto cleanup_creds;
+		}
+
+		/* Must use a subkey for forwarded tickets. */
+		retval = krb5_auth_con_setflags( context, *auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY);
+		if (retval) {
+			DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_setflags failed (%s)\n",
+				error_message(retval)));
+			goto cleanup_creds;
+		}
+
+		retval = ads_krb5_get_fwd_ticket( context,
+						auth_context,
+						credsp,
+						ccache,
+						&in_data );
+		if (retval) {
+			DEBUG( 1, ("ads_krb5_get_fwd_ticket failed (%s)\n", error_message( retval ) ) );
+			goto cleanup_creds;
+		}
+		/* We need to do this in order to allow our GSS-API: */
+		retval = krb5_auth_con_set_req_cksumtype( context, *auth_context, GSSAPI_CHECKSUM );
+		if (retval) {
+			DEBUG( 1, ("krb5_auth_con_set_req_cksumtype failed (%s)\n",
+				error_message( retval ) ) );
+			goto cleanup_creds;
+		}
+	}
+
 	retval = krb5_mk_req_extended(context, auth_context, ap_req_options, 
 				      &in_data, credsp, outbuf);
 	if (retval) {
 		DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", 
 			 error_message(retval)));
 	}
-	
+
+	if (in_data.data) {
+		free( in_data.data );
+		in_data.length = 0;
+	}
+
 	krb5_free_creds(context, credsp);
 
 cleanup_creds:
@@ -1704,6 +1783,167 @@ done:
  	return ret;
 }
 
+
+
+/**************************************************************
+Routine: ads_krb5_get_fwd_ticket
+ Description:
+    When a service ticket is flagged as trusted
+    for delegation we should provide a forwardable
+    ticket so that the remote host can act on our
+    behalf.  This is done by taking the 2nd forwardable
+    TGT and storing it in the GSS-API authenticator
+    "checksum".  This routine will populate
+    the krb5_data authenticator with this TGT.
+ Parameters:
+    krb5_context context: The kerberos context for this authentication.
+    krb5_auth_context:    The authentication context.
+    krb5_creds *credsp:   The ticket credentials (AS-REP).
+    krb5_ccache ccache:   The credentials cache.
+    krb5_data &authenticator: The checksum field that will store the TGT, and
+     authenticator.data must be freed by the caller.
+
+ Returns:
+    krb5_error_code: 0 if no errors, otherwise set.
+**************************************************************/
+
+static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context,
+					 krb5_auth_context *auth_context,
+					 krb5_creds *credsp,
+					 krb5_ccache ccache,
+					 krb5_data *authenticator)
+{
+	krb5_data fwdData;
+    	krb5_creds **inputCreds = NULL;
+	krb5_error_code retval = 0;
+    	krb5_data *pKrbCred  = NULL;
+	char *pChksum = NULL;
+	char *p = NULL;
+
+	ZERO_STRUCT(fwdData);
+	ZERO_STRUCTP(authenticator);
+
+	retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
+				*auth_context,  /* Authentication context [in] */
+				CONST_DISCARD(char *, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
+				credsp->client, /* Client principal for the tgt [in] */
+				credsp->server, /* Server principal for the tgt [in] */
+				ccache,         /* Credential cache to use for storage [in] */
+				1,              /* Turn on for "Forwardable ticket" [in] */
+				&fwdData );     /* Resulting response [out] */
+
+	if (retval) {
+		DEBUG(1,("ads_krb5_get_fwd_ticket: krb5_fwd_tgt_creds failed (%s)\n", 
+			error_message(retval)));
+		goto out;
+	}
+
+    	/* Read the forward data response and extract a credentials block. */
+    	retval = krb5_rd_cred( context, *auth_context, &fwdData, &inputCreds, NULL );
+
+    	/* Remove that input data, we never needed it anyway. */
+    	if (fwdData.length > 0) {
+      		krb5_free_data_contents( context, &fwdData );
+    	}
+
+	if (retval) {
+		DEBUG(1,("ads_krb5_get_fwd_ticket: krb5_rd_cred failed (%s)\n",
+       					error_message(retval)));
+		goto out;
+	}
+
+	/* The mk_1cred will be a single credential ticket that will contain all the
+	   information needed to hand to a remote host so they can work on behalf of
+	   us (power of attorney I believe its called).  The mk_1cred will build
+	   what is known as a KRB_CRED structure, or an ASN.1 APPLICATION 22 message:
+
+		RFC-1510 page 63:
+			KRB-CRED         ::= [APPLICATION 22]   SEQUENCE {
+				pvno[0]                INTEGER,
+				msg-type[1]            INTEGER, -- KRB_CRED
+				tickets[2]             SEQUENCE OF Ticket,
+				enc-part[3]            EncryptedData
+			}
+
+	  That will be represented here:
+	*/
+
+	retval = krb5_mk_1cred(context,
+				*auth_context,
+				*inputCreds,    /* INPUT (this is what we're using as a source) */
+				&pKrbCred,      /* OUTPUT (our resulting KRB_CRED structure) */
+				NULL);
+
+	if (retval) {
+		DEBUG(1,("ads_krb5_get_fwd_ticket: krb5_mk_1cred failed (%s)\n", 
+		 					error_message(retval)));
+		return retval;
+	}
+
+	/* Paranoia check for integer wrap. */
+	if ((unsigned int)GSSAPI_CHECKSUM_SIZE + (unsigned int)pKrbCred->length <
+		(unsigned int)GSSAPI_CHECKSUM_SIZE) {
+		retval = EINVAL;
+		goto out;
+	}
+
+	/* We're going to allocate a gssChecksum structure with a little
+	   extra data the length of the kerberos credentials length
+	   (APPLICATION 22) so that we can pack it on the end of the structure.
+	*/
+
+	pChksum	= SMB_MALLOC(GSSAPI_CHECKSUM_SIZE + pKrbCred->length );
+	if (!pChksum) {
+		retval = ENOMEM;
+		goto out;
+	}
+
+	p = pChksum;
+
+	SIVAL(p, 0, GSSAPI_BNDLENGTH);
+	p += 4;
+
+	/* Zero out the bindings fields */
+	memset(p, 0x0, GSSAPI_BNDLENGTH );
+	p += GSSAPI_BNDLENGTH;
+
+	SIVAL(p, 0, GSS_C_DELEG_FLAG );
+	p += 4;
+	SSVAL(p, 0, 1 );
+	p += 2;
+	SSVAL(p, 0, pKrbCred->length );
+	p += 2;
+
+	/* Migrate the kerberos KRB_CRED data to the checksum delegation */
+	memcpy(p, pKrbCred->data, pKrbCred->length );
+	p += pKrbCred->length;
+
+	/* We need to do this in order to allow our GSS-API  */
+	retval = krb5_auth_con_set_req_cksumtype( context, *auth_context, GSSAPI_CHECKSUM );
+	if (retval) {
+		goto out;
+	}
+
+	/* We now have a service ticket, now turn it into an AP-REQ. */
+	authenticator->length = ntohs(pKrbCred->length + GSSAPI_CHECKSUM_SIZE);
+
+	/* Caller should call free() when they're done with this. */
+	authenticator->data = (char *)pChksum;
+
+  out:
+
+	if (pKrbCred && (pKrbCred->length > 0)) {
+		krb5_free_data( context, pKrbCred );
+	}
+
+	if (inputCreds) {
+		/* This free's the triple pointer mess. */
+		krb5_free_tgt_creds( context, inputCreds );
+	}
+
+	return retval;
+}
+
 #else /* HAVE_KRB5 */
  /* this saves a few linking headaches */
  int cli_krb5_get_ticket(const char *principal, time_t time_offset, 


More information about the samba-technical mailing list