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