diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/ads.h | 61 | ||||
-rw-r--r-- | source3/libads/sasl_wrapping.c | 131 |
2 files changed, 175 insertions, 17 deletions
diff --git a/source3/include/ads.h b/source3/include/ads.h index 5c15359c41..8404ad907b 100644 --- a/source3/include/ads.h +++ b/source3/include/ads.h @@ -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; diff --git a/source3/libads/sasl_wrapping.c b/source3/libads/sasl_wrapping.c index 4a26afe740..9617961969 100644 --- a/source3/libads/sasl_wrapping.c +++ b/source3/libads/sasl_wrapping.c @@ -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); |