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