1/*
2 * Copyright (c) 1997-2005 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 "kpasswd_locl.h"
35
36#include <kadm5/admin.h>
37#ifdef HAVE_SYS_UN_H
38#include <sys/un.h>
39#endif
40#include <hdb.h>
41#include <heim-ipc.h>
42#include <kadm5/private.h>
43
44static krb5_context context;
45static krb5_log_facility *log_facility;
46
47static struct getarg_strings addresses_str;
48krb5_addresses explicit_addresses;
49
50static krb5_keytab global_keytab = NULL;
51
52static void
53add_one_address (const char *str, int first)
54{
55    krb5_error_code ret;
56    krb5_addresses tmp;
57
58    ret = krb5_parse_address (context, str, &tmp);
59    if (ret)
60	krb5_err (context, 1, ret, "parse_address `%s'", str);
61    if (first)
62	krb5_copy_addresses(context, &tmp, &explicit_addresses);
63    else
64	krb5_append_addresses(context, &explicit_addresses, &tmp);
65    krb5_free_addresses (context, &tmp);
66}
67
68static void
69send_reply(krb5_data *ap_rep, krb5_data *rest,
70	   krb5_data *out_data)
71{
72    uint16_t ap_rep_len;
73    u_char *p;
74
75    if (ap_rep)
76	ap_rep_len = ap_rep->length;
77    else
78	ap_rep_len = 0;
79
80    if (krb5_data_alloc(out_data, 6 + ap_rep_len + rest->length))
81	return;
82
83    p = out_data->data;
84    *p++ = (out_data->length >> 8) & 0xFF;
85    *p++ = (out_data->length >> 0) & 0xFF;
86    *p++ = 0;
87    *p++ = 1;
88    *p++ = (ap_rep_len >> 8) & 0xFF;
89    *p++ = (ap_rep_len >> 0) & 0xFF;
90
91    if (ap_rep_len) {
92	memcpy(p, ap_rep->data, ap_rep->length);
93	p += ap_rep->length;
94    }
95
96    memcpy(p, rest->data, rest->length);
97}
98
99static int
100make_result (krb5_data *data,
101	     uint16_t result_code,
102	     const char *expl)
103{
104    char *str;
105    krb5_data_zero (data);
106
107    data->length = asprintf (&str,
108			     "%c%c%s",
109			     (result_code >> 8) & 0xFF,
110			     result_code & 0xFF,
111			     expl);
112
113    if (str == NULL) {
114	krb5_warnx (context, "Out of memory generating error reply");
115	return 1;
116    }
117    data->data = str;
118    return 0;
119}
120
121static void
122reply_error(krb5_realm realm,
123	    krb5_error_code error_code,
124	    uint16_t result_code,
125	    const char *expl,
126	    krb5_data *out_data)
127{
128    krb5_error_code ret;
129    krb5_data error_data;
130    krb5_data e_data;
131    krb5_principal server = NULL;
132
133    if (make_result(&e_data, result_code, expl))
134	return;
135
136    if (realm) {
137	ret = krb5_make_principal (context, &server, realm,
138				   "kadmin", "changepw", NULL);
139	if (ret) {
140	    krb5_data_free (&e_data);
141	    return;
142	}
143    }
144
145    ret = krb5_mk_error (context,
146			 error_code,
147			 NULL,
148			 &e_data,
149			 NULL,
150			 server,
151			 NULL,
152			 NULL,
153			 &error_data);
154    if (server)
155	krb5_free_principal(context, server);
156    krb5_data_free (&e_data);
157    if (ret) {
158	krb5_warn (context, ret, "Could not even generate error reply");
159	return;
160    }
161    send_reply(NULL, &error_data, out_data);
162    krb5_data_free (&error_data);
163}
164
165static void
166reply_priv(krb5_auth_context auth_context,
167	   uint16_t result_code,
168	   const char *expl,
169	   krb5_data *out_data)
170{
171    krb5_error_code ret;
172    krb5_data krb_priv_data;
173    krb5_data ap_rep_data;
174    krb5_data e_data;
175
176    ret = krb5_mk_rep (context,
177		       auth_context,
178		       &ap_rep_data);
179    if (ret) {
180	krb5_warn (context, ret, "Could not even generate error reply");
181	return;
182    }
183
184    if (make_result(&e_data, result_code, expl))
185	return;
186
187    ret = krb5_mk_priv (context,
188			auth_context,
189			&e_data,
190			&krb_priv_data,
191			NULL);
192    krb5_data_free (&e_data);
193    if (ret) {
194	krb5_warn (context, ret, "Could not even generate error reply");
195	return;
196    }
197    send_reply(&ap_rep_data, &krb_priv_data, out_data);
198    krb5_data_free (&ap_rep_data);
199    krb5_data_free (&krb_priv_data);
200}
201
202/*
203 * Change the password for `principal', sending the reply back on `s'
204 * (`sa', `sa_size') to `pwd_data'.
205 */
206
207static void
208change(krb5_auth_context auth_context,
209       krb5_principal admin_principal,
210       uint16_t version,
211       krb5_data *in_data,
212       krb5_data *out_data)
213{
214    krb5_error_code ret;
215    char *client = NULL, *admin = NULL;
216    const char *pwd_reason;
217    kadm5_config_params conf;
218    void *kadm5_handle = NULL;
219    krb5_principal principal = NULL;
220    krb5_data *pwd_data = NULL;
221    char *tmp;
222    ChangePasswdDataMS chpw;
223
224    memset (&conf, 0, sizeof(conf));
225    memset(&chpw, 0, sizeof(chpw));
226
227    if (version == KRB5_KPASSWD_VERS_CHANGEPW) {
228	ret = krb5_copy_data(context, in_data, &pwd_data);
229	if (ret) {
230	    krb5_warn (context, ret, "krb5_copy_data");
231	    reply_priv(auth_context, KRB5_KPASSWD_MALFORMED,
232		       "out out memory copying password", out_data);
233	    return;
234	}
235	principal = admin_principal;
236    } else if (version == KRB5_KPASSWD_VERS_SETPW) {
237	size_t len;
238
239	ret = decode_ChangePasswdDataMS(in_data->data, in_data->length,
240					&chpw, &len);
241	if (ret) {
242	    krb5_warn (context, ret, "decode_ChangePasswdDataMS");
243	    reply_priv(auth_context, KRB5_KPASSWD_MALFORMED,
244			"malformed ChangePasswdData", out_data);
245	    return;
246	}
247
248
249	ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data);
250	if (ret) {
251	    krb5_warn (context, ret, "krb5_copy_data");
252	    reply_priv(auth_context, KRB5_KPASSWD_MALFORMED,
253			"out out memory copying password", out_data);
254	    goto out;
255	}
256
257	if (chpw.targname == NULL && chpw.targrealm != NULL) {
258	    krb5_warn (context, ret, "kadm5_init_with_password_ctx");
259	    reply_priv(auth_context, KRB5_KPASSWD_MALFORMED,
260			"targrealm but not targname", out_data);
261	    goto out;
262	}
263
264	if (chpw.targname) {
265	    krb5_principal_data princ;
266
267	    princ.name = *chpw.targname;
268	    princ.realm = *chpw.targrealm;
269	    if (princ.realm == NULL) {
270		ret = krb5_get_default_realm(context, &princ.realm);
271
272		if (ret) {
273		    krb5_warnx (context,
274				"kadm5_init_with_password_ctx: "
275				"failed to allocate realm");
276		    reply_priv(auth_context, KRB5_KPASSWD_SOFTERROR,
277				"failed to allocate realm", out_data);
278		    goto out;
279		}
280	    }
281	    ret = krb5_copy_principal(context, &princ, &principal);
282	    if (*chpw.targrealm == NULL)
283		free(princ.realm);
284	    if (ret) {
285		krb5_warn(context, ret, "krb5_copy_principal");
286		reply_priv(auth_context, KRB5_KPASSWD_HARDERROR,
287			   "failed to allocate principal", out_data);
288		goto out;
289	    }
290	} else
291	    principal = admin_principal;
292    } else {
293	krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto");
294	reply_priv(auth_context, KRB5_KPASSWD_HARDERROR,
295		    "Unknown protocol used", out_data);
296	return;
297    }
298
299    ret = krb5_unparse_name (context, admin_principal, &admin);
300    if (ret) {
301	krb5_warn (context, ret, "unparse_name failed");
302	reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, "out of memory error", out_data);
303	goto out;
304    }
305
306    conf.realm = principal->realm;
307    conf.mask |= KADM5_CONFIG_REALM;
308
309    ret = kadm5_s_init_with_password_ctx(context,
310					 admin,
311					 NULL,
312					 KADM5_ADMIN_SERVICE,
313					 &conf, 0, 0,
314					 &kadm5_handle);
315    if (ret) {
316	krb5_warn (context, ret, "kadm5_init_with_password_ctx");
317	reply_priv(auth_context, 2, "Internal error", out_data);
318	goto out;
319    }
320
321    ret = krb5_unparse_name(context, principal, &client);
322    if (ret) {
323	krb5_warn (context, ret, "unparse_name failed");
324	reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, "out of memory error", out_data);
325	goto out;
326    }
327
328    /*
329     * Check password quality if not changing as administrator
330     */
331
332    if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {
333
334	pwd_reason = kadm5_check_password_quality (context, principal,
335						   pwd_data);
336	if (pwd_reason != NULL ) {
337	    krb5_warnx (context,
338			"%s didn't pass password quality check with error: %s",
339			client, pwd_reason);
340	    reply_priv(auth_context, KRB5_KPASSWD_SOFTERROR, pwd_reason, out_data);
341	    goto out;
342	}
343	krb5_warnx (context, "Changing password for %s", client);
344    } else {
345	ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
346					  principal);
347	if (ret) {
348	    krb5_warn (context, ret,
349		       "Check ACL failed for %s for changing %s password",
350		       admin, client);
351	    reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, "permission denied", out_data);
352	    goto out;
353	}
354	krb5_warnx (context, "%s is changing password for %s", admin, client);
355    }
356
357    ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
358    if (ret) {
359	krb5_warn (context, ret, "malloc: out of memory");
360	reply_priv(auth_context, KRB5_KPASSWD_HARDERROR,
361		    "Internal error", out_data);
362	goto out;
363    }
364    tmp = pwd_data->data;
365    tmp[pwd_data->length - 1] = '\0';
366
367    ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, 1, tmp, 0, NULL);
368    krb5_free_data (context, pwd_data);
369    pwd_data = NULL;
370    if (ret) {
371	const char *str = krb5_get_error_message(context, ret);
372	krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str);
373	reply_priv(auth_context, KRB5_KPASSWD_SOFTERROR,
374		    str ? str : "Internal error", out_data);
375	krb5_free_error_message(context, str);
376	goto out;
377    }
378    reply_priv(auth_context, KRB5_KPASSWD_SUCCESS,
379	       "Password changed", out_data);
380out:
381    free_ChangePasswdDataMS(&chpw);
382    if (principal != admin_principal)
383	krb5_free_principal(context, principal);
384    if (admin)
385	free(admin);
386    if (client)
387	free(client);
388    if (pwd_data)
389	krb5_free_data(context, pwd_data);
390    if (kadm5_handle)
391	kadm5_s_destroy (kadm5_handle);
392}
393
394static int
395verify(krb5_auth_context *auth_context,
396       krb5_realm *realms,
397       krb5_keytab keytab,
398       krb5_ticket **ticket,
399       uint16_t *version,
400       int s,
401       struct sockaddr *sa,
402       int sa_size,
403       u_char *msg,
404       size_t len,
405       krb5_data *out_data)
406{
407    krb5_error_code ret;
408    uint16_t pkt_len, pkt_ver, ap_req_len;
409    krb5_data ap_req_data;
410    krb5_data krb_priv_data;
411    krb5_realm *r;
412
413    /*
414     * Only send an error reply if the request passes basic length
415     * verification.  Otherwise, kpasswdd would reply to every UDP packet,
416     * allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP
417     * packet with a source address of another UDP service that also replies
418     * to every packet.
419     *
420     * Also suppress the error reply if ap_req_len is 0, which indicates
421     * either an invalid request or an error packet.  An error packet may be
422     * the result of a ping-pong attacker pointing us at another kpasswdd.
423     */
424    pkt_len = (msg[0] << 8) | (msg[1]);
425    pkt_ver = (msg[2] << 8) | (msg[3]);
426    ap_req_len = (msg[4] << 8) | (msg[5]);
427    if (pkt_len != len) {
428	krb5_warnx (context, "Strange len: %ld != %ld",
429		    (long)pkt_len, (long)len);
430	return 1;
431    }
432    if (ap_req_len == 0) {
433	krb5_warnx (context, "Request is error packet (ap_req_len == 0)");
434	return 1;
435    }
436    if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW &&
437	pkt_ver != KRB5_KPASSWD_VERS_SETPW) {
438	krb5_warnx (context, "Bad version (%d)", pkt_ver);
439	reply_error (NULL, 0, 1, "Wrong program version", out_data);
440	return 1;
441    }
442    *version = pkt_ver;
443
444    ap_req_data.data   = msg + 6;
445    ap_req_data.length = ap_req_len;
446
447    ret = krb5_rd_req (context,
448		       auth_context,
449		       &ap_req_data,
450		       NULL,
451		       keytab,
452		       NULL,
453		       ticket);
454    if (ret) {
455	krb5_warn (context, ret, "krb5_rd_req");
456	reply_error (NULL, ret, 3, "Authentication failed", out_data);
457	return 1;
458    }
459
460    /* verify realm and principal */
461    for (r = realms; *r != NULL; r++) {
462	krb5_principal principal;
463	krb5_boolean same;
464
465	ret = krb5_make_principal (context,
466				   &principal,
467				   *r,
468				   "kadmin",
469				   "changepw",
470				   NULL);
471	if (ret)
472	    krb5_err (context, 1, ret, "krb5_make_principal");
473
474	same = krb5_principal_compare(context, principal, (*ticket)->server);
475	krb5_free_principal(context, principal);
476	if (same == TRUE)
477	    break;
478    }
479    if (*r == NULL) {
480	char *str;
481	krb5_unparse_name(context, (*ticket)->server, &str);
482	krb5_warnx (context, "client used not valid principal %s", str);
483	free(str);
484	reply_error (NULL, ret, 1,
485		     "Bad request", out_data);
486	goto out;
487    }
488
489    if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) {
490	krb5_warnx (context, "server realm (%s) not same a client realm (%s)",
491		    (*ticket)->server->realm, (*ticket)->client->realm);
492	reply_error ((*ticket)->server->realm, ret, 1,
493		     "Bad request", out_data);
494	goto out;
495    }
496
497    if (!(*ticket)->ticket.flags.initial) {
498	krb5_warnx (context, "initial flag not set");
499	reply_error ((*ticket)->server->realm, ret, 1,
500		     "Bad request", out_data);
501	goto out;
502    }
503    krb_priv_data.data   = msg + 6 + ap_req_len;
504    krb_priv_data.length = len - 6 - ap_req_len;
505
506    ret = krb5_rd_priv (context,
507			*auth_context,
508			&krb_priv_data,
509			out_data,
510			NULL);
511
512    if (ret) {
513	krb5_warn (context, ret, "krb5_rd_priv");
514	reply_error ((*ticket)->server->realm, ret, 3,
515		     "Bad request", out_data);
516	goto out;
517    }
518    return 0;
519out:
520    krb5_free_ticket (context, *ticket);
521    ticket = NULL;
522    return 1;
523}
524
525static krb5_error_code
526process(int s,
527	struct sockaddr *server,
528	int server_size,
529	struct sockaddr *client,
530	int client_size,
531	u_char *msg,
532	size_t len,
533	heim_idata *out_data)
534{
535    krb5_error_code ret;
536    krb5_auth_context auth_context = NULL;
537    krb5_ticket *ticket;
538    uint16_t version;
539    krb5_realm *realms = NULL;
540    krb5_data clear_data;
541    krb5_address client_addr, server_addr;
542
543    krb5_data_zero(&clear_data);
544    memset(&client_addr, 0, sizeof(client_addr));
545    memset(&server_addr, 0, sizeof(server_addr));
546
547    ret = krb5_sockaddr2address(context, client, &client_addr);
548    if (ret) {
549	krb5_warn(context, ret, "krb5_sockaddr2address");
550	goto out;
551    }
552    ret = krb5_sockaddr2address(context, server, &server_addr);
553    if (ret) {
554	krb5_warn(context, ret, "krb5_sockaddr2address");
555	goto out;
556    }
557
558    {
559	char cstr[200];
560	size_t ss;
561	krb5_print_address(&client_addr, cstr, sizeof(cstr), &ss);
562	krb5_warnx(context, "request from client: %s", cstr);
563    }
564
565    ret = krb5_get_default_realms(context, &realms);
566    if (ret) {
567	krb5_warn(context, ret, "krb5_get_default_realms");
568	return ret;
569    }
570
571    ret = krb5_auth_con_init(context, &auth_context);
572    if (ret) {
573	krb5_warn (context, ret, "krb5_auth_con_init");
574	goto out;
575    }
576
577    krb5_auth_con_setflags(context, auth_context,
578			   KRB5_AUTH_CONTEXT_DO_SEQUENCE);
579
580    if (verify(&auth_context, realms, global_keytab, &ticket,
581	       &version, s, client, client_size, msg, len, &clear_data) == 0)
582    {
583	ret = krb5_auth_con_setaddrs(context,
584				     auth_context,
585				     &server_addr,
586				     &client_addr);
587	if (ret) {
588	    krb5_warn(context, ret, "krb5_sockaddr2address");
589	    goto out;
590	}
591
592	change(auth_context, ticket->client,
593	       version, &clear_data, out_data);
594
595	krb5_free_ticket(context, ticket);
596    }
597
598out:
599    krb5_free_address(context, &client_addr);
600    krb5_free_address(context, &server_addr);
601    if (clear_data.data)
602	memset(clear_data.data, 0, clear_data.length);
603    krb5_data_free(&clear_data);
604
605    krb5_auth_con_free(context, auth_context);
606    if (realms)
607	krb5_free_host_realm(context, realms);
608    return ret;
609}
610
611struct data {
612    rk_socket_t s;
613    struct sockaddr *sa;
614    struct sockaddr_storage __ss;
615    krb5_socklen_t sa_size;
616    heim_sipc service;
617    struct data *next;
618};
619
620/*
621 * Linked list to not leak memory
622 */
623static struct data *head_data = NULL;
624
625/*
626 *
627 */
628
629static void
630passwd_service(void *ctx, const heim_idata *req,
631	       const heim_icred cred,
632	       heim_ipc_complete complete,
633	       heim_sipc_call cctx)
634{
635    struct data *data = ctx;
636    heim_idata out_data;
637    krb5_error_code ret;
638    struct sockaddr *client, *server;
639    krb5_socklen_t client_size, server_size;
640
641    client = heim_ipc_cred_get_client_address(cred, &client_size);
642    heim_assert(client != NULL, "no address from client");
643
644    server = heim_ipc_cred_get_server_address(cred, &server_size);
645    heim_assert(server != NULL, "no address from server");
646
647    krb5_data_zero(&out_data);
648
649    ret = process(data->s,
650		  server, server_size,
651		  client, client_size,
652		  req->data, req->length, &out_data);
653
654    complete(cctx, ret, &out_data);
655
656    memset (out_data.data, 0, out_data.length);
657    krb5_data_free(&out_data);
658}
659
660
661
662static void
663listen_on(krb5_address *addr, int type, int port)
664{
665    krb5_error_code ret;
666    struct data *data;
667
668    data = calloc(1, sizeof(*data));
669    if (data == NULL)
670	krb5_errx(context, 1, "out of memory");
671
672    data->sa = (struct sockaddr *)&data->__ss;
673    data->sa_size = sizeof(data->__ss);
674
675    krb5_addr2sockaddr(context, addr, data->sa, &data->sa_size, port);
676
677    data->s = socket(data->sa->sa_family, type, 0);
678    if (data->s < 0) {
679	krb5_warn(context, errno, "socket");
680	free(data);
681	return;
682    }
683    socket_set_nopipe(data->s, 1);
684    socket_set_reuseaddr(data->s, 1);
685#ifdef HAVE_IPV6
686    if (data->sa->sa_family == AF_INET6)
687	socket_set_ipv6only(data->s, 1);
688#endif
689
690    socket_set_ipv6only(data->s, 1);
691    socket_set_reuseaddr(data->s, 1);
692
693    if (bind(data->s, data->sa, data->sa_size) < 0) {
694	char str[128];
695	size_t len;
696	int save_errno = errno;
697
698	ret = krb5_print_address (addr, str, sizeof(str), &len);
699	if (ret)
700	    strlcpy(str, "unknown address", sizeof(str));
701	krb5_warn (context, save_errno, "bind(%s/%d)", str, ntohs(port));
702	rk_closesocket(data->s);
703	free(data);
704	return;
705    }
706
707    if(type == SOCK_STREAM) {
708	if (listen(data->s, SOMAXCONN) < 0){
709	    char a_str[256];
710	    size_t len;
711
712	    krb5_print_address(addr, a_str, sizeof(a_str), &len);
713	    krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port));
714	    rk_closesocket(data->s);
715	    free(data);
716	    return;
717	}
718
719	ret = heim_sipc_stream_listener(data->s,
720					HEIM_SIPC_TYPE_UINT32 | HEIM_SIPC_TYPE_ONE_REQUEST,
721					passwd_service, data, &data->service);
722	if (ret)
723	    errx(1, "heim_sipc_stream_listener: %d", ret);
724    } else {
725	ret = heim_sipc_service_dgram(data->s, 0,
726				      passwd_service, data, &data->service);
727	if (ret)
728	    errx(1, "heim_sipc_service_dgram: %d", ret);
729    }
730
731    data->next = head_data;
732    head_data = data;
733}
734
735static int
736doit(int port)
737{
738    krb5_error_code ret;
739    krb5_addresses addrs;
740    unsigned i;
741
742    if (explicit_addresses.len) {
743	addrs = explicit_addresses;
744    } else {
745#if defined(IPV6_PKTINFO) && defined(IP_PKTINFO)
746	ret = krb5_get_all_any_addrs(context, &addrs);
747#else
748	ret = krb5_get_all_server_addrs(context, &addrs);
749#endif
750	if (ret)
751	    krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
752    }
753
754    for (i = 0; i < addrs.len; ++i) {
755	listen_on(&addrs.val[i], SOCK_STREAM, port);
756	listen_on(&addrs.val[i], SOCK_DGRAM, port);
757    }
758
759    heim_ipc_main();
760
761    krb5_free_addresses (context, &addrs);
762    krb5_free_context (context);
763    return 0;
764}
765
766static void
767terminated(void *ctx)
768{
769    exit(1);
770}
771
772static const char *check_library  = NULL;
773static const char *check_function = NULL;
774static getarg_strings policy_libraries = { 0, NULL };
775static char sHDB[] = "HDB:";
776static char *keytab_str = sHDB;
777static char *realm_str;
778static int version_flag;
779static int help_flag;
780static char *port_str;
781static char *config_file;
782
783struct getargs args[] = {
784#ifdef HAVE_DLOPEN
785    { "check-library", 0, arg_string, &check_library,
786      "library to load password check function from", "library" },
787    { "check-function", 0, arg_string, &check_function,
788      "password check function to load", "function" },
789    { "policy-libraries", 0, arg_strings, &policy_libraries,
790      "password check function to load", "function" },
791#endif
792    { "addresses",	0,	arg_strings, &addresses_str,
793      "addresses to listen on", "list of addresses" },
794    { "keytab", 'k', arg_string, &keytab_str,
795      "keytab to get authentication key from", "kspec" },
796    { "config-file", 'c', arg_string, &config_file, NULL, NULL },
797    { "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
798    { "port",  'p', arg_string, &port_str, "port", NULL },
799    { "version", 0, arg_flag, &version_flag, NULL, NULL },
800    { "help", 0, arg_flag, &help_flag, NULL, NULL }
801};
802int num_args = sizeof(args) / sizeof(args[0]);
803
804static void
805usage(int ret)
806{
807    arg_printusage (args, num_args, NULL, "");
808    exit (ret);
809}
810
811
812int
813main (int argc, char **argv)
814{
815    int optidx = 0;
816    krb5_error_code ret;
817    char **files;
818    int port, i;
819
820    setprogname (argv[0]);
821
822    ret = krb5_init_context (&context);
823    if (ret)
824	errx(1, "krb5_init_context failed: %d", ret);
825
826    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
827	usage(1);
828
829    if(version_flag) {
830	print_version(NULL);
831	exit(0);
832    }
833
834    if (config_file == NULL) {
835	asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
836	if (config_file == NULL)
837	    errx(1, "out of memory");
838    }
839
840    ret = krb5_prepend_config_files_default(config_file, &files);
841    if (ret)
842	krb5_err(context, 1, ret, "getting configuration files");
843
844    ret = krb5_set_config_files(context, files);
845    krb5_free_config_files(files);
846    if (ret)
847	krb5_err(context, 1, ret, "reading configuration files");
848
849    if(realm_str)
850	krb5_set_default_realm(context, realm_str);
851
852    krb5_openlog (context, "kpasswdd", &log_facility);
853    krb5_set_warn_dest(context, log_facility);
854
855    if (port_str != NULL) {
856	struct servent *s = roken_getservbyname (port_str, "udp");
857
858	if (s != NULL)
859	    port = s->s_port;
860	else {
861	    char *ptr;
862
863	    port = (int)strtol (port_str, &ptr, 10);
864	    if (port == 0 && ptr == port_str)
865		krb5_errx (context, 1, "bad port `%s'", port_str);
866	    port = htons(port);
867	}
868    } else
869	port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT);
870
871    ret = krb5_kt_register(context, &hdb_kt_ops);
872    if(ret)
873	krb5_err(context, 1, ret, "krb5_kt_register");
874
875    ret = krb5_kt_resolve(context, keytab_str, &global_keytab);
876    if(ret)
877	krb5_err(context, 1, ret, "%s", keytab_str);
878
879    kadm5_setup_passwd_quality_check (context, check_library, check_function);
880
881    for (i = 0; i < policy_libraries.num_strings; i++) {
882	ret = kadm5_add_passwd_quality_verifier(context,
883						policy_libraries.strings[i]);
884	if (ret)
885	    krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
886    }
887    ret = kadm5_add_passwd_quality_verifier(context, NULL);
888    if (ret)
889	krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
890
891
892    explicit_addresses.len = 0;
893
894    if (addresses_str.num_strings) {
895	int j;
896
897	for (j = 0; j < addresses_str.num_strings; ++j)
898	    add_one_address (addresses_str.strings[j], j == 0);
899	free_getarg_strings (&addresses_str);
900    } else {
901	char **foo = krb5_config_get_strings (context, NULL,
902					      "kdc", "addresses", NULL);
903
904	if (foo != NULL) {
905	    add_one_address (*foo++, TRUE);
906	    while (*foo)
907		add_one_address (*foo++, FALSE);
908	}
909    }
910
911    heim_sipc_signal_handler(SIGINT, terminated, "SIGINT");
912    heim_sipc_signal_handler(SIGTERM, terminated, "SIGTERM");
913
914    pidfile(NULL);
915
916    return doit (port);
917}
918