1/*	$NetBSD: kpasswdd.c,v 1.1.1.1 2011/04/13 18:14:38 elric Exp $	*/
2
3/*
4 * Copyright (c) 1997-2005 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "kpasswd_locl.h"
37__RCSID("$NetBSD: kpasswdd.c,v 1.1.1.1 2011/04/13 18:14:38 elric Exp $");
38
39#include <kadm5/admin.h>
40#ifdef HAVE_SYS_UN_H
41#include <sys/un.h>
42#endif
43#include <krb5/hdb.h>
44#include <kadm5/private.h>
45
46static krb5_context context;
47static krb5_log_facility *log_facility;
48
49static struct getarg_strings addresses_str;
50krb5_addresses explicit_addresses;
51
52static sig_atomic_t exit_flag = 0;
53
54static void
55add_one_address (const char *str, int first)
56{
57    krb5_error_code ret;
58    krb5_addresses tmp;
59
60    ret = krb5_parse_address (context, str, &tmp);
61    if (ret)
62	krb5_err (context, 1, ret, "parse_address `%s'", str);
63    if (first)
64	krb5_copy_addresses(context, &tmp, &explicit_addresses);
65    else
66	krb5_append_addresses(context, &explicit_addresses, &tmp);
67    krb5_free_addresses (context, &tmp);
68}
69
70static void
71send_reply (int s,
72	    struct sockaddr *sa,
73	    int sa_size,
74	    krb5_data *ap_rep,
75	    krb5_data *rest)
76{
77    struct msghdr msghdr;
78    struct iovec iov[3];
79    uint16_t len, ap_rep_len;
80    u_char header[6];
81    u_char *p;
82
83    if (ap_rep)
84	ap_rep_len = ap_rep->length;
85    else
86	ap_rep_len = 0;
87
88    len = 6 + ap_rep_len + rest->length;
89    p = header;
90    *p++ = (len >> 8) & 0xFF;
91    *p++ = (len >> 0) & 0xFF;
92    *p++ = 0;
93    *p++ = 1;
94    *p++ = (ap_rep_len >> 8) & 0xFF;
95    *p++ = (ap_rep_len >> 0) & 0xFF;
96
97    memset (&msghdr, 0, sizeof(msghdr));
98    msghdr.msg_name       = (void *)sa;
99    msghdr.msg_namelen    = sa_size;
100    msghdr.msg_iov        = iov;
101    msghdr.msg_iovlen     = sizeof(iov)/sizeof(*iov);
102#if 0
103    msghdr.msg_control    = NULL;
104    msghdr.msg_controllen = 0;
105#endif
106
107    iov[0].iov_base       = (char *)header;
108    iov[0].iov_len        = 6;
109    if (ap_rep_len) {
110	iov[1].iov_base   = ap_rep->data;
111	iov[1].iov_len    = ap_rep->length;
112    } else {
113	iov[1].iov_base   = NULL;
114	iov[1].iov_len    = 0;
115    }
116    iov[2].iov_base       = rest->data;
117    iov[2].iov_len        = rest->length;
118
119    if (sendmsg (s, &msghdr, 0) < 0)
120	krb5_warn (context, errno, "sendmsg");
121}
122
123static int
124make_result (krb5_data *data,
125	     uint16_t result_code,
126	     const char *expl)
127{
128    char *str;
129    krb5_data_zero (data);
130
131    data->length = asprintf (&str,
132			     "%c%c%s",
133			     (result_code >> 8) & 0xFF,
134			     result_code & 0xFF,
135			     expl);
136
137    if (str == NULL) {
138	krb5_warnx (context, "Out of memory generating error reply");
139	return 1;
140    }
141    data->data = str;
142    return 0;
143}
144
145static void
146reply_error (krb5_realm realm,
147	     int s,
148	     struct sockaddr *sa,
149	     int sa_size,
150	     krb5_error_code error_code,
151	     uint16_t result_code,
152	     const char *expl)
153{
154    krb5_error_code ret;
155    krb5_data error_data;
156    krb5_data e_data;
157    krb5_principal server = NULL;
158
159    if (make_result(&e_data, result_code, expl))
160	return;
161
162    if (realm) {
163	ret = krb5_make_principal (context, &server, realm,
164				   "kadmin", "changepw", NULL);
165	if (ret) {
166	    krb5_data_free (&e_data);
167	    return;
168	}
169    }
170
171    ret = krb5_mk_error (context,
172			 error_code,
173			 NULL,
174			 &e_data,
175			 NULL,
176			 server,
177			 NULL,
178			 NULL,
179			 &error_data);
180    if (server)
181	krb5_free_principal(context, server);
182    krb5_data_free (&e_data);
183    if (ret) {
184	krb5_warn (context, ret, "Could not even generate error reply");
185	return;
186    }
187    send_reply (s, sa, sa_size, NULL, &error_data);
188    krb5_data_free (&error_data);
189}
190
191static void
192reply_priv (krb5_auth_context auth_context,
193	    int s,
194	    struct sockaddr *sa,
195	    int sa_size,
196	    uint16_t result_code,
197	    const char *expl)
198{
199    krb5_error_code ret;
200    krb5_data krb_priv_data;
201    krb5_data ap_rep_data;
202    krb5_data e_data;
203
204    ret = krb5_mk_rep (context,
205		       auth_context,
206		       &ap_rep_data);
207    if (ret) {
208	krb5_warn (context, ret, "Could not even generate error reply");
209	return;
210    }
211
212    if (make_result(&e_data, result_code, expl))
213	return;
214
215    ret = krb5_mk_priv (context,
216			auth_context,
217			&e_data,
218			&krb_priv_data,
219			NULL);
220    krb5_data_free (&e_data);
221    if (ret) {
222	krb5_warn (context, ret, "Could not even generate error reply");
223	return;
224    }
225    send_reply (s, sa, sa_size, &ap_rep_data, &krb_priv_data);
226    krb5_data_free (&ap_rep_data);
227    krb5_data_free (&krb_priv_data);
228}
229
230/*
231 * Change the password for `principal', sending the reply back on `s'
232 * (`sa', `sa_size') to `pwd_data'.
233 */
234
235static void
236change (krb5_auth_context auth_context,
237	krb5_principal admin_principal,
238	uint16_t version,
239	int s,
240	struct sockaddr *sa,
241	int sa_size,
242	krb5_data *in_data)
243{
244    krb5_error_code ret;
245    char *client = NULL, *admin = NULL;
246    const char *pwd_reason;
247    kadm5_config_params conf;
248    void *kadm5_handle = NULL;
249    krb5_principal principal = NULL;
250    krb5_data *pwd_data = NULL;
251    char *tmp;
252    ChangePasswdDataMS chpw;
253
254    memset (&conf, 0, sizeof(conf));
255    memset(&chpw, 0, sizeof(chpw));
256
257    if (version == KRB5_KPASSWD_VERS_CHANGEPW) {
258	ret = krb5_copy_data(context, in_data, &pwd_data);
259	if (ret) {
260	    krb5_warn (context, ret, "krb5_copy_data");
261	    reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
262			"out out memory copying password");
263	    return;
264	}
265	principal = admin_principal;
266    } else if (version == KRB5_KPASSWD_VERS_SETPW) {
267	size_t len;
268
269	ret = decode_ChangePasswdDataMS(in_data->data, in_data->length,
270					&chpw, &len);
271	if (ret) {
272	    krb5_warn (context, ret, "decode_ChangePasswdDataMS");
273	    reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
274			"malformed ChangePasswdData");
275	    return;
276	}
277
278
279	ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data);
280	if (ret) {
281	    krb5_warn (context, ret, "krb5_copy_data");
282	    reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
283			"out out memory copying password");
284	    goto out;
285	}
286
287	if (chpw.targname == NULL && chpw.targrealm != NULL) {
288	    krb5_warn (context, ret, "kadm5_init_with_password_ctx");
289	    reply_priv (auth_context, s, sa, sa_size,
290			KRB5_KPASSWD_MALFORMED,
291			"targrealm but not targname");
292	    goto out;
293	}
294
295	if (chpw.targname) {
296	    krb5_principal_data princ;
297
298	    princ.name = *chpw.targname;
299	    princ.realm = *chpw.targrealm;
300	    if (princ.realm == NULL) {
301		ret = krb5_get_default_realm(context, &princ.realm);
302
303		if (ret) {
304		    krb5_warnx (context,
305				"kadm5_init_with_password_ctx: "
306				"failed to allocate realm");
307		    reply_priv (auth_context, s, sa, sa_size,
308				KRB5_KPASSWD_SOFTERROR,
309				"failed to allocate realm");
310		    goto out;
311		}
312	    }
313	    ret = krb5_copy_principal(context, &princ, &principal);
314	    if (*chpw.targrealm == NULL)
315		free(princ.realm);
316	    if (ret) {
317		krb5_warn(context, ret, "krb5_copy_principal");
318		reply_priv(auth_context, s, sa, sa_size,
319			   KRB5_KPASSWD_HARDERROR,
320			   "failed to allocate principal");
321		goto out;
322	    }
323	} else
324	    principal = admin_principal;
325    } else {
326	krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto");
327	reply_priv (auth_context, s, sa, sa_size,
328		    KRB5_KPASSWD_HARDERROR,
329		    "Unknown protocol used");
330	return;
331    }
332
333    ret = krb5_unparse_name (context, admin_principal, &admin);
334    if (ret) {
335	krb5_warn (context, ret, "unparse_name failed");
336	reply_priv (auth_context, s, sa, sa_size,
337		    KRB5_KPASSWD_HARDERROR, "out of memory error");
338	goto out;
339    }
340
341    conf.realm = principal->realm;
342    conf.mask |= KADM5_CONFIG_REALM;
343
344    ret = kadm5_init_with_password_ctx(context,
345				       admin,
346				       NULL,
347				       KADM5_ADMIN_SERVICE,
348				       &conf, 0, 0,
349				       &kadm5_handle);
350    if (ret) {
351	krb5_warn (context, ret, "kadm5_init_with_password_ctx");
352	reply_priv (auth_context, s, sa, sa_size, 2,
353		    "Internal error");
354	goto out;
355    }
356
357    ret = krb5_unparse_name(context, principal, &client);
358    if (ret) {
359	krb5_warn (context, ret, "unparse_name failed");
360	reply_priv (auth_context, s, sa, sa_size,
361		    KRB5_KPASSWD_HARDERROR, "out of memory error");
362	goto out;
363    }
364
365    /*
366     * Check password quality if not changing as administrator
367     */
368
369    if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {
370
371	pwd_reason = kadm5_check_password_quality (context, principal,
372						   pwd_data);
373	if (pwd_reason != NULL ) {
374	    krb5_warnx (context,
375			"%s didn't pass password quality check with error: %s",
376			client, pwd_reason);
377	    reply_priv (auth_context, s, sa, sa_size,
378			KRB5_KPASSWD_SOFTERROR, pwd_reason);
379	    goto out;
380	}
381	krb5_warnx (context, "Changing password for %s", client);
382    } else {
383	ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
384					  principal);
385	if (ret) {
386	    krb5_warn (context, ret,
387		       "Check ACL failed for %s for changing %s password",
388		       admin, client);
389	    reply_priv (auth_context, s, sa, sa_size,
390			KRB5_KPASSWD_HARDERROR, "permission denied");
391	    goto out;
392	}
393	krb5_warnx (context, "%s is changing password for %s", admin, client);
394    }
395
396    ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
397    if (ret) {
398	krb5_warn (context, ret, "malloc: out of memory");
399	reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR,
400		    "Internal error");
401	goto out;
402    }
403    tmp = pwd_data->data;
404    tmp[pwd_data->length - 1] = '\0';
405
406    ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
407    krb5_free_data (context, pwd_data);
408    pwd_data = NULL;
409    if (ret) {
410	const char *str = krb5_get_error_message(context, ret);
411	krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str);
412	reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR,
413		    str ? str : "Internal error");
414	krb5_free_error_message(context, str);
415	goto out;
416    }
417    reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS,
418		"Password changed");
419out:
420    free_ChangePasswdDataMS(&chpw);
421    if (principal != admin_principal)
422	krb5_free_principal(context, principal);
423    if (admin)
424	free(admin);
425    if (client)
426	free(client);
427    if (pwd_data)
428	krb5_free_data(context, pwd_data);
429    if (kadm5_handle)
430	kadm5_destroy (kadm5_handle);
431}
432
433static int
434verify (krb5_auth_context *auth_context,
435	krb5_realm *realms,
436	krb5_keytab keytab,
437	krb5_ticket **ticket,
438	krb5_data *out_data,
439	uint16_t *version,
440	int s,
441	struct sockaddr *sa,
442	int sa_size,
443	u_char *msg,
444	size_t len)
445{
446    krb5_error_code ret;
447    uint16_t pkt_len, pkt_ver, ap_req_len;
448    krb5_data ap_req_data;
449    krb5_data krb_priv_data;
450    krb5_realm *r;
451
452    /*
453     * Only send an error reply if the request passes basic length
454     * verification.  Otherwise, kpasswdd would reply to every UDP packet,
455     * allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP
456     * packet with a source address of another UDP service that also replies
457     * to every packet.
458     *
459     * Also suppress the error reply if ap_req_len is 0, which indicates
460     * either an invalid request or an error packet.  An error packet may be
461     * the result of a ping-pong attacker pointing us at another kpasswdd.
462     */
463    pkt_len = (msg[0] << 8) | (msg[1]);
464    pkt_ver = (msg[2] << 8) | (msg[3]);
465    ap_req_len = (msg[4] << 8) | (msg[5]);
466    if (pkt_len != len) {
467	krb5_warnx (context, "Strange len: %ld != %ld",
468		    (long)pkt_len, (long)len);
469	return 1;
470    }
471    if (ap_req_len == 0) {
472	krb5_warnx (context, "Request is error packet (ap_req_len == 0)");
473	return 1;
474    }
475    if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW &&
476	pkt_ver != KRB5_KPASSWD_VERS_SETPW) {
477	krb5_warnx (context, "Bad version (%d)", pkt_ver);
478	reply_error (NULL, s, sa, sa_size, 0, 1, "Wrong program version");
479	return 1;
480    }
481    *version = pkt_ver;
482
483    ap_req_data.data   = msg + 6;
484    ap_req_data.length = ap_req_len;
485
486    ret = krb5_rd_req (context,
487		       auth_context,
488		       &ap_req_data,
489		       NULL,
490		       keytab,
491		       NULL,
492		       ticket);
493    if (ret) {
494	krb5_warn (context, ret, "krb5_rd_req");
495	reply_error (NULL, s, sa, sa_size, ret, 3, "Authentication failed");
496	return 1;
497    }
498
499    /* verify realm and principal */
500    for (r = realms; *r != NULL; r++) {
501	krb5_principal principal;
502	krb5_boolean same;
503
504	ret = krb5_make_principal (context,
505				   &principal,
506				   *r,
507				   "kadmin",
508				   "changepw",
509				   NULL);
510	if (ret)
511	    krb5_err (context, 1, ret, "krb5_make_principal");
512
513	same = krb5_principal_compare(context, principal, (*ticket)->server);
514	krb5_free_principal(context, principal);
515	if (same == TRUE)
516	    break;
517    }
518    if (*r == NULL) {
519	char *str;
520	krb5_unparse_name(context, (*ticket)->server, &str);
521	krb5_warnx (context, "client used not valid principal %s", str);
522	free(str);
523	reply_error (NULL, s, sa, sa_size, ret, 1,
524		     "Bad request");
525	goto out;
526    }
527
528    if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) {
529	krb5_warnx (context, "server realm (%s) not same a client realm (%s)",
530		    (*ticket)->server->realm, (*ticket)->client->realm);
531	reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
532		     "Bad request");
533	goto out;
534    }
535
536    if (!(*ticket)->ticket.flags.initial) {
537	krb5_warnx (context, "initial flag not set");
538	reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
539		     "Bad request");
540	goto out;
541    }
542    krb_priv_data.data   = msg + 6 + ap_req_len;
543    krb_priv_data.length = len - 6 - ap_req_len;
544
545    ret = krb5_rd_priv (context,
546			*auth_context,
547			&krb_priv_data,
548			out_data,
549			NULL);
550
551    if (ret) {
552	krb5_warn (context, ret, "krb5_rd_priv");
553	reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 3,
554		     "Bad request");
555	goto out;
556    }
557    return 0;
558out:
559    krb5_free_ticket (context, *ticket);
560    ticket = NULL;
561    return 1;
562}
563
564static void
565process (krb5_realm *realms,
566	 krb5_keytab keytab,
567	 int s,
568	 krb5_address *this_addr,
569	 struct sockaddr *sa,
570	 int sa_size,
571	 u_char *msg,
572	 int len)
573{
574    krb5_error_code ret;
575    krb5_auth_context auth_context = NULL;
576    krb5_data out_data;
577    krb5_ticket *ticket;
578    krb5_address other_addr;
579    uint16_t version;
580
581
582    krb5_data_zero (&out_data);
583
584    ret = krb5_auth_con_init (context, &auth_context);
585    if (ret) {
586	krb5_warn (context, ret, "krb5_auth_con_init");
587	return;
588    }
589
590    krb5_auth_con_setflags (context, auth_context,
591			    KRB5_AUTH_CONTEXT_DO_SEQUENCE);
592
593    ret = krb5_sockaddr2address (context, sa, &other_addr);
594    if (ret) {
595	krb5_warn (context, ret, "krb5_sockaddr2address");
596	goto out;
597    }
598
599    ret = krb5_auth_con_setaddrs (context,
600				  auth_context,
601				  this_addr,
602				  &other_addr);
603    krb5_free_address (context, &other_addr);
604    if (ret) {
605	krb5_warn (context, ret, "krb5_auth_con_setaddr");
606	goto out;
607    }
608
609    if (verify (&auth_context, realms, keytab, &ticket, &out_data,
610		&version, s, sa, sa_size, msg, len) == 0) {
611	change (auth_context,
612		ticket->client,
613		version,
614		s,
615		sa, sa_size,
616		&out_data);
617	memset (out_data.data, 0, out_data.length);
618	krb5_free_ticket (context, ticket);
619    }
620
621out:
622    krb5_data_free (&out_data);
623    krb5_auth_con_free (context, auth_context);
624}
625
626#ifdef INETD_SUPPORT
627/*
628 * XXX this code relies on getsockname() returning a valid local
629 * address for a "connected" DGRAM socket. This is true for most, but
630 * probably not all systems. For some systems, this could be done
631 * cleaner by using the IP_RECVDSTADDR option + recvmsg().
632 */
633static int
634get_local_addr(struct sockaddr *remote, int remlen,
635	       struct sockaddr *local, socklen_t *loclen)
636{
637	int s, ret;
638
639	s = socket(remote->sa_family, SOCK_DGRAM, 0);
640	if (s < 0)
641		return -1;
642
643	if (connect(s, remote, remlen) < 0) {
644		close(s);
645		return -1;
646	}
647
648	ret = getsockname(s, local, loclen);
649	close(s);
650	return ret;
651}
652#endif
653
654static int
655doit (krb5_keytab keytab, int port)
656{
657    krb5_error_code ret;
658    int *sockets;
659    int maxfd;
660    krb5_realm *realms;
661    krb5_addresses addrs;
662    krb5_address *my_addrp;
663    unsigned n, i;
664    fd_set real_fdset;
665    struct sockaddr_storage __ss;
666    struct sockaddr *sa = (struct sockaddr *)&__ss;
667#ifdef INETD_SUPPORT
668    int fdz;
669    int from_inetd;
670    socklen_t fromlen;
671    krb5_address my_addr;
672    struct sockaddr_storage __local;
673    struct sockaddr *localsa = (struct sockaddr *)&__local;
674#endif
675
676    ret = krb5_get_default_realms(context, &realms);
677    if (ret)
678	krb5_err (context, 1, ret, "krb5_get_default_realms");
679
680#ifdef INETD_SUPPORT
681    fromlen = sizeof __ss;
682    from_inetd = (getsockname(0, sa, &fromlen) == 0);
683
684    if (!from_inetd) {
685#endif
686    if (explicit_addresses.len) {
687	addrs = explicit_addresses;
688    } else {
689	ret = krb5_get_all_server_addrs (context, &addrs);
690	if (ret)
691	    krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
692    }
693    n = addrs.len;
694
695    sockets = malloc (n * sizeof(*sockets));
696    if (sockets == NULL)
697	krb5_errx (context, 1, "out of memory");
698    maxfd = -1;
699    FD_ZERO(&real_fdset);
700    for (i = 0; i < n; ++i) {
701	krb5_socklen_t sa_size = sizeof(__ss);
702
703	krb5_addr2sockaddr (context, &addrs.val[i], sa, &sa_size, port);
704
705	sockets[i] = socket (sa->sa_family, SOCK_DGRAM, 0);
706	if (sockets[i] < 0)
707	    krb5_err (context, 1, errno, "socket");
708	if (bind (sockets[i], sa, sa_size) < 0) {
709	    char str[128];
710	    size_t len;
711	    int save_errno = errno;
712
713	    ret = krb5_print_address (&addrs.val[i], str, sizeof(str), &len);
714	    if (ret)
715		strlcpy(str, "unknown address", sizeof(str));
716	    krb5_warn (context, save_errno, "bind(%s)", str);
717	    continue;
718	}
719	maxfd = max (maxfd, sockets[i]);
720	if (maxfd >= FD_SETSIZE)
721	    krb5_errx (context, 1, "fd too large");
722	FD_SET(sockets[i], &real_fdset);
723    }
724#ifdef INETD_SUPPORT
725    } else {
726        n = 1;
727        maxfd = 0;
728	fdz = 0;
729        sockets = &fdz;
730        FD_ZERO(&real_fdset);
731        FD_SET(0, &real_fdset);
732    }
733#endif
734    if (maxfd == -1)
735	krb5_errx (context, 1, "No sockets!");
736
737    while(exit_flag == 0) {
738	int ret;
739	fd_set fdset = real_fdset;
740
741	ret = select (maxfd + 1, &fdset, NULL, NULL, NULL);
742	if (ret < 0) {
743	    if (errno == EINTR)
744		continue;
745	    else
746		krb5_err (context, 1, errno, "select");
747	}
748	for (i = 0; i < n; ++i)
749	    if (FD_ISSET(sockets[i], &fdset)) {
750		u_char buf[BUFSIZ];
751		socklen_t addrlen = sizeof(__ss);
752
753		ret = recvfrom (sockets[i], buf, sizeof(buf), 0,
754				sa, &addrlen);
755		if (ret < 0) {
756		    if(errno == EINTR)
757			break;
758		    else
759			krb5_err (context, 1, errno, "recvfrom");
760		}
761#ifdef INETD_SUPPORT
762		if (from_inetd) {
763			socklen_t loclen = sizeof(__local);
764			int ret2;
765
766			ret2 = get_local_addr(sa, addrlen, localsa, &loclen);
767			if (ret2 < 0)
768				krb5_errx (context, errno, "get_local_addr");
769			ret2 = krb5_sockaddr2address(context, localsa,
770			    &my_addr);
771			if (ret2)
772				krb5_errx (context, ret2,
773				    "krb5_sockaddr2address");
774			my_addrp = &my_addr;
775		} else
776#endif
777		my_addrp = &addrs.val[i];
778
779		process (realms, keytab, sockets[i],
780			 my_addrp,
781			 sa, addrlen,
782			 buf, ret);
783#ifdef INETD_SUPPORT
784		if (from_inetd) {
785		    krb5_free_address(context, &my_addr);
786		}
787#endif
788	    }
789#ifdef INETD_SUPPORT
790	if (from_inetd)
791	    break;
792#endif
793    }
794
795    for (i = 0; i < n; ++i)
796	close(sockets[i]);
797    free(sockets);
798
799#ifdef INETD_SUPPORT
800    if (!from_inetd)
801#endif
802	krb5_free_addresses (context, &addrs);
803    krb5_free_host_realm (context, realms);
804    krb5_free_context (context);
805    return 0;
806}
807
808static RETSIGTYPE
809sigterm(int sig)
810{
811    exit_flag = 1;
812}
813
814static const char *check_library  = NULL;
815static const char *check_function = NULL;
816static getarg_strings policy_libraries = { 0, NULL };
817static char *keytab_str = "HDB:";
818static char *realm_str;
819static int version_flag;
820static int help_flag;
821static char *port_str;
822static char *config_file;
823
824struct getargs args[] = {
825#ifdef HAVE_DLOPEN
826    { "check-library", 0, arg_string, &check_library,
827      "library to load password check function from", "library" },
828    { "check-function", 0, arg_string, &check_function,
829      "password check function to load", "function" },
830    { "policy-libraries", 0, arg_strings, &policy_libraries,
831      "password check function to load", "function" },
832#endif
833    { "addresses",	0,	arg_strings, &addresses_str,
834      "addresses to listen on", "list of addresses" },
835    { "keytab", 'k', arg_string, &keytab_str,
836      "keytab to get authentication key from", "kspec" },
837    { "config-file", 'c', arg_string, &config_file },
838    { "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
839    { "port",  'p', arg_string, &port_str, "port" },
840    { "version", 0, arg_flag, &version_flag },
841    { "help", 0, arg_flag, &help_flag }
842};
843int num_args = sizeof(args) / sizeof(args[0]);
844
845int
846main (int argc, char **argv)
847{
848    krb5_keytab keytab;
849    krb5_error_code ret;
850    char **files;
851    int port, i;
852
853    krb5_program_setup(&context, argc, argv, args, num_args, NULL);
854
855    if(help_flag)
856	krb5_std_usage(0, args, num_args);
857    if(version_flag) {
858	print_version(NULL);
859	exit(0);
860    }
861
862    if (config_file == NULL) {
863	asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
864	if (config_file == NULL)
865	    errx(1, "out of memory");
866    }
867
868    ret = krb5_prepend_config_files_default(config_file, &files);
869    if (ret)
870	krb5_err(context, 1, ret, "getting configuration files");
871
872    ret = krb5_set_config_files(context, files);
873    krb5_free_config_files(files);
874    if (ret)
875	krb5_err(context, 1, ret, "reading configuration files");
876
877    if(realm_str)
878	krb5_set_default_realm(context, realm_str);
879
880    krb5_openlog (context, "kpasswdd", &log_facility);
881    krb5_set_warn_dest(context, log_facility);
882
883    if (port_str != NULL) {
884	struct servent *s = roken_getservbyname (port_str, "udp");
885
886	if (s != NULL)
887	    port = s->s_port;
888	else {
889	    char *ptr;
890
891	    port = strtol (port_str, &ptr, 10);
892	    if (port == 0 && ptr == port_str)
893		krb5_errx (context, 1, "bad port `%s'", port_str);
894	    port = htons(port);
895	}
896    } else
897	port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT);
898
899    ret = krb5_kt_register(context, &hdb_kt_ops);
900    if(ret)
901	krb5_err(context, 1, ret, "krb5_kt_register");
902
903    ret = krb5_kt_resolve(context, keytab_str, &keytab);
904    if(ret)
905	krb5_err(context, 1, ret, "%s", keytab_str);
906
907    kadm5_setup_passwd_quality_check (context, check_library, check_function);
908
909    for (i = 0; i < policy_libraries.num_strings; i++) {
910	ret = kadm5_add_passwd_quality_verifier(context,
911						policy_libraries.strings[i]);
912	if (ret)
913	    krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
914    }
915    ret = kadm5_add_passwd_quality_verifier(context, NULL);
916    if (ret)
917	krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
918
919
920    explicit_addresses.len = 0;
921
922    if (addresses_str.num_strings) {
923	int i;
924
925	for (i = 0; i < addresses_str.num_strings; ++i)
926	    add_one_address (addresses_str.strings[i], i == 0);
927	free_getarg_strings (&addresses_str);
928    } else {
929	char **foo = krb5_config_get_strings (context, NULL,
930					      "kdc", "addresses", NULL);
931
932	if (foo != NULL) {
933	    add_one_address (*foo++, TRUE);
934	    while (*foo)
935		add_one_address (*foo++, FALSE);
936	}
937    }
938
939#ifdef HAVE_SIGACTION
940    {
941	struct sigaction sa;
942
943	sa.sa_flags = 0;
944	sa.sa_handler = sigterm;
945	sigemptyset(&sa.sa_mask);
946
947	sigaction(SIGINT,  &sa, NULL);
948	sigaction(SIGTERM, &sa, NULL);
949    }
950#else
951    signal(SIGINT,  sigterm);
952    signal(SIGTERM, sigterm);
953#endif
954
955    pidfile(NULL);
956
957    return doit (keytab, port);
958}
959