s3-secdesc: remove "typedef struct security_descriptor SEC_DESC".
[kai/samba.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
22 #ifdef HAVE_LDAP_SASL_WRAPPING
23
24 static 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
35 static int ads_saslwrap_remove(Sockbuf_IO_Desc *sbiod)
36 {
37         return 0;
38 }
39
40 static 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
55 static 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
72 static 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
83 static 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          */
168 eagain:
169         errno = EAGAIN;
170         return -1;
171 }
172
173 static 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
187 static 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
197 static 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
244 static 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
264 static int ads_saslwrap_close(Sockbuf_IO_Desc *sbiod)
265 {
266         return 0;
267 }
268
269 static 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
278 ADS_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
306 ADS_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 */