• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/heimdal/lib/krb5/
1/*
2 * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "krb5_locl.h"
35#include "send_to_kdc_plugin.h"
36
37struct send_to_kdc {
38    krb5_send_to_kdc_func func;
39    void *data;
40};
41
42/*
43 * send the data in `req' on the socket `fd' (which is datagram iff udp)
44 * waiting `tmout' for a reply and returning the reply in `rep'.
45 * iff limit read up to this many bytes
46 * returns 0 and data in `rep' if succesful, otherwise -1
47 */
48
49static int
50recv_loop (int fd,
51	   time_t tmout,
52	   int udp,
53	   size_t limit,
54	   krb5_data *rep)
55{
56     fd_set fdset;
57     struct timeval timeout;
58     int ret;
59     int nbytes;
60
61     if (fd >= FD_SETSIZE) {
62	 return -1;
63     }
64
65     krb5_data_zero(rep);
66     do {
67	 FD_ZERO(&fdset);
68	 FD_SET(fd, &fdset);
69	 timeout.tv_sec  = tmout;
70	 timeout.tv_usec = 0;
71	 ret = select (fd + 1, &fdset, NULL, NULL, &timeout);
72	 if (ret < 0) {
73	     if (errno == EINTR)
74		 continue;
75	     return -1;
76	 } else if (ret == 0) {
77	     return 0;
78	 } else {
79	     void *tmp;
80
81	     if (ioctl (fd, FIONREAD, &nbytes) < 0) {
82		 krb5_data_free (rep);
83		 return -1;
84	     }
85	     if(nbytes <= 0)
86		 return 0;
87
88	     if (limit)
89		 nbytes = min(nbytes, limit - rep->length);
90
91	     tmp = realloc (rep->data, rep->length + nbytes);
92	     if (tmp == NULL) {
93		 krb5_data_free (rep);
94		 return -1;
95	     }
96	     rep->data = tmp;
97	     ret = recv (fd, (char*)tmp + rep->length, nbytes, 0);
98	     if (ret < 0) {
99		 krb5_data_free (rep);
100		 return -1;
101	     }
102	     rep->length += ret;
103	 }
104     } while(!udp && (limit == 0 || rep->length < limit));
105     return 0;
106}
107
108/*
109 * Send kerberos requests and receive a reply on a udp or any other kind
110 * of a datagram socket.  See `recv_loop'.
111 */
112
113static int
114send_and_recv_udp(int fd,
115		  time_t tmout,
116		  const krb5_data *req,
117		  krb5_data *rep)
118{
119    if (send (fd, req->data, req->length, 0) < 0)
120	return -1;
121
122    return recv_loop(fd, tmout, 1, 0, rep);
123}
124
125/*
126 * `send_and_recv' for a TCP (or any other stream) socket.
127 * Since there are no record limits on a stream socket the protocol here
128 * is to prepend the request with 4 bytes of its length and the reply
129 * is similarly encoded.
130 */
131
132static int
133send_and_recv_tcp(int fd,
134		  time_t tmout,
135		  const krb5_data *req,
136		  krb5_data *rep)
137{
138    unsigned char len[4];
139    unsigned long rep_len;
140    krb5_data len_data;
141
142    _krb5_put_int(len, req->length, 4);
143    if(net_write(fd, len, sizeof(len)) < 0)
144	return -1;
145    if(net_write(fd, req->data, req->length) < 0)
146	return -1;
147    if (recv_loop (fd, tmout, 0, 4, &len_data) < 0)
148	return -1;
149    if (len_data.length != 4) {
150	krb5_data_free (&len_data);
151	return -1;
152    }
153    _krb5_get_int(len_data.data, &rep_len, 4);
154    krb5_data_free (&len_data);
155    if (recv_loop (fd, tmout, 0, rep_len, rep) < 0)
156	return -1;
157    if(rep->length != rep_len) {
158	krb5_data_free (rep);
159	return -1;
160    }
161    return 0;
162}
163
164int
165_krb5_send_and_recv_tcp(int fd,
166			time_t tmout,
167			const krb5_data *req,
168			krb5_data *rep)
169{
170    return send_and_recv_tcp(fd, tmout, req, rep);
171}
172
173/*
174 * `send_and_recv' tailored for the HTTP protocol.
175 */
176
177static int
178send_and_recv_http(int fd,
179		   time_t tmout,
180		   const char *prefix,
181		   const krb5_data *req,
182		   krb5_data *rep)
183{
184    char *request;
185    char *str;
186    int ret;
187    int len = base64_encode(req->data, req->length, &str);
188
189    if(len < 0)
190	return -1;
191    asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str);
192    free(str);
193    if (request == NULL)
194	return -1;
195    ret = net_write (fd, request, strlen(request));
196    free (request);
197    if (ret < 0)
198	return ret;
199    ret = recv_loop(fd, tmout, 0, 0, rep);
200    if(ret)
201	return ret;
202    {
203	unsigned long rep_len;
204	char *s, *p;
205
206	s = realloc(rep->data, rep->length + 1);
207	if (s == NULL) {
208	    krb5_data_free (rep);
209	    return -1;
210	}
211	s[rep->length] = 0;
212	p = strstr(s, "\r\n\r\n");
213	if(p == NULL) {
214	    krb5_data_zero(rep);
215	    free(s);
216	    return -1;
217	}
218	p += 4;
219	rep->data = s;
220	rep->length -= p - s;
221	if(rep->length < 4) { /* remove length */
222	    krb5_data_zero(rep);
223	    free(s);
224	    return -1;
225	}
226	rep->length -= 4;
227	_krb5_get_int(p, &rep_len, 4);
228	if (rep_len != rep->length) {
229	    krb5_data_zero(rep);
230	    free(s);
231	    return -1;
232	}
233	memmove(rep->data, p + 4, rep->length);
234    }
235    return 0;
236}
237
238static int
239init_port(const char *s, int fallback)
240{
241    if (s) {
242	int tmp;
243
244	sscanf (s, "%d", &tmp);
245	return htons(tmp);
246    } else
247	return fallback;
248}
249
250/*
251 * Return 0 if succesful, otherwise 1
252 */
253
254static int
255send_via_proxy (krb5_context context,
256		const krb5_krbhst_info *hi,
257		const krb5_data *send_data,
258		krb5_data *receive)
259{
260    char *proxy2 = strdup(context->http_proxy);
261    char *proxy  = proxy2;
262    char *prefix;
263    char *colon;
264    struct addrinfo hints;
265    struct addrinfo *ai, *a;
266    int ret;
267    int s = -1;
268    char portstr[NI_MAXSERV];
269
270    if (proxy == NULL)
271	return ENOMEM;
272    if (strncmp (proxy, "http://", 7) == 0)
273	proxy += 7;
274
275    colon = strchr(proxy, ':');
276    if(colon != NULL)
277	*colon++ = '\0';
278    memset (&hints, 0, sizeof(hints));
279    hints.ai_family   = PF_UNSPEC;
280    hints.ai_socktype = SOCK_STREAM;
281    snprintf (portstr, sizeof(portstr), "%d",
282	      ntohs(init_port (colon, htons(80))));
283    ret = getaddrinfo (proxy, portstr, &hints, &ai);
284    free (proxy2);
285    if (ret)
286	return krb5_eai_to_heim_errno(ret, errno);
287
288    for (a = ai; a != NULL; a = a->ai_next) {
289	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol | SOCK_CLOEXEC);
290	if (s < 0)
291	    continue;
292	rk_cloexec(s);
293	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
294	    close (s);
295	    continue;
296	}
297	break;
298    }
299    if (a == NULL) {
300	freeaddrinfo (ai);
301	return 1;
302    }
303    freeaddrinfo (ai);
304
305    asprintf(&prefix, "http://%s/", hi->hostname);
306    if(prefix == NULL) {
307	close(s);
308	return 1;
309    }
310    ret = send_and_recv_http(s, context->kdc_timeout,
311			     prefix, send_data, receive);
312    close (s);
313    free(prefix);
314    if(ret == 0 && receive->length != 0)
315	return 0;
316    return 1;
317}
318
319static krb5_error_code
320send_via_plugin(krb5_context context,
321		krb5_krbhst_info *hi,
322		time_t timeout,
323		const krb5_data *send_data,
324		krb5_data *receive)
325{
326    struct krb5_plugin *list = NULL, *e;
327    krb5_error_code ret;
328
329    ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_SEND_TO_KDC, &list);
330    if(ret != 0 || list == NULL)
331	return KRB5_PLUGIN_NO_HANDLE;
332
333    for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
334	krb5plugin_send_to_kdc_ftable *service;
335	void *ctx;
336
337	service = _krb5_plugin_get_symbol(e);
338	if (service->minor_version != 0)
339	    continue;
340
341	(*service->init)(context, &ctx);
342	ret = (*service->send_to_kdc)(context, ctx, hi,
343				      timeout, send_data, receive);
344	(*service->fini)(ctx);
345	if (ret == 0)
346	    break;
347	if (ret != KRB5_PLUGIN_NO_HANDLE) {
348	    krb5_set_error_message(context, ret,
349				   N_("Plugin send_to_kdc failed to "
350				      "lookup with error: %d", ""), ret);
351	    break;
352	}
353    }
354    _krb5_plugin_free(list);
355    return KRB5_PLUGIN_NO_HANDLE;
356}
357
358
359/*
360 * Send the data `send' to one host from `handle` and get back the reply
361 * in `receive'.
362 */
363
364krb5_error_code KRB5_LIB_FUNCTION
365krb5_sendto (krb5_context context,
366	     const krb5_data *send_data,
367	     krb5_krbhst_handle handle,
368	     krb5_data *receive)
369{
370     krb5_error_code ret;
371     int fd;
372     int i;
373
374     krb5_data_zero(receive);
375
376     for (i = 0; i < context->max_retries; ++i) {
377	 krb5_krbhst_info *hi;
378
379	 while (krb5_krbhst_next(context, handle, &hi) == 0) {
380	     struct addrinfo *ai, *a;
381
382	     if (context->send_to_kdc) {
383		 struct send_to_kdc *s = context->send_to_kdc;
384
385		 ret = (*s->func)(context, s->data, hi,
386				  context->kdc_timeout, send_data, receive);
387		 if (ret == 0 && receive->length != 0)
388		     goto out;
389		 continue;
390	     }
391
392	     ret = send_via_plugin(context, hi, context->kdc_timeout,
393				   send_data, receive);
394	     if (ret == 0 && receive->length != 0)
395		 goto out;
396	     else if (ret != KRB5_PLUGIN_NO_HANDLE)
397		 continue;
398
399	     if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
400		 if (send_via_proxy (context, hi, send_data, receive) == 0) {
401		     ret = 0;
402		     goto out;
403		 }
404		 continue;
405	     }
406
407	     ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
408	     if (ret)
409		 continue;
410
411	     for (a = ai; a != NULL; a = a->ai_next) {
412		 fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
413		 if (fd < 0)
414		     continue;
415		 rk_cloexec(fd);
416		 if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
417		     close (fd);
418		     continue;
419		 }
420		 switch (hi->proto) {
421		 case KRB5_KRBHST_HTTP :
422		     ret = send_and_recv_http(fd, context->kdc_timeout,
423					      "", send_data, receive);
424		     break;
425		 case KRB5_KRBHST_TCP :
426		     ret = send_and_recv_tcp (fd, context->kdc_timeout,
427					      send_data, receive);
428		     break;
429		 case KRB5_KRBHST_UDP :
430		     ret = send_and_recv_udp (fd, context->kdc_timeout,
431					      send_data, receive);
432		     break;
433		 }
434		 close (fd);
435		 if(ret == 0 && receive->length != 0)
436		     goto out;
437	     }
438	 }
439	 krb5_krbhst_reset(context, handle);
440     }
441     krb5_clear_error_message (context);
442     ret = KRB5_KDC_UNREACH;
443out:
444     return ret;
445}
446
447krb5_error_code KRB5_LIB_FUNCTION
448krb5_sendto_kdc(krb5_context context,
449		const krb5_data *send_data,
450		const krb5_realm *realm,
451		krb5_data *receive)
452{
453    return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0);
454}
455
456krb5_error_code KRB5_LIB_FUNCTION
457krb5_sendto_kdc_flags(krb5_context context,
458		      const krb5_data *send_data,
459		      const krb5_realm *realm,
460		      krb5_data *receive,
461		      int flags)
462{
463    krb5_error_code ret;
464    krb5_sendto_ctx ctx;
465
466    ret = krb5_sendto_ctx_alloc(context, &ctx);
467    if (ret)
468	return ret;
469    krb5_sendto_ctx_add_flags(ctx, flags);
470    krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL);
471
472    ret = krb5_sendto_context(context, ctx, send_data, *realm, receive);
473    krb5_sendto_ctx_free(context, ctx);
474    return ret;
475}
476
477krb5_error_code KRB5_LIB_FUNCTION
478krb5_set_send_to_kdc_func(krb5_context context,
479			  krb5_send_to_kdc_func func,
480			  void *data)
481{
482    free(context->send_to_kdc);
483    if (func == NULL) {
484	context->send_to_kdc = NULL;
485	return 0;
486    }
487
488    context->send_to_kdc = malloc(sizeof(*context->send_to_kdc));
489    if (context->send_to_kdc == NULL) {
490	krb5_set_error_message(context, ENOMEM,
491			       N_("malloc: out of memory", ""));
492	return ENOMEM;
493    }
494
495    context->send_to_kdc->func = func;
496    context->send_to_kdc->data = data;
497    return 0;
498}
499
500krb5_error_code KRB5_LIB_FUNCTION
501_krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to)
502{
503    if (context->send_to_kdc)
504	return krb5_set_send_to_kdc_func(to,
505					 context->send_to_kdc->func,
506					 context->send_to_kdc->data);
507    else
508	return krb5_set_send_to_kdc_func(to, NULL, NULL);
509}
510
511
512
513struct krb5_sendto_ctx_data {
514    int flags;
515    int type;
516    krb5_sendto_ctx_func func;
517    void *data;
518};
519
520krb5_error_code KRB5_LIB_FUNCTION
521krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
522{
523    *ctx = calloc(1, sizeof(**ctx));
524    if (*ctx == NULL) {
525	krb5_set_error_message(context, ENOMEM,
526			       N_("malloc: out of memory", ""));
527	return ENOMEM;
528    }
529    return 0;
530}
531
532void KRB5_LIB_FUNCTION
533krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
534{
535    ctx->flags |= flags;
536}
537
538int KRB5_LIB_FUNCTION
539krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
540{
541    return ctx->flags;
542}
543
544void KRB5_LIB_FUNCTION
545krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type)
546{
547    ctx->type = type;
548}
549
550
551void KRB5_LIB_FUNCTION
552krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
553			 krb5_sendto_ctx_func func,
554			 void *data)
555{
556    ctx->func = func;
557    ctx->data = data;
558}
559
560void KRB5_LIB_FUNCTION
561krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
562{
563    memset(ctx, 0, sizeof(*ctx));
564    free(ctx);
565}
566
567krb5_error_code KRB5_LIB_FUNCTION
568krb5_sendto_context(krb5_context context,
569		    krb5_sendto_ctx ctx,
570		    const krb5_data *send_data,
571		    const krb5_realm realm,
572		    krb5_data *receive)
573{
574    krb5_error_code ret;
575    krb5_krbhst_handle handle = NULL;
576    int type, freectx = 0;
577    int action;
578
579    krb5_data_zero(receive);
580
581    if (ctx == NULL) {
582	freectx = 1;
583	ret = krb5_sendto_ctx_alloc(context, &ctx);
584	if (ret)
585	    return ret;
586    }
587
588    type = ctx->type;
589    if (type == 0) {
590	if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
591	    type = KRB5_KRBHST_ADMIN;
592	else
593	    type = KRB5_KRBHST_KDC;
594    }
595
596    if (send_data->length > context->large_msg_size)
597	ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
598
599    /* loop until we get back a appropriate response */
600
601    do {
602	action = KRB5_SENDTO_DONE;
603
604	krb5_data_free(receive);
605
606	if (handle == NULL) {
607	    ret = krb5_krbhst_init_flags(context, realm, type,
608					 ctx->flags, &handle);
609	    if (ret) {
610		if (freectx)
611		    krb5_sendto_ctx_free(context, ctx);
612		return ret;
613	    }
614	}
615
616	ret = krb5_sendto(context, send_data, handle, receive);
617	if (ret)
618	    break;
619	if (ctx->func) {
620	    ret = (*ctx->func)(context, ctx, ctx->data, receive, &action);
621	    if (ret)
622		break;
623	}
624	if (action != KRB5_SENDTO_CONTINUE) {
625	    krb5_krbhst_free(context, handle);
626	    handle = NULL;
627	}
628    } while (action != KRB5_SENDTO_DONE);
629    if (handle)
630	krb5_krbhst_free(context, handle);
631    if (ret == KRB5_KDC_UNREACH)
632	krb5_set_error_message(context, ret,
633			       N_("unable to reach any KDC in realm %s", ""),
634			       realm);
635    if (ret)
636	krb5_data_free(receive);
637    if (freectx)
638	krb5_sendto_ctx_free(context, ctx);
639    return ret;
640}
641
642krb5_error_code
643_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
644		const krb5_data *reply, int *action)
645{
646    krb5_error_code ret;
647    KRB_ERROR error;
648
649    if(krb5_rd_error(context, reply, &error))
650	return 0;
651
652    ret = krb5_error_from_rd_error(context, &error, NULL);
653    krb5_free_error_contents(context, &error);
654
655    switch(ret) {
656    case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
657	if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
658	    break;
659	krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
660	*action = KRB5_SENDTO_RESTART;
661	break;
662    }
663    case KRB5KDC_ERR_SVC_UNAVAILABLE:
664	*action = KRB5_SENDTO_CONTINUE;
665	break;
666    }
667    return 0;
668}
669