1/* 2 Unix SMB/CIFS implementation. 3 ads sasl wrapping code 4 Copyright (C) Stefan Metzmacher 2007 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "includes.h" 21 22#ifdef HAVE_LDAP_SASL_WRAPPING 23 24static int ads_saslwrap_setup(Sockbuf_IO_Desc *sbiod, void *arg) 25{ 26 ADS_STRUCT *ads = (ADS_STRUCT *)arg; 27 28 ads->ldap.sbiod = sbiod; 29 30 sbiod->sbiod_pvt = ads; 31 32 return 0; 33} 34 35static int ads_saslwrap_remove(Sockbuf_IO_Desc *sbiod) 36{ 37 return 0; 38} 39 40static ber_slen_t ads_saslwrap_prepare_inbuf(ADS_STRUCT *ads) 41{ 42 ads->ldap.in.ofs = 0; 43 ads->ldap.in.needed = 0; 44 ads->ldap.in.left = 0; 45 ads->ldap.in.size = 4 + ads->ldap.in.min_wrapped; 46 ads->ldap.in.buf = talloc_array(ads->ldap.mem_ctx, 47 uint8, ads->ldap.in.size); 48 if (!ads->ldap.in.buf) { 49 return -1; 50 } 51 52 return 0; 53} 54 55static ber_slen_t ads_saslwrap_grow_inbuf(ADS_STRUCT *ads) 56{ 57 if (ads->ldap.in.size == (4 + ads->ldap.in.needed)) { 58 return 0; 59 } 60 61 ads->ldap.in.size = 4 + ads->ldap.in.needed; 62 ads->ldap.in.buf = talloc_realloc(ads->ldap.mem_ctx, 63 ads->ldap.in.buf, 64 uint8, ads->ldap.in.size); 65 if (!ads->ldap.in.buf) { 66 return -1; 67 } 68 69 return 0; 70} 71 72static void ads_saslwrap_shrink_inbuf(ADS_STRUCT *ads) 73{ 74 talloc_free(ads->ldap.in.buf); 75 76 ads->ldap.in.buf = NULL; 77 ads->ldap.in.size = 0; 78 ads->ldap.in.ofs = 0; 79 ads->ldap.in.needed = 0; 80 ads->ldap.in.left = 0; 81} 82 83static ber_slen_t ads_saslwrap_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 84{ 85 ADS_STRUCT *ads = (ADS_STRUCT *)sbiod->sbiod_pvt; 86 ber_slen_t ret; 87 88 /* If ofs < 4 it means we don't have read the length header yet */ 89 if (ads->ldap.in.ofs < 4) { 90 ret = ads_saslwrap_prepare_inbuf(ads); 91 if (ret < 0) return ret; 92 93 ret = LBER_SBIOD_READ_NEXT(sbiod, 94 ads->ldap.in.buf + ads->ldap.in.ofs, 95 4 - ads->ldap.in.ofs); 96 if (ret <= 0) return ret; 97 ads->ldap.in.ofs += ret; 98 99 if (ads->ldap.in.ofs < 4) goto eagain; 100 101 ads->ldap.in.needed = RIVAL(ads->ldap.in.buf, 0); 102 if (ads->ldap.in.needed > ads->ldap.in.max_wrapped) { 103 errno = EINVAL; 104 return -1; 105 } 106 if (ads->ldap.in.needed < ads->ldap.in.min_wrapped) { 107 errno = EINVAL; 108 return -1; 109 } 110 111 ret = ads_saslwrap_grow_inbuf(ads); 112 if (ret < 0) return ret; 113 } 114 115 /* 116 * if there's more data needed from the remote end, 117 * we need to read more 118 */ 119 if (ads->ldap.in.needed > 0) { 120 ret = LBER_SBIOD_READ_NEXT(sbiod, 121 ads->ldap.in.buf + ads->ldap.in.ofs, 122 ads->ldap.in.needed); 123 if (ret <= 0) return ret; 124 ads->ldap.in.ofs += ret; 125 ads->ldap.in.needed -= ret; 126 127 if (ads->ldap.in.needed > 0) goto eagain; 128 } 129 130 /* 131 * if we have a complete packet and have not yet unwrapped it 132 * we need to call the mech specific unwrap() hook 133 */ 134 if (ads->ldap.in.needed == 0 && ads->ldap.in.left == 0) { 135 ADS_STATUS status; 136 status = ads->ldap.wrap_ops->unwrap(ads); 137 if (!ADS_ERR_OK(status)) { 138 errno = EACCES; 139 return -1; 140 } 141 } 142 143 /* 144 * if we have unwrapped data give it to the caller 145 */ 146 if (ads->ldap.in.left > 0) { 147 ret = MIN(ads->ldap.in.left, len); 148 memcpy(buf, ads->ldap.in.buf + ads->ldap.in.ofs, ret); 149 ads->ldap.in.ofs += ret; 150 ads->ldap.in.left -= ret; 151 152 /* 153 * if no more is left shrink the inbuf, 154 * this will trigger reading a new SASL packet 155 * from the remote stream in the next call 156 */ 157 if (ads->ldap.in.left == 0) { 158 ads_saslwrap_shrink_inbuf(ads); 159 } 160 161 return ret; 162 } 163 164 /* 165 * if we don't have anything for the caller yet, 166 * tell him to ask again 167 */ 168eagain: 169 errno = EAGAIN; 170 return -1; 171} 172 173static ber_slen_t ads_saslwrap_prepare_outbuf(ADS_STRUCT *ads, uint32 len) 174{ 175 ads->ldap.out.ofs = 0; 176 ads->ldap.out.left = 0; 177 ads->ldap.out.size = 4 + ads->ldap.out.sig_size + len; 178 ads->ldap.out.buf = talloc_array(ads->ldap.mem_ctx, 179 uint8, ads->ldap.out.size); 180 if (!ads->ldap.out.buf) { 181 return -1; 182 } 183 184 return 0; 185} 186 187static void ads_saslwrap_shrink_outbuf(ADS_STRUCT *ads) 188{ 189 talloc_free(ads->ldap.out.buf); 190 191 ads->ldap.out.buf = NULL; 192 ads->ldap.out.size = 0; 193 ads->ldap.out.ofs = 0; 194 ads->ldap.out.left = 0; 195} 196 197static ber_slen_t ads_saslwrap_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 198{ 199 ADS_STRUCT *ads = (ADS_STRUCT *)sbiod->sbiod_pvt; 200 ber_slen_t ret, rlen; 201 202 /* if the buffer is empty, we need to wrap in incoming buffer */ 203 if (ads->ldap.out.left == 0) { 204 ADS_STATUS status; 205 206 if (len == 0) { 207 errno = EINVAL; 208 return -1; 209 } 210 211 rlen = MIN(len, ads->ldap.out.max_unwrapped); 212 213 ret = ads_saslwrap_prepare_outbuf(ads, rlen); 214 if (ret < 0) return ret; 215 216 status = ads->ldap.wrap_ops->wrap(ads, (uint8 *)buf, rlen); 217 if (!ADS_ERR_OK(status)) { 218 errno = EACCES; 219 return -1; 220 } 221 222 RSIVAL(ads->ldap.out.buf, 0, ads->ldap.out.left - 4); 223 } else { 224 rlen = -1; 225 } 226 227 ret = LBER_SBIOD_WRITE_NEXT(sbiod, 228 ads->ldap.out.buf + ads->ldap.out.ofs, 229 ads->ldap.out.left); 230 if (ret <= 0) return ret; 231 ads->ldap.out.ofs += ret; 232 ads->ldap.out.left -= ret; 233 234 if (ads->ldap.out.left == 0) { 235 ads_saslwrap_shrink_outbuf(ads); 236 } 237 238 if (rlen > 0) return rlen; 239 240 errno = EAGAIN; 241 return -1; 242} 243 244static int ads_saslwrap_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) 245{ 246 ADS_STRUCT *ads = (ADS_STRUCT *)sbiod->sbiod_pvt; 247 int ret; 248 249 switch (opt) { 250 case LBER_SB_OPT_DATA_READY: 251 if (ads->ldap.in.left > 0) { 252 return 1; 253 } 254 ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg); 255 break; 256 default: 257 ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg); 258 break; 259 } 260 261 return ret; 262} 263 264static int ads_saslwrap_close(Sockbuf_IO_Desc *sbiod) 265{ 266 return 0; 267} 268 269static const Sockbuf_IO ads_saslwrap_sockbuf_io = { 270 ads_saslwrap_setup, /* sbi_setup */ 271 ads_saslwrap_remove, /* sbi_remove */ 272 ads_saslwrap_ctrl, /* sbi_ctrl */ 273 ads_saslwrap_read, /* sbi_read */ 274 ads_saslwrap_write, /* sbi_write */ 275 ads_saslwrap_close /* sbi_close */ 276}; 277 278ADS_STATUS ads_setup_sasl_wrapping(ADS_STRUCT *ads, 279 const struct ads_saslwrap_ops *ops, 280 void *private_data) 281{ 282 ADS_STATUS status; 283 Sockbuf *sb; 284 Sockbuf_IO *io = discard_const_p(Sockbuf_IO, &ads_saslwrap_sockbuf_io); 285 int rc; 286 287 rc = ldap_get_option(ads->ldap.ld, LDAP_OPT_SOCKBUF, &sb); 288 status = ADS_ERROR_LDAP(rc); 289 if (!ADS_ERR_OK(status)) { 290 return status; 291 } 292 293 /* setup the real wrapping callbacks */ 294 rc = ber_sockbuf_add_io(sb, io, LBER_SBIOD_LEVEL_TRANSPORT, ads); 295 status = ADS_ERROR_LDAP(rc); 296 if (!ADS_ERR_OK(status)) { 297 return status; 298 } 299 300 ads->ldap.wrap_ops = ops; 301 ads->ldap.wrap_private_data = private_data; 302 303 return ADS_SUCCESS; 304} 305#else 306ADS_STATUS ads_setup_sasl_wrapping(ADS_STRUCT *ads, 307 const struct ads_saslwrap_ops *ops, 308 void *private_data) 309{ 310 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); 311} 312#endif /* HAVE_LDAP_SASL_WRAPPING */ 313