winbindd: add "winbind scan trusted domains = no" to avoid trust enumeration
[bbaumbach/samba-autobuild/.git] / source3 / libads / sasl_wrapping.c
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 #include "ads.h"
22
23 void ndr_print_ads_saslwrap_struct(struct ndr_print *ndr, const char *name, const struct ads_saslwrap *r)
24 {
25         ndr_print_struct(ndr, name, "saslwrap");
26         ndr->depth++;
27         ndr_print_uint16(ndr, "wrap_type", r->wrap_type);
28 #ifdef HAVE_LDAP_SASL_WRAPPING
29         ndr_print_ptr(ndr, "sbiod", r->sbiod);
30 #endif /* HAVE_LDAP_SASL_WRAPPING */
31         ndr_print_ptr(ndr, "mem_ctx", r->mem_ctx);
32         ndr_print_ptr(ndr, "wrap_ops", r->wrap_ops);
33         ndr_print_ptr(ndr, "wrap_private_data", r->wrap_private_data);
34         ndr_print_struct(ndr, name, "in");
35         ndr->depth++;
36         ndr_print_uint32(ndr, "ofs", r->in.ofs);
37         ndr_print_uint32(ndr, "needed", r->in.needed);
38         ndr_print_uint32(ndr, "left", r->in.left);
39         ndr_print_uint32(ndr, "max_wrapped", r->in.max_wrapped);
40         ndr_print_uint32(ndr, "min_wrapped", r->in.min_wrapped);
41         ndr_print_uint32(ndr, "size", r->in.size);
42         ndr_print_array_uint8(ndr, "buf", r->in.buf, r->in.size);
43         ndr->depth--;
44         ndr_print_struct(ndr, name, "out");
45         ndr->depth++;
46         ndr_print_uint32(ndr, "ofs", r->out.ofs);
47         ndr_print_uint32(ndr, "left", r->out.left);
48         ndr_print_uint32(ndr, "max_unwrapped", r->out.max_unwrapped);
49         ndr_print_uint32(ndr, "sig_size", r->out.sig_size);
50         ndr_print_uint32(ndr, "size", r->out.size);
51         ndr_print_array_uint8(ndr, "buf", r->out.buf, r->out.size);
52         ndr->depth--;
53 }
54
55 #ifdef HAVE_LDAP_SASL_WRAPPING
56
57 static int ads_saslwrap_setup(Sockbuf_IO_Desc *sbiod, void *arg)
58 {
59         struct ads_saslwrap *wrap = (struct ads_saslwrap *)arg;
60
61         wrap->sbiod     = sbiod;
62
63         sbiod->sbiod_pvt = wrap;
64
65         return 0;
66 }
67
68 static int ads_saslwrap_remove(Sockbuf_IO_Desc *sbiod)
69 {
70         return 0;
71 }
72
73 static ber_slen_t ads_saslwrap_prepare_inbuf(struct ads_saslwrap *wrap)
74 {
75         wrap->in.ofs    = 0;
76         wrap->in.needed = 0;
77         wrap->in.left   = 0;
78         wrap->in.size   = 4 + wrap->in.min_wrapped;
79         wrap->in.buf    = talloc_array(wrap->mem_ctx,
80                                        uint8_t, wrap->in.size);
81         if (!wrap->in.buf) {
82                 return -1;
83         }
84
85         return 0;
86 }
87
88 static ber_slen_t ads_saslwrap_grow_inbuf(struct ads_saslwrap *wrap)
89 {
90         if (wrap->in.size == (4 + wrap->in.needed)) {
91                 return 0;
92         }
93
94         wrap->in.size   = 4 + wrap->in.needed;
95         wrap->in.buf    = talloc_realloc(wrap->mem_ctx,
96                                          wrap->in.buf,
97                                          uint8_t, wrap->in.size);
98         if (!wrap->in.buf) {
99                 return -1;
100         }
101
102         return 0;
103 }
104
105 static void ads_saslwrap_shrink_inbuf(struct ads_saslwrap *wrap)
106 {
107         talloc_free(wrap->in.buf);
108
109         wrap->in.buf    = NULL;
110         wrap->in.size   = 0;
111         wrap->in.ofs    = 0;
112         wrap->in.needed = 0;
113         wrap->in.left   = 0;
114 }
115
116 static ber_slen_t ads_saslwrap_read(Sockbuf_IO_Desc *sbiod,
117                                     void *buf, ber_len_t len)
118 {
119         struct ads_saslwrap *wrap =
120                         (struct ads_saslwrap *)sbiod->sbiod_pvt;
121         ber_slen_t ret;
122
123         /* If ofs < 4 it means we don't have read the length header yet */
124         if (wrap->in.ofs < 4) {
125                 ret = ads_saslwrap_prepare_inbuf(wrap);
126                 if (ret < 0) return ret;
127
128                 ret = LBER_SBIOD_READ_NEXT(sbiod,
129                                            wrap->in.buf + wrap->in.ofs,
130                                            4 - wrap->in.ofs);
131                 if (ret <= 0) return ret;
132                 wrap->in.ofs += ret;
133
134                 if (wrap->in.ofs < 4) goto eagain;
135
136                 wrap->in.needed = RIVAL(wrap->in.buf, 0);
137                 if (wrap->in.needed > wrap->in.max_wrapped) {
138                         errno = EINVAL;
139                         return -1;
140                 }
141                 if (wrap->in.needed < wrap->in.min_wrapped) {
142                         errno = EINVAL;
143                         return -1;
144                 }
145
146                 ret = ads_saslwrap_grow_inbuf(wrap);
147                 if (ret < 0) return ret;
148         }
149
150         /*
151          * if there's more data needed from the remote end,
152          * we need to read more
153          */
154         if (wrap->in.needed > 0) {
155                 ret = LBER_SBIOD_READ_NEXT(sbiod,
156                                            wrap->in.buf + wrap->in.ofs,
157                                            wrap->in.needed);
158                 if (ret <= 0) return ret;
159                 wrap->in.ofs += ret;
160                 wrap->in.needed -= ret;
161
162                 if (wrap->in.needed > 0) goto eagain;
163         }
164
165         /*
166          * if we have a complete packet and have not yet unwrapped it
167          * we need to call the mech specific unwrap() hook
168          */
169         if (wrap->in.needed == 0 && wrap->in.left == 0) {
170                 ADS_STATUS status;
171                 status = wrap->wrap_ops->unwrap(wrap);
172                 if (!ADS_ERR_OK(status)) {
173                         errno = EACCES;
174                         return -1;
175                 }
176         }
177
178         /*
179          * if we have unwrapped data give it to the caller
180          */
181         if (wrap->in.left > 0) {
182                 ret = MIN(wrap->in.left, len);
183                 memcpy(buf, wrap->in.buf + wrap->in.ofs, ret);
184                 wrap->in.ofs += ret;
185                 wrap->in.left -= ret;
186
187                 /*
188                  * if no more is left shrink the inbuf,
189                  * this will trigger reading a new SASL packet
190                  * from the remote stream in the next call
191                  */
192                 if (wrap->in.left == 0) {
193                         ads_saslwrap_shrink_inbuf(wrap);
194                 }
195
196                 return ret;
197         }
198
199         /*
200          * if we don't have anything for the caller yet,
201          * tell him to ask again
202          */
203 eagain:
204         errno = EAGAIN;
205         return -1;
206 }
207
208 static ber_slen_t ads_saslwrap_prepare_outbuf(struct ads_saslwrap *wrap,
209                                               uint32_t len)
210 {
211         wrap->out.ofs   = 0;
212         wrap->out.left  = 0;
213         wrap->out.size  = 4 + wrap->out.sig_size + len;
214         wrap->out.buf   = talloc_array(wrap->mem_ctx,
215                                                uint8_t, wrap->out.size);
216         if (!wrap->out.buf) {
217                 return -1;
218         }
219
220         return 0;
221 }
222
223 static void ads_saslwrap_shrink_outbuf(struct ads_saslwrap *wrap)
224 {
225         talloc_free(wrap->out.buf);
226
227         wrap->out.buf   = NULL;
228         wrap->out.size  = 0;
229         wrap->out.ofs   = 0;
230         wrap->out.left  = 0;
231 }
232
233 static ber_slen_t ads_saslwrap_write(Sockbuf_IO_Desc *sbiod,
234                                      void *buf, ber_len_t len)
235 {
236         struct ads_saslwrap *wrap =
237                         (struct ads_saslwrap *)sbiod->sbiod_pvt;
238         ber_slen_t ret, rlen;
239
240         /* if the buffer is empty, we need to wrap in incoming buffer */
241         if (wrap->out.left == 0) {
242                 ADS_STATUS status;
243
244                 if (len == 0) {
245                         errno = EINVAL;
246                         return -1;
247                 }
248
249                 rlen = MIN(len, wrap->out.max_unwrapped);
250
251                 ret = ads_saslwrap_prepare_outbuf(wrap, rlen);
252                 if (ret < 0) return ret;
253                 
254                 status = wrap->wrap_ops->wrap(wrap, (uint8_t *)buf, rlen);
255                 if (!ADS_ERR_OK(status)) {
256                         errno = EACCES;
257                         return -1;
258                 }
259
260                 RSIVAL(wrap->out.buf, 0, wrap->out.left - 4);
261         } else {
262                 rlen = -1;
263         }
264
265         ret = LBER_SBIOD_WRITE_NEXT(sbiod,
266                                     wrap->out.buf + wrap->out.ofs,
267                                     wrap->out.left);
268         if (ret <= 0) return ret;
269         wrap->out.ofs += ret;
270         wrap->out.left -= ret;
271
272         if (wrap->out.left == 0) {
273                 ads_saslwrap_shrink_outbuf(wrap);
274         }
275
276         if (rlen > 0) return rlen;
277
278         errno = EAGAIN;
279         return -1;
280 }
281
282 static int ads_saslwrap_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
283 {
284         struct ads_saslwrap *wrap =
285                         (struct ads_saslwrap *)sbiod->sbiod_pvt;
286         int ret;
287
288         switch (opt) {
289         case LBER_SB_OPT_DATA_READY:
290                 if (wrap->in.left > 0) {
291                         return 1;
292                 }
293                 ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);
294                 break;
295         default:
296                 ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);
297                 break;
298         }
299
300         return ret;
301 }
302
303 static int ads_saslwrap_close(Sockbuf_IO_Desc *sbiod)
304 {
305         return 0;
306 }
307
308 static const Sockbuf_IO ads_saslwrap_sockbuf_io = {
309         ads_saslwrap_setup,     /* sbi_setup */
310         ads_saslwrap_remove,    /* sbi_remove */
311         ads_saslwrap_ctrl,      /* sbi_ctrl */
312         ads_saslwrap_read,      /* sbi_read */
313         ads_saslwrap_write,     /* sbi_write */
314         ads_saslwrap_close      /* sbi_close */
315 };
316
317 ADS_STATUS ads_setup_sasl_wrapping(struct ads_saslwrap *wrap, LDAP *ld,
318                                    const struct ads_saslwrap_ops *ops,
319                                    void *private_data)
320 {
321         ADS_STATUS status;
322         Sockbuf *sb;
323         Sockbuf_IO *io = discard_const_p(Sockbuf_IO, &ads_saslwrap_sockbuf_io);
324         int rc;
325
326         rc = ldap_get_option(ld, LDAP_OPT_SOCKBUF, &sb);
327         status = ADS_ERROR_LDAP(rc);
328         if (!ADS_ERR_OK(status)) {
329                 return status;
330         }
331
332         /* setup the real wrapping callbacks */
333         rc = ber_sockbuf_add_io(sb, io, LBER_SBIOD_LEVEL_TRANSPORT, wrap);
334         status = ADS_ERROR_LDAP(rc);
335         if (!ADS_ERR_OK(status)) {
336                 return status;
337         }
338
339         wrap->wrap_ops          = ops;
340         wrap->wrap_private_data = private_data;
341
342         return ADS_SUCCESS;
343 }
344 #else
345 ADS_STATUS ads_setup_sasl_wrapping(struct ads_saslwrap *wrap, LDAP *ld,
346                                    const struct ads_saslwrap_ops *ops,
347                                    void *private_data)
348 {
349         return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
350 }
351 #endif /* HAVE_LDAP_SASL_WRAPPING */