• 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/source4/auth/kerberos/
1/*
2   Unix SMB/CIFS implementation.
3   Wrapper for krb5_init_context
4
5   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6   Copyright (C) Andrew Tridgell 2005
7   Copyright (C) Stefan Metzmacher 2004
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "includes.h"
24#include "system/kerberos.h"
25#include <tevent.h>
26#include "auth/kerberos/kerberos.h"
27#include "lib/socket/socket.h"
28#include "lib/stream/packet.h"
29#include "system/network.h"
30#include "param/param.h"
31#include "libcli/resolve/resolve.h"
32
33/*
34  context structure for operations on cldap packets
35*/
36struct smb_krb5_socket {
37	struct socket_context *sock;
38
39	/* the fd event */
40	struct tevent_fd *fde;
41
42	NTSTATUS status;
43	DATA_BLOB request, reply;
44
45	struct packet_context *packet;
46
47	size_t partial_read;
48
49	krb5_krbhst_info *hi;
50};
51
52static krb5_error_code smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
53{
54	krb5_free_context(ctx->krb5_context);
55	return 0;
56}
57
58static krb5_error_code smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
59{
60	/* Otherwise krb5_free_context will try and close what we have already free()ed */
61	krb5_set_warn_dest(ctx->krb5_context, NULL);
62	krb5_closelog(ctx->krb5_context, ctx->logf);
63	smb_krb5_context_destroy_1(ctx);
64	return 0;
65}
66
67/* We never close down the DEBUG system, and no need to unreference the use */
68static void smb_krb5_debug_close(void *private_data) {
69	return;
70}
71
72static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private_data)
73{
74	DEBUG(2, ("Kerberos: %s\n", msg));
75}
76
77/*
78  handle recv events on a smb_krb5 socket
79*/
80static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
81{
82	TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
83	DATA_BLOB blob;
84	size_t nread, dsize;
85
86	smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
87	if (!NT_STATUS_IS_OK(smb_krb5->status)) {
88		talloc_free(tmp_ctx);
89		return;
90	}
91
92	blob = data_blob_talloc(tmp_ctx, NULL, dsize);
93	if (blob.data == NULL && dsize != 0) {
94		smb_krb5->status = NT_STATUS_NO_MEMORY;
95		talloc_free(tmp_ctx);
96		return;
97	}
98
99	smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
100	if (!NT_STATUS_IS_OK(smb_krb5->status)) {
101		talloc_free(tmp_ctx);
102		return;
103	}
104	blob.length = nread;
105
106	if (nread == 0) {
107		smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
108		talloc_free(tmp_ctx);
109		return;
110	}
111
112	DEBUG(2,("Received smb_krb5 packet of length %d\n",
113		 (int)blob.length));
114
115	talloc_steal(smb_krb5, blob.data);
116	smb_krb5->reply = blob;
117	talloc_free(tmp_ctx);
118}
119
120static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
121{
122	struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
123	talloc_steal(smb_krb5, data.data);
124	smb_krb5->reply = data;
125	smb_krb5->reply.length -= 4;
126	smb_krb5->reply.data += 4;
127	return NT_STATUS_OK;
128}
129
130/*
131  handle request timeouts
132*/
133static void smb_krb5_request_timeout(struct tevent_context *event_ctx,
134				  struct tevent_timer *te, struct timeval t,
135				  void *private_data)
136{
137	struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
138	DEBUG(5,("Timed out smb_krb5 packet\n"));
139	smb_krb5->status = NT_STATUS_IO_TIMEOUT;
140}
141
142static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
143{
144	struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
145	smb_krb5->status = status;
146}
147
148/*
149  handle send events on a smb_krb5 socket
150*/
151static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
152{
153	NTSTATUS status;
154
155	size_t len;
156
157	len = smb_krb5->request.length;
158	status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
159
160	if (!NT_STATUS_IS_OK(status)) return;
161
162	TEVENT_FD_READABLE(smb_krb5->fde);
163
164	TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
165	return;
166}
167
168
169/*
170  handle fd events on a smb_krb5_socket
171*/
172static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
173				 uint16_t flags, void *private_data)
174{
175	struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
176	switch (smb_krb5->hi->proto) {
177	case KRB5_KRBHST_UDP:
178		if (flags & TEVENT_FD_READ) {
179			smb_krb5_socket_recv(smb_krb5);
180			return;
181		}
182		if (flags & TEVENT_FD_WRITE) {
183			smb_krb5_socket_send(smb_krb5);
184			return;
185		}
186		/* not reached */
187		return;
188	case KRB5_KRBHST_TCP:
189		if (flags & TEVENT_FD_READ) {
190			packet_recv(smb_krb5->packet);
191			return;
192		}
193		if (flags & TEVENT_FD_WRITE) {
194			packet_queue_run(smb_krb5->packet);
195			return;
196		}
197		/* not reached */
198		return;
199	case KRB5_KRBHST_HTTP:
200		/* can't happen */
201		break;
202	}
203}
204
205
206krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
207					    void *data,
208					    krb5_krbhst_info *hi,
209					    time_t timeout,
210					    const krb5_data *send_buf,
211					    krb5_data *recv_buf)
212{
213	krb5_error_code ret;
214	NTSTATUS status;
215	struct socket_address *remote_addr;
216	const char *name;
217	struct addrinfo *ai, *a;
218	struct smb_krb5_socket *smb_krb5;
219
220	struct tevent_context *ev = talloc_get_type(data, struct tevent_context);
221
222	DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
223
224	ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
225	if (ret) {
226		return ret;
227	}
228
229	for (a = ai; a; a = ai->ai_next) {
230		smb_krb5 = talloc(NULL, struct smb_krb5_socket);
231		if (!smb_krb5) {
232			return ENOMEM;
233		}
234		smb_krb5->hi = hi;
235
236		switch (a->ai_family) {
237		case PF_INET:
238			name = "ipv4";
239			break;
240#ifdef HAVE_IPV6
241		case PF_INET6:
242			name = "ipv6";
243			break;
244#endif
245		default:
246			talloc_free(smb_krb5);
247			return EINVAL;
248		}
249
250		status = NT_STATUS_INVALID_PARAMETER;
251		switch (hi->proto) {
252		case KRB5_KRBHST_UDP:
253			status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
254			break;
255		case KRB5_KRBHST_TCP:
256			status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
257			break;
258		case KRB5_KRBHST_HTTP:
259			talloc_free(smb_krb5);
260			return EINVAL;
261		}
262		if (!NT_STATUS_IS_OK(status)) {
263			talloc_free(smb_krb5);
264			continue;
265		}
266
267		talloc_steal(smb_krb5, smb_krb5->sock);
268
269		remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
270		if (!remote_addr) {
271			talloc_free(smb_krb5);
272			continue;
273		}
274
275		status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
276		if (!NT_STATUS_IS_OK(status)) {
277			talloc_free(smb_krb5);
278			continue;
279		}
280		talloc_free(remote_addr);
281
282		/* Setup the FDE, start listening for read events
283		 * from the start (otherwise we may miss a socket
284		 * drop) and mark as AUTOCLOSE along with the fde */
285
286		/* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
287		smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
288					      socket_get_fd(smb_krb5->sock),
289					      TEVENT_FD_READ,
290					      smb_krb5_socket_handler, smb_krb5);
291		/* its now the job of the event layer to close the socket */
292		tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
293		socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
294
295		tevent_add_timer(ev, smb_krb5,
296				 timeval_current_ofs(timeout, 0),
297				 smb_krb5_request_timeout, smb_krb5);
298
299		smb_krb5->status = NT_STATUS_OK;
300		smb_krb5->reply = data_blob(NULL, 0);
301
302		switch (hi->proto) {
303		case KRB5_KRBHST_UDP:
304			TEVENT_FD_WRITEABLE(smb_krb5->fde);
305			smb_krb5->request = send_blob;
306			break;
307		case KRB5_KRBHST_TCP:
308
309			smb_krb5->packet = packet_init(smb_krb5);
310			if (smb_krb5->packet == NULL) {
311				talloc_free(smb_krb5);
312				return ENOMEM;
313			}
314			packet_set_private(smb_krb5->packet, smb_krb5);
315			packet_set_socket(smb_krb5->packet, smb_krb5->sock);
316			packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
317			packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
318			packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
319			packet_set_event_context(smb_krb5->packet, ev);
320			packet_set_fde(smb_krb5->packet, smb_krb5->fde);
321
322			smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
323			RSIVAL(smb_krb5->request.data, 0, send_blob.length);
324			memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
325			packet_send(smb_krb5->packet, smb_krb5->request);
326			break;
327		case KRB5_KRBHST_HTTP:
328			talloc_free(smb_krb5);
329			return EINVAL;
330		}
331		while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
332			if (tevent_loop_once(ev) != 0) {
333				talloc_free(smb_krb5);
334				return EINVAL;
335			}
336		}
337		if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
338			talloc_free(smb_krb5);
339			continue;
340		}
341
342		if (!NT_STATUS_IS_OK(smb_krb5->status)) {
343			DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
344			talloc_free(smb_krb5);
345			continue;
346		}
347
348		ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
349		if (ret) {
350			talloc_free(smb_krb5);
351			return ret;
352		}
353		talloc_free(smb_krb5);
354
355		break;
356	}
357	if (a) {
358		return 0;
359	}
360	return KRB5_KDC_UNREACH;
361}
362
363krb5_error_code smb_krb5_init_context(void *parent_ctx,
364				      struct tevent_context *ev,
365				      struct loadparm_context *lp_ctx,
366				       struct smb_krb5_context **smb_krb5_context)
367{
368	krb5_error_code ret;
369	TALLOC_CTX *tmp_ctx;
370	char **config_files;
371	const char *config_file;
372
373	initialize_krb5_error_table();
374
375	tmp_ctx = talloc_new(parent_ctx);
376	*smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
377
378	if (!*smb_krb5_context || !tmp_ctx) {
379		talloc_free(tmp_ctx);
380		return ENOMEM;
381	}
382
383	ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
384	if (ret) {
385		DEBUG(1,("krb5_init_context failed (%s)\n",
386			 error_message(ret)));
387		talloc_free(tmp_ctx);
388		return ret;
389	}
390
391	talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
392
393	config_file = config_path(tmp_ctx, lp_ctx, "krb5.conf");
394	if (!config_file) {
395		talloc_free(tmp_ctx);
396		return ENOMEM;
397	}
398
399	/* Use our local krb5.conf file by default */
400	ret = krb5_prepend_config_files_default(config_file == NULL?"":config_file, &config_files);
401	if (ret) {
402		DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
403			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
404		talloc_free(tmp_ctx);
405		return ret;
406	}
407
408	ret = krb5_set_config_files((*smb_krb5_context)->krb5_context,
409				    config_files);
410	krb5_free_config_files(config_files);
411	if (ret) {
412		DEBUG(1,("krb5_set_config_files failed (%s)\n",
413			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
414		talloc_free(tmp_ctx);
415		return ret;
416	}
417
418	if (lp_realm(lp_ctx) && *lp_realm(lp_ctx)) {
419		char *upper_realm = strupper_talloc(tmp_ctx, lp_realm(lp_ctx));
420		if (!upper_realm) {
421			DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm(lp_ctx)));
422			talloc_free(tmp_ctx);
423			return ENOMEM;
424		}
425		ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
426		if (ret) {
427			DEBUG(1,("krb5_set_default_realm failed (%s)\n",
428				 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
429			talloc_free(tmp_ctx);
430			return ret;
431		}
432	}
433
434	/* TODO: Should we have a different name here? */
435	ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
436
437	if (ret) {
438		DEBUG(1,("krb5_initlog failed (%s)\n",
439			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
440		talloc_free(tmp_ctx);
441		return ret;
442	}
443
444	talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
445
446	ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
447			       smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
448	if (ret) {
449		DEBUG(1,("krb5_addlog_func failed (%s)\n",
450			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
451		talloc_free(tmp_ctx);
452		return ret;
453	}
454	krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
455
456	/* Set use of our socket lib */
457	ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
458					smb_krb5_send_and_recv_func,
459					ev);
460	if (ret) {
461		DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
462			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
463		talloc_free(tmp_ctx);
464		return ret;
465	}
466
467	talloc_steal(parent_ctx, *smb_krb5_context);
468	talloc_free(tmp_ctx);
469
470	/* Set options in kerberos */
471
472	krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context,
473					   lp_parm_bool(lp_ctx, NULL, "krb5", "set_dns_canonicalize", false));
474
475	return 0;
476}
477
478