[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [SAMBA4][PATCH] Fix up AES sign/seal on DCE/RPC



On Sat, 2005-09-10 at 10:57 +1000, Andrew Bartlett wrote:
> This patch, inspired by the lukeh's heimdal-mechglue branch appears to
> fix up gesnec_gssapi for sign/seal, samba->samba when using AES.  It
> also works Samba->Windows and Windows->Samba.
> 
> However, I'm not sure if I've broken the original intent of this
> function, and it might make more sense to have a direct
> gss_sig_length(conext, data_len, &length) function.  I'm going to have a
> look at that approach now.

This patch adds a new function:
OM_uint32
gss_wrap_size (
            OM_uint32 * /*minor_status*/,
            const gss_ctx_id_t /*context_handle*/,
            int /*conf_req_flag*/,
            gss_qop_t /*qop_req*/,
            OM_uint32 /*req_input_size*/,
            OM_uint32 * /*output_size*/
        );

This tells the caller what the wrapped size would be, given an input
size.  From there, I can tell what the 'signature' portion would be.

My testing so far has been on AES and ARCFOUR, where this seems to match
up with the results of the actual sealing.  I'm looking for comments on
this patch. (as well as any hints towards any testing setup that may
already exist for the size_limit function).

Andrew Bartlett

-- 
Andrew Bartlett                                http://samba.org/~abartlet/
Samba Developer, SuSE Labs, Novell Inc.        http://suse.de
Authentication Developer, Samba Team           http://samba.org
Student Network Administrator, Hawker College  http://hawkerc.net
Index: auth/gensec/gensec.c
===================================================================
--- auth/gensec/gensec.c	(revision 10115)
+++ auth/gensec/gensec.c	(working copy)
@@ -559,7 +559,7 @@
 	return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
 }
 
-size_t gensec_sig_size(struct gensec_security *gensec_security) 
+size_t gensec_sig_size(struct gensec_security *gensec_security, size_t data_size) 
 {
 	if (!gensec_security->ops->sig_size) {
 		return 0;
@@ -568,7 +568,7 @@
 		return 0;
 	}
 	
-	return gensec_security->ops->sig_size(gensec_security);
+	return gensec_security->ops->sig_size(gensec_security, data_size);
 }
 
 NTSTATUS gensec_wrap(struct gensec_security *gensec_security, 
Index: auth/gensec/gensec.h
===================================================================
--- auth/gensec/gensec.h	(revision 10115)
+++ auth/gensec/gensec.h	(working copy)
@@ -73,7 +73,7 @@
 				const uint8_t *data, size_t length, 
 				const uint8_t *whole_pdu, size_t pdu_length, 
 				DATA_BLOB *sig);
-	size_t   (*sig_size)(struct gensec_security *gensec_security);
+	size_t   (*sig_size)(struct gensec_security *gensec_security, size_t data_size);
 	NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, 
 				 const uint8_t *data, size_t length, 
 				 const uint8_t *whole_pdu, size_t pdu_length, 
Index: auth/gensec/gensec_gssapi.c
===================================================================
--- auth/gensec/gensec_gssapi.c	(revision 10115)
+++ auth/gensec/gensec_gssapi.c	(working copy)
@@ -480,10 +480,31 @@
 	return NT_STATUS_OK;
 }
 
-static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security) 
+static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size) 
 {
-	/* not const but work for DCERPC packets and arcfour */
-	return 45;
+	struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
+	OM_uint32 maj_stat, min_stat;
+	OM_uint32 output_size;
+	maj_stat = gss_wrap_size(&min_stat, 
+				 gensec_gssapi_state->gssapi_context,
+				 gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
+				 GSS_C_QOP_DEFAULT,
+				 data_size, 
+				 &output_size);
+	if (GSS_ERROR(maj_stat)) {
+		TALLOC_CTX *mem_ctx = talloc_new(NULL); 
+		DEBUG(1, ("gensec_gssapi_seal_packet: determinaing signature size with gss_wrap_size_limit failed: %s\n", 
+			  gssapi_error_string(mem_ctx, maj_stat, min_stat)));
+		talloc_free(mem_ctx);
+		return 0;
+	}
+
+	if (output_size < data_size) {
+		return 0;
+	}
+
+	/* The difference between the max output and the max input must be the signature */
+	return output_size - data_size;
 }
 
 static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
@@ -496,7 +517,7 @@
 	OM_uint32 maj_stat, min_stat;
 	gss_buffer_desc input_token, output_token;
 	int conf_state;
-	ssize_t sig_length = 0;
+	ssize_t sig_length;
 
 	input_token.length = length;
 	input_token.value = data;
