• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source3/libads/
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