r23922: implement input buffer handling for the SASL read wrapper
authorStefan Metzmacher <metze@samba.org>
Tue, 17 Jul 2007 10:13:53 +0000 (10:13 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:28:42 +0000 (12:28 -0500)
metze
(This used to be commit 7d8518ebd9470062b499b7074a940e14520e99f2)

source3/include/ads.h
source3/libads/sasl_wrapping.c

index 5c15359c410c697d8d9ec9b96a87384aab3740a6..8404ad907b50477ed20b4633c46e623b1021a480 100644 (file)
@@ -15,7 +15,31 @@ enum wb_posix_mapping {
        WB_POSIX_MAP_UNIXINFO   = 4
 };
 
-typedef struct {       
+/* there are 5 possible types of errors the ads subsystem can produce */
+enum ads_error_type {ENUM_ADS_ERROR_KRB5, ENUM_ADS_ERROR_GSS, 
+                    ENUM_ADS_ERROR_LDAP, ENUM_ADS_ERROR_SYSTEM, ENUM_ADS_ERROR_NT};
+
+typedef struct {
+       enum ads_error_type error_type;
+       union err_state{                
+               int rc;
+               NTSTATUS nt_status;
+       } err;
+       /* For error_type = ENUM_ADS_ERROR_GSS minor_status describe GSS API error */
+       /* Where rc represents major_status of GSS API error */
+       int minor_status;
+} ADS_STATUS;
+
+struct ads_struct;
+
+struct ads_saslwrap_ops {
+       const char *name;
+       ADS_STATUS (*wrap)(struct ads_struct *);
+       ADS_STATUS (*unwrap)(struct ads_struct *);
+       ADS_STATUS (*disconnect)(struct ads_struct *);
+};
+
+typedef struct ads_struct {
        int is_mine;    /* do I own this structure's memory? */
        
        /* info needed to find the server */
@@ -63,6 +87,27 @@ typedef struct {
 
 #ifdef HAVE_LDAP_SASL_WRAPPING
                Sockbuf_IO_Desc *sbiod; /* lowlevel state for LDAP wrapping */
+               TALLOC_CTX *mem_ctx;
+               const struct ads_saslwrap_ops *wrap_ops;
+               void *wrap_private_data;
+               struct {
+                       uint32 ofs;
+                       uint32 needed;
+                       uint32 left;
+                       uint32 max;
+                       uint32 min;
+                       uint32 size;
+                       uint8 *buf;
+               } in;
+               struct {
+                       uint32 ofs;
+                       uint32 needed;
+                       uint32 left;
+                       uint32 max;
+                       uint32 min;
+                       uint32 size;
+                       uint8 *buf;
+               } out;
 #endif /* HAVE_LDAP_SASL_WRAPPING */
        } ldap;
 #endif /* HAVE_LDAP */
@@ -80,20 +125,6 @@ struct posix_schema {
 };
 
 
-/* there are 5 possible types of errors the ads subsystem can produce */
-enum ads_error_type {ENUM_ADS_ERROR_KRB5, ENUM_ADS_ERROR_GSS, 
-                    ENUM_ADS_ERROR_LDAP, ENUM_ADS_ERROR_SYSTEM, ENUM_ADS_ERROR_NT};
-
-typedef struct {
-       enum ads_error_type error_type;
-       union err_state{                
-               int rc;
-               NTSTATUS nt_status;
-       } err;
-       /* For error_type = ENUM_ADS_ERROR_GSS minor_status describe GSS API error */
-       /* Where rc represents major_status of GSS API error */
-       int minor_status;
-} ADS_STATUS;
 
 #ifdef HAVE_ADS
 typedef LDAPMod **ADS_MODLIST;
index 4a26afe740242d14588cfe0b252cb5192c9135a9..9617961969fd07f5d4c40b02bc301aa9c87eda63 100644 (file)
@@ -37,9 +37,137 @@ static int ads_saslwrap_remove(Sockbuf_IO_Desc *sbiod)
        return 0;
 }
 
+static ber_slen_t ads_saslwrap_prepare_inbuf(ADS_STRUCT *ads)
+{
+       ads->ldap.in.ofs        = 0;
+       ads->ldap.in.needed     = 0;
+       ads->ldap.in.left       = 0;
+       ads->ldap.in.size       = 4 + ads->ldap.in.min;
+       ads->ldap.in.buf        = talloc_array(ads->ldap.mem_ctx,
+                                              uint8, ads->ldap.in.size);
+       if (!ads->ldap.in.buf) {
+               return -1;
+       }
+
+       return 0;
+}
+
+static ber_slen_t ads_saslwrap_grow_inbuf(ADS_STRUCT *ads)
+{
+       if (ads->ldap.in.size == (4 + ads->ldap.in.needed)) {
+               return 0;
+       }
+
+       ads->ldap.in.size       = 4 + ads->ldap.in.needed;
+       ads->ldap.in.buf        = talloc_realloc(ads->ldap.mem_ctx,
+                                                ads->ldap.in.buf,
+                                                uint8, ads->ldap.in.size);
+       if (!ads->ldap.in.buf) {
+               return -1;
+       }
+
+       return 0;
+}
+
+static void ads_saslwrap_shrink_inbuf(ADS_STRUCT *ads)
+{
+       talloc_free(ads->ldap.in.buf);
+
+       ads->ldap.in.buf        = NULL;
+       ads->ldap.in.size       = 0;
+       ads->ldap.in.ofs        = 0;
+       ads->ldap.in.needed     = 0;
+       ads->ldap.in.left       = 0;
+}
+
 static ber_slen_t ads_saslwrap_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
 {
-       return LBER_SBIOD_READ_NEXT(sbiod, buf, len);
+       ADS_STRUCT *ads = (ADS_STRUCT *)sbiod->sbiod_pvt;
+       ber_slen_t ret;
+
+       /* If ofs < 4 it means we don't have read the length header yet */
+       if (ads->ldap.in.ofs < 4) {
+               ret = ads_saslwrap_prepare_inbuf(ads);
+               if (ret < 0) return ret;
+
+               ret = LBER_SBIOD_READ_NEXT(sbiod,
+                                          ads->ldap.in.buf + ads->ldap.in.ofs,
+                                          4 - ads->ldap.in.ofs);
+               if (ret < 0) return ret;
+               ads->ldap.in.ofs += ret;
+
+               if (ads->ldap.in.ofs < 4) goto eagain;
+
+               ads->ldap.in.needed = RIVAL(ads->ldap.in.buf, 4);
+               if (ads->ldap.in.needed > ads->ldap.in.max) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (ads->ldap.in.needed < ads->ldap.in.min) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               ret = ads_saslwrap_grow_inbuf(ads);
+               if (ret < 0) return ret;
+       }
+
+       /*
+        * if there's more data needed from the remote end,
+        * we need to read more
+        */
+       if (ads->ldap.in.needed > 0) {
+               ret = LBER_SBIOD_READ_NEXT(sbiod,
+                                          ads->ldap.in.buf + ads->ldap.in.ofs,
+                                          ads->ldap.in.needed);
+               if (ret < 0) return ret;
+               ads->ldap.in.ofs += ret;
+               ads->ldap.in.needed -= ret;
+
+               if (ads->ldap.in.needed > 0) goto eagain;
+       }
+
+       /*
+        * if we have a complete packet and have not yet unwrapped it
+        * we need to call the mech specific unwrap() hook
+        */
+       if (ads->ldap.in.needed == 0 && ads->ldap.in.left == 0) {
+               ADS_STATUS status;
+               status = ads->ldap.wrap_ops->unwrap(ads);
+               if (!ADS_ERR_OK(status)) {
+                       errno = EACCES;
+                       return -1;
+               }
+       }
+
+       /*
+        * if we have unwrapped data give it to the caller
+        */
+       if (ads->ldap.in.left > 0) {
+               ret = MIN(ads->ldap.in.left, len);
+               memcpy(buf, ads->ldap.in.buf + ads->ldap.in.ofs, ret);
+               ads->ldap.in.ofs += ret;
+               ads->ldap.in.left -= ret;
+
+               /*
+                * if no more is left shrink the inbuf,
+                * this will trigger reading a new SASL packet
+                * from the remote stream in the next call
+                */
+               if (ads->ldap.in.left == 0) {
+                       ads_saslwrap_shrink_inbuf(ads);
+               }
+
+               return ret;
+       }
+
+       /*
+        * if we don't have anything for the caller yet,
+        * tell him to ask again
+        */
+eagain:
+       errno = EAGAIN;
+       return -1;
 }
 
 static ber_slen_t ads_saslwrap_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
@@ -47,7 +175,6 @@ static ber_slen_t ads_saslwrap_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_
        return LBER_SBIOD_WRITE_NEXT(sbiod, buf, len);
 }
 
-
 static int ads_saslwrap_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
 {
        return LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);