@@ -514,12 +535,15 @@
 		return NT_STATUS_ACCESS_DENIED;
 	}
 
-	if (output_token.length < length) {
+	sig_length = gensec_gssapi_sig_size(gensec_security, length);
+
+	/* Caller must pad to right boundary */
+	if (output_token.length != (length + sig_length)) {
+		DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap length [%d] does not match caller length [%d] plus sig size [%d] = [%d]\n", 
+			  output_token.length, length, sig_length, length + sig_length));
 		return NT_STATUS_INTERNAL_ERROR;
 	}
 
-	sig_length = 45;
-
 	memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
 	*sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
 
@@ -618,7 +642,7 @@
 		return NT_STATUS_INTERNAL_ERROR;
 	}
 
-	sig_length = 45;
+	sig_length = gensec_gssapi_sig_size(gensec_security, length);
 
 	/*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/
 	*sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
Index: auth/gensec/schannel.c
===================================================================
--- auth/gensec/schannel.c	(revision 10115)
+++ auth/gensec/schannel.c	(working copy)
@@ -26,7 +26,7 @@
 #include "auth/auth.h"
 #include "auth/gensec/schannel.h"
 
-static size_t schannel_sig_size(struct gensec_security *gensec_security)
+static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
 {
 	return 32;
 }
Index: auth/gensec/spnego.c
===================================================================
--- auth/gensec/spnego.c	(revision 10115)
+++ auth/gensec/spnego.c	(working copy)
@@ -198,7 +198,7 @@
 			     mem_ctx, in, out);
 }
 
-static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security) 
+static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size) 
 {
 	struct spnego_state *spnego_state = gensec_security->private_data;
 
@@ -207,7 +207,7 @@
 		return 0;
 	}
 	
-	return gensec_sig_size(spnego_state->sub_sec_security);
+	return gensec_sig_size(spnego_state->sub_sec_security, data_size);
 }
 
 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, 
Index: heimdal/lib/gssapi/wrap.c
===================================================================
--- heimdal/lib/gssapi/wrap.c	(revision 10115)
+++ heimdal/lib/gssapi/wrap.c	(working copy)
@@ -120,7 +120,7 @@
 }
 
 static OM_uint32
-sub_wrap_size (
+sub_wrap_size_limit (
             OM_uint32 req_output_size,
             OM_uint32 * max_input_size,
 	    int blocksize,
@@ -156,6 +156,8 @@
   krb5_keyblock *key;
   OM_uint32 ret;
   krb5_keytype keytype;
+  OM_uint32 output_size;
+  OM_uint32 blocksize;
 
   ret = gss_krb5_get_subkey(context_handle, &key);
   if (ret) {
@@ -167,17 +169,102 @@
 
   switch (keytype) {
   case KEYTYPE_DES :
+      ret = sub_wrap_size_limit(req_output_size, max_input_size, 8, 22);
+      break;
+  case KEYTYPE_DES3 :
+      ret = sub_wrap_size_limit(req_output_size, max_input_size, 8, 34);
+      break;
   case KEYTYPE_ARCFOUR:
   case KEYTYPE_ARCFOUR_56:
-      ret = sub_wrap_size(req_output_size, max_input_size, 8, 22);
+      ret = _gssapi_wrap_size_arcfour(minor_status, context_handle, 
+				      conf_req_flag, qop_req, 
+				      req_output_size, &output_size, 
+				      &blocksize, key);
+      
+      if (output_size > req_output_size) {
+	      *max_input_size = req_output_size - (output_size - req_output_size);
+	      (*max_input_size) &= (~(OM_uint32)(blocksize - 1));
+      } else {
+	      *max_input_size = 0;
+      }
       break;
+  default :
+      ret = _gssapi_wrap_size_cfx(minor_status, context_handle, 
+				  conf_req_flag, qop_req, 
+				  req_output_size, &output_size, 
+				  &blocksize, key);
+      if (output_size > req_output_size) {
+	      *max_input_size = req_output_size - (output_size - req_output_size);
+	      (*max_input_size) &= (~(OM_uint32)(blocksize - 1));
+      } else {
+	      *max_input_size = 0;
+      }
+      break;
+  }
+  krb5_free_keyblock (gssapi_krb5_context, key);
+  *minor_status = 0;
+  return ret;
+}
+
+static OM_uint32
+sub_wrap_size (
+            OM_uint32 req_input_size,
+            OM_uint32 * output_size,
+	    int blocksize,
+	    int extrasize
+           )
+{
+    size_t len, total_len, padlength, datalen;
+
+    padlength = blocksize - (req_input_size % blocksize);
+    datalen = req_input_size + padlength + 8;
+    len = datalen + extrasize;
+    gssapi_krb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
+    
+    *output_size = total_len;
+
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gss_wrap_size (
+            OM_uint32 * minor_status,
+            const gss_ctx_id_t context_handle,
+            int conf_req_flag,
+            gss_qop_t qop_req,
+            OM_uint32 req_input_size,
+            OM_uint32 * output_size
+           )
+{
+  krb5_keyblock *key;
+  OM_uint32 ret, padlen;
+  krb5_keytype keytype;
+
+  ret = gss_krb5_get_subkey(context_handle, &key);
+  if (ret) {
+      gssapi_krb5_set_error_string ();
+      *minor_status = ret;
+      return GSS_S_FAILURE;
+  }
+  krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype);
+
+  switch (keytype) {
+  case KEYTYPE_DES :
+      ret = sub_wrap_size(req_input_size, output_size, 8, 22);
+      break;
   case KEYTYPE_DES3 :
-      ret = sub_wrap_size(req_output_size, max_input_size, 8, 34);
+      ret = sub_wrap_size(req_input_size, output_size, 8, 34);
       break;
+  case KEYTYPE_ARCFOUR:
+  case KEYTYPE_ARCFOUR_56:
+      ret = _gssapi_wrap_size_arcfour(minor_status, context_handle, 
+				      conf_req_flag, qop_req, 
+				      req_input_size, output_size, &padlen, key);
+      break;
   default :
       ret = _gssapi_wrap_size_cfx(minor_status, context_handle, 
 				  conf_req_flag, qop_req, 
-				  req_output_size, max_input_size, key);
+				  req_input_size, output_size, &padlen, key);
       break;
   }
   krb5_free_keyblock (gssapi_krb5_context, key);
Index: heimdal/lib/gssapi/cfx.c
===================================================================
--- heimdal/lib/gssapi/cfx.c	(revision 10115)
+++ heimdal/lib/gssapi/cfx.c	(working copy)
@@ -48,7 +48,8 @@
 		size_t input_length,
 		size_t *output_length,
 		size_t *cksumsize,
-		u_int16_t *padlength)
+		u_int16_t *padlength, 
+	        size_t *padsize)
 {
     krb5_error_code ret;
     krb5_cksumtype type;
@@ -68,18 +69,17 @@
     }
 
     if (conf_req_flag) {
-	size_t padsize;
 
 	/* Header is concatenated with data before encryption */
 	input_length += sizeof(gss_cfx_wrap_token_desc);
 
-	ret = krb5_crypto_getpadsize(gssapi_krb5_context, crypto, &padsize);
+	ret = krb5_crypto_getpadsize(gssapi_krb5_context, crypto, padsize);
 	if (ret) {
 	    return ret;
 	}
 	if (padsize > 1) {
 	    /* XXX check this */
-	    *padlength = padsize - (input_length % padsize);
+	    *padlength = *padsize - (input_length % *padsize);
 	}
 
 	/* We add the pad ourselves (noted here for completeness only) */
@@ -90,6 +90,7 @@
     } else {
 	/* Checksum is concatenated with data */
 	*output_length += input_length + *cksumsize;
+	*padsize = 0;
     }
 
     assert(*output_length > input_length);
@@ -101,13 +102,15 @@
 				const gss_ctx_id_t context_handle,
 				int conf_req_flag,
 				gss_qop_t qop_req,
-				OM_uint32 req_output_size,
-				OM_uint32 *max_input_size,
+				OM_uint32 req_input_size,
+				OM_uint32 *output_len,
+				OM_uint32 *padsize,
 				krb5_keyblock *key)
 {
     krb5_error_code ret;
     krb5_crypto crypto;
-    u_int16_t padlength;
+    u_int16_t pad_length;
+    size_t pad_size;
     size_t output_length, cksumsize;
 
     ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
@@ -118,8 +121,8 @@
     }
 
     ret = wrap_length_cfx(crypto, conf_req_flag, 
-			  req_output_size,
-			  &output_length, &cksumsize, &padlength);
+			  req_input_size,
+			  &output_length, &cksumsize, &pad_length, &pad_size);
     if (ret != 0) {
 	gssapi_krb5_set_error_string();
 	*minor_status = ret;
@@ -127,13 +130,8 @@
 	return GSS_S_FAILURE;
     }
 
-    if (output_length < req_output_size) {
-	*max_input_size = (req_output_size - output_length);
-	*max_input_size -= padlength;
-    } else {
-	/* Should this return an error? */
-	*max_input_size = 0;
-    }
+    *output_len = output_length;
+    *padsize = pad_size;
 
     krb5_crypto_destroy(gssapi_krb5_context, crypto);
 
@@ -201,7 +199,7 @@
     krb5_data cipher;
     size_t wrapped_len, cksumsize;
     u_int16_t padlength, rrc = 0;
-    OM_uint32 seq_number;
+    OM_uint32 seq_number, padsize;
     u_char *p;
 
     ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
@@ -213,7 +211,7 @@
 
     ret = wrap_length_cfx(crypto, conf_req_flag, 
 			  input_message_buffer->length,
-			  &wrapped_len, &cksumsize, &padlength);
+			  &wrapped_len, &cksumsize, &padlength, &padsize);
     if (ret != 0) {
 	gssapi_krb5_set_error_string();
 	*minor_status = ret;
Index: heimdal/lib/gssapi/cfx.h
===================================================================
--- heimdal/lib/gssapi/cfx.h	(revision 10115)
+++ heimdal/lib/gssapi/cfx.h	(working copy)
@@ -66,8 +66,9 @@
 				const gss_ctx_id_t context_handle,
 				int conf_req_flag,
 				gss_qop_t qop_req,
-				OM_uint32 req_output_size,
-				OM_uint32 *max_input_size,
+				OM_uint32 req_input_size,
+				OM_uint32 *output_len,
+				OM_uint32 *padlen,
 				krb5_keyblock *key);
 
 OM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status,
Index: heimdal/lib/gssapi/gssapi.h
===================================================================
--- heimdal/lib/gssapi/gssapi.h	(revision 10115)
+++ heimdal/lib/gssapi/gssapi.h	(working copy)
@@ -628,6 +628,16 @@
             int * /*open_context*/
            );
 
+OM_uint32
+gss_wrap_size (
+            OM_uint32 * /*minor_status*/,
+            const gss_ctx_id_t /*context_handle*/,
+            int /*conf_req_flag*/,
+            gss_qop_t /*qop_req*/,
+            OM_uint32 /*req_input_size*/,
+            OM_uint32 * /*output_size*/
+	);
+
 OM_uint32 gss_wrap_size_limit (
             OM_uint32 * /*minor_status*/,
             const gss_ctx_id_t /*context_handle*/,
Index: heimdal/lib/gssapi/arcfour.c
===================================================================
--- heimdal/lib/gssapi/arcfour.c	(revision 10115)
+++ heimdal/lib/gssapi/arcfour.c	(working copy)
@@ -326,6 +326,37 @@
 }
 
 OM_uint32
+_gssapi_wrap_size_arcfour(OM_uint32 * minor_status,
+			  const gss_ctx_id_t context_handle,
+			  int conf_req_flag,
+			  gss_qop_t qop_req,
+			  OM_uint32 req_input_size,
+			  OM_uint32 * output_size,
+			  OM_uint32 * padlen,
+			  krb5_keyblock *key)
+{
+    size_t len, total_len, datalen;
+    *padlen = 0;
+    datalen = req_input_size;
+    len = GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+    /* if GSS_C_DCE_STYLE is in use:
+     *  - we only need to encapsulate the WRAP token
+     *  - we should not add padding
+     */
+    if (!(context_handle->flags & GSS_C_DCE_STYLE)) {
+    	datalen += 1 /* padding */;
+    	len += datalen;
+    }
+    _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+    if (context_handle->flags & GSS_C_DCE_STYLE) {
+    	total_len += datalen;
+    }
+
+    *output_size = total_len;
+    return GSS_S_COMPLETE;
+}
+	
+OM_uint32
 _gssapi_wrap_arcfour(OM_uint32 * minor_status,
 		     const gss_ctx_id_t context_handle,
 		     int conf_req_flag,
Index: heimdal/lib/gssapi/arcfour.h
===================================================================
--- heimdal/lib/gssapi/arcfour.h	(revision 10115)
+++ heimdal/lib/gssapi/arcfour.h	(working copy)
@@ -70,5 +70,14 @@
 				     gss_qop_t *qop_state,
 				     krb5_keyblock *key,
 				     char *type);
+OM_uint32
+_gssapi_wrap_size_arcfour(OM_uint32 * minor_status,
+			  const gss_ctx_id_t context_handle,
+			  int conf_req_flag,
+			  gss_qop_t qop_req,
+			  OM_uint32 req_input_size,
+			  OM_uint32 * output_size,
+			  OM_uint32 * padlen,
+			  krb5_keyblock *key);
 
 #endif /* GSSAPI_ARCFOUR_H_ */

This is a digitally signed message part