No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / kpasswd / kpasswdd.c
blob06aadaf988c5a0eb860165277249786d5acbd1a7
1 /*
2 * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "kpasswd_locl.h"
35 __RCSID("$Heimdal: kpasswdd.c 22252 2007-12-09 05:59:34Z lha $"
36 "$NetBSD: kpasswdd.c,v 1.10 2008/03/22 08:37:03 mlelstv Exp $");
38 #include <kadm5/admin.h>
39 #ifdef HAVE_SYS_UN_H
40 #include <sys/un.h>
41 #endif
42 #include <hdb.h>
43 #include <kadm5/private.h>
45 static krb5_context context;
46 static krb5_log_facility *log_facility;
48 static struct getarg_strings addresses_str;
49 krb5_addresses explicit_addresses;
51 static sig_atomic_t exit_flag = 0;
53 static void
54 add_one_address (const char *str, int first)
56 krb5_error_code ret;
57 krb5_addresses tmp;
59 ret = krb5_parse_address (context, str, &tmp);
60 if (ret)
61 krb5_err (context, 1, ret, "parse_address `%s'", str);
62 if (first)
63 krb5_copy_addresses(context, &tmp, &explicit_addresses);
64 else
65 krb5_append_addresses(context, &explicit_addresses, &tmp);
66 krb5_free_addresses (context, &tmp);
69 static void
70 send_reply (int s,
71 struct sockaddr *sa,
72 int sa_size,
73 krb5_data *ap_rep,
74 krb5_data *rest)
76 struct msghdr msghdr;
77 struct iovec iov[3];
78 uint16_t len, ap_rep_len;
79 u_char header[6];
80 u_char *p;
82 if (ap_rep)
83 ap_rep_len = ap_rep->length;
84 else
85 ap_rep_len = 0;
87 len = 6 + ap_rep_len + rest->length;
88 p = header;
89 *p++ = (len >> 8) & 0xFF;
90 *p++ = (len >> 0) & 0xFF;
91 *p++ = 0;
92 *p++ = 1;
93 *p++ = (ap_rep_len >> 8) & 0xFF;
94 *p++ = (ap_rep_len >> 0) & 0xFF;
96 memset (&msghdr, 0, sizeof(msghdr));
97 msghdr.msg_name = (void *)sa;
98 msghdr.msg_namelen = sa_size;
99 msghdr.msg_iov = iov;
100 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
101 #if 0
102 msghdr.msg_control = NULL;
103 msghdr.msg_controllen = 0;
104 #endif
106 iov[0].iov_base = (char *)header;
107 iov[0].iov_len = 6;
108 if (ap_rep_len) {
109 iov[1].iov_base = ap_rep->data;
110 iov[1].iov_len = ap_rep->length;
111 } else {
112 iov[1].iov_base = NULL;
113 iov[1].iov_len = 0;
115 iov[2].iov_base = rest->data;
116 iov[2].iov_len = rest->length;
118 if (sendmsg (s, &msghdr, 0) < 0)
119 krb5_warn (context, errno, "sendmsg");
122 static int
123 make_result (krb5_data *data,
124 uint16_t result_code,
125 const char *expl)
127 char *str;
128 krb5_data_zero (data);
130 data->length = asprintf (&str,
131 "%c%c%s",
132 (result_code >> 8) & 0xFF,
133 result_code & 0xFF,
134 expl);
136 if (str == NULL) {
137 krb5_warnx (context, "Out of memory generating error reply");
138 return 1;
140 data->data = str;
141 return 0;
144 static void
145 reply_error (krb5_realm realm,
146 int s,
147 struct sockaddr *sa,
148 int sa_size,
149 krb5_error_code error_code,
150 uint16_t result_code,
151 const char *expl)
153 krb5_error_code ret;
154 krb5_data error_data;
155 krb5_data e_data;
156 krb5_principal server = NULL;
158 if (make_result(&e_data, result_code, expl))
159 return;
161 if (realm) {
162 ret = krb5_make_principal (context, &server, realm,
163 "kadmin", "changepw", NULL);
164 if (ret) {
165 krb5_data_free (&e_data);
166 return;
170 ret = krb5_mk_error (context,
171 error_code,
172 NULL,
173 &e_data,
174 NULL,
175 server,
176 NULL,
177 NULL,
178 &error_data);
179 if (server)
180 krb5_free_principal(context, server);
181 krb5_data_free (&e_data);
182 if (ret) {
183 krb5_warn (context, ret, "Could not even generate error reply");
184 return;
186 send_reply (s, sa, sa_size, NULL, &error_data);
187 krb5_data_free (&error_data);
190 static void
191 reply_priv (krb5_auth_context auth_context,
192 int s,
193 struct sockaddr *sa,
194 int sa_size,
195 uint16_t result_code,
196 const char *expl)
198 krb5_error_code ret;
199 krb5_data krb_priv_data;
200 krb5_data ap_rep_data;
201 krb5_data e_data;
203 ret = krb5_mk_rep (context,
204 auth_context,
205 &ap_rep_data);
206 if (ret) {
207 krb5_warn (context, ret, "Could not even generate error reply");
208 return;
211 if (make_result(&e_data, result_code, expl))
212 return;
214 ret = krb5_mk_priv (context,
215 auth_context,
216 &e_data,
217 &krb_priv_data,
218 NULL);
219 krb5_data_free (&e_data);
220 if (ret) {
221 krb5_warn (context, ret, "Could not even generate error reply");
222 return;
224 send_reply (s, sa, sa_size, &ap_rep_data, &krb_priv_data);
225 krb5_data_free (&ap_rep_data);
226 krb5_data_free (&krb_priv_data);
230 * Change the password for `principal', sending the reply back on `s'
231 * (`sa', `sa_size') to `pwd_data'.
234 static void
235 change (krb5_auth_context auth_context,
236 krb5_principal admin_principal,
237 uint16_t version,
238 int s,
239 struct sockaddr *sa,
240 int sa_size,
241 krb5_data *in_data)
243 krb5_error_code ret;
244 char *client = NULL, *admin = NULL;
245 const char *pwd_reason;
246 kadm5_config_params conf;
247 void *kadm5_handle = NULL;
248 krb5_principal principal;
249 krb5_data *pwd_data = NULL;
250 char *tmp;
251 ChangePasswdDataMS chpw;
253 memset (&conf, 0, sizeof(conf));
254 memset(&chpw, 0, sizeof(chpw));
256 if (version == KRB5_KPASSWD_VERS_CHANGEPW) {
257 ret = krb5_copy_data(context, in_data, &pwd_data);
258 if (ret) {
259 krb5_warn (context, ret, "krb5_copy_data");
260 reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
261 "out out memory copying password");
262 return;
264 principal = admin_principal;
265 } else if (version == KRB5_KPASSWD_VERS_SETPW) {
266 size_t len;
268 ret = decode_ChangePasswdDataMS(in_data->data, in_data->length,
269 &chpw, &len);
270 if (ret) {
271 krb5_warn (context, ret, "decode_ChangePasswdDataMS");
272 reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
273 "malformed ChangePasswdData");
274 return;
278 ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data);
279 if (ret) {
280 krb5_warn (context, ret, "krb5_copy_data");
281 reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
282 "out out memory copying password");
283 goto out;
286 if (chpw.targname == NULL && chpw.targrealm != NULL) {
287 krb5_warn (context, ret, "kadm5_init_with_password_ctx");
288 reply_priv (auth_context, s, sa, sa_size,
289 KRB5_KPASSWD_MALFORMED,
290 "targrealm but not targname");
291 goto out;
294 if (chpw.targname) {
295 krb5_principal_data princ;
297 princ.name = *chpw.targname;
298 princ.realm = *chpw.targrealm;
299 if (princ.realm == NULL) {
300 ret = krb5_get_default_realm(context, &princ.realm);
302 if (ret) {
303 krb5_warnx (context,
304 "kadm5_init_with_password_ctx: "
305 "failed to allocate realm");
306 reply_priv (auth_context, s, sa, sa_size,
307 KRB5_KPASSWD_SOFTERROR,
308 "failed to allocate realm");
309 goto out;
312 ret = krb5_copy_principal(context, &princ, &principal);
313 if (*chpw.targrealm == NULL)
314 free(princ.realm);
315 if (ret) {
316 krb5_warn(context, ret, "krb5_copy_principal");
317 reply_priv(auth_context, s, sa, sa_size,
318 KRB5_KPASSWD_HARDERROR,
319 "failed to allocate principal");
320 goto out;
322 } else
323 principal = admin_principal;
324 } else {
325 krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto");
326 reply_priv (auth_context, s, sa, sa_size,
327 KRB5_KPASSWD_HARDERROR,
328 "Unknown protocol used");
329 return;
332 ret = krb5_unparse_name (context, admin_principal, &admin);
333 if (ret) {
334 krb5_warn (context, ret, "unparse_name failed");
335 reply_priv (auth_context, s, sa, sa_size,
336 KRB5_KPASSWD_HARDERROR, "out of memory error");
337 goto out;
340 conf.realm = principal->realm;
341 conf.mask |= KADM5_CONFIG_REALM;
343 ret = kadm5_init_with_password_ctx(context,
344 admin,
345 NULL,
346 KADM5_ADMIN_SERVICE,
347 &conf, 0, 0,
348 &kadm5_handle);
349 if (ret) {
350 krb5_warn (context, ret, "kadm5_init_with_password_ctx");
351 reply_priv (auth_context, s, sa, sa_size, 2,
352 "Internal error");
353 goto out;
356 ret = krb5_unparse_name(context, principal, &client);
357 if (ret) {
358 krb5_warn (context, ret, "unparse_name failed");
359 reply_priv (auth_context, s, sa, sa_size,
360 KRB5_KPASSWD_HARDERROR, "out of memory error");
361 goto out;
365 * Check password quality if not changing as administrator
368 if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {
370 pwd_reason = kadm5_check_password_quality (context, principal,
371 pwd_data);
372 if (pwd_reason != NULL ) {
373 krb5_warnx (context,
374 "%s didn't pass password quality check with error: %s",
375 client, pwd_reason);
376 reply_priv (auth_context, s, sa, sa_size,
377 KRB5_KPASSWD_SOFTERROR, pwd_reason);
378 goto out;
380 krb5_warnx (context, "Changing password for %s", client);
381 } else {
382 ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
383 principal);
384 if (ret) {
385 krb5_warn (context, ret,
386 "Check ACL failed for %s for changing %s password",
387 admin, client);
388 reply_priv (auth_context, s, sa, sa_size,
389 KRB5_KPASSWD_HARDERROR, "permission denied");
390 goto out;
392 krb5_warnx (context, "%s is changing password for %s", admin, client);
395 ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
396 if (ret) {
397 krb5_warn (context, ret, "malloc: out of memory");
398 reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR,
399 "Internal error");
400 goto out;
402 tmp = pwd_data->data;
403 tmp[pwd_data->length - 1] = '\0';
405 ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
406 krb5_free_data (context, pwd_data);
407 pwd_data = NULL;
408 if (ret) {
409 char *str = krb5_get_error_message(context, ret);
410 krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str);
411 reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR,
412 str ? str : "Internal error");
413 krb5_free_error_string(context, str);
414 goto out;
416 reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS,
417 "Password changed");
418 out:
419 free_ChangePasswdDataMS(&chpw);
420 if (admin)
421 free(admin);
422 if (client)
423 free(client);
424 if (pwd_data)
425 krb5_free_data(context, pwd_data);
426 if (kadm5_handle)
427 kadm5_destroy (kadm5_handle);
430 static int
431 verify (krb5_auth_context *auth_context,
432 krb5_realm *realms,
433 krb5_keytab keytab,
434 krb5_ticket **ticket,
435 krb5_data *out_data,
436 uint16_t *version,
437 int s,
438 struct sockaddr *sa,
439 int sa_size,
440 u_char *msg,
441 size_t len)
443 krb5_error_code ret;
444 uint16_t pkt_len, pkt_ver, ap_req_len;
445 krb5_data ap_req_data;
446 krb5_data krb_priv_data;
447 krb5_realm *r;
449 pkt_len = (msg[0] << 8) | (msg[1]);
450 pkt_ver = (msg[2] << 8) | (msg[3]);
451 ap_req_len = (msg[4] << 8) | (msg[5]);
452 if (pkt_len != len) {
453 krb5_warnx (context, "Strange len: %ld != %ld",
454 (long)pkt_len, (long)len);
455 reply_error (NULL, s, sa, sa_size, 0, 1, "Bad request");
456 return 1;
458 if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW &&
459 pkt_ver != KRB5_KPASSWD_VERS_SETPW) {
460 krb5_warnx (context, "Bad version (%d)", pkt_ver);
461 reply_error (NULL, s, sa, sa_size, 0, 1, "Wrong program version");
462 return 1;
464 *version = pkt_ver;
466 ap_req_data.data = msg + 6;
467 ap_req_data.length = ap_req_len;
469 ret = krb5_rd_req (context,
470 auth_context,
471 &ap_req_data,
472 NULL,
473 keytab,
474 NULL,
475 ticket);
476 if (ret) {
477 krb5_warn (context, ret, "krb5_rd_req");
478 reply_error (NULL, s, sa, sa_size, ret, 3, "Authentication failed");
479 return 1;
482 /* verify realm and principal */
483 for (r = realms; *r != NULL; r++) {
484 krb5_principal principal;
485 krb5_boolean same;
487 ret = krb5_make_principal (context,
488 &principal,
490 "kadmin",
491 "changepw",
492 NULL);
493 if (ret)
494 krb5_err (context, 1, ret, "krb5_make_principal");
496 same = krb5_principal_compare(context, principal, (*ticket)->server);
497 krb5_free_principal(context, principal);
498 if (same == TRUE)
499 break;
501 if (*r == NULL) {
502 char *str;
503 krb5_unparse_name(context, (*ticket)->server, &str);
504 krb5_warnx (context, "client used not valid principal %s", str);
505 free(str);
506 reply_error (NULL, s, sa, sa_size, ret, 1,
507 "Bad request");
508 goto out;
511 if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) {
512 krb5_warnx (context, "server realm (%s) not same a client realm (%s)",
513 (*ticket)->server->realm, (*ticket)->client->realm);
514 reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
515 "Bad request");
516 goto out;
519 if (!(*ticket)->ticket.flags.initial) {
520 krb5_warnx (context, "initial flag not set");
521 reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
522 "Bad request");
523 goto out;
525 krb_priv_data.data = msg + 6 + ap_req_len;
526 krb_priv_data.length = len - 6 - ap_req_len;
528 ret = krb5_rd_priv (context,
529 *auth_context,
530 &krb_priv_data,
531 out_data,
532 NULL);
534 if (ret) {
535 krb5_warn (context, ret, "krb5_rd_priv");
536 reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 3,
537 "Bad request");
538 goto out;
540 return 0;
541 out:
542 krb5_free_ticket (context, *ticket);
543 ticket = NULL;
544 return 1;
547 static void
548 process (krb5_realm *realms,
549 krb5_keytab keytab,
550 int s,
551 krb5_address *this_addr,
552 struct sockaddr *sa,
553 int sa_size,
554 u_char *msg,
555 int len)
557 krb5_error_code ret;
558 krb5_auth_context auth_context = NULL;
559 krb5_data out_data;
560 krb5_ticket *ticket;
561 krb5_address other_addr;
562 uint16_t version;
565 krb5_data_zero (&out_data);
567 ret = krb5_auth_con_init (context, &auth_context);
568 if (ret) {
569 krb5_warn (context, ret, "krb5_auth_con_init");
570 return;
573 krb5_auth_con_setflags (context, auth_context,
574 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
576 ret = krb5_sockaddr2address (context, sa, &other_addr);
577 if (ret) {
578 krb5_warn (context, ret, "krb5_sockaddr2address");
579 goto out;
582 ret = krb5_auth_con_setaddrs (context,
583 auth_context,
584 this_addr,
585 &other_addr);
586 krb5_free_address (context, &other_addr);
587 if (ret) {
588 krb5_warn (context, ret, "krb5_auth_con_setaddr");
589 goto out;
592 if (verify (&auth_context, realms, keytab, &ticket, &out_data,
593 &version, s, sa, sa_size, msg, len) == 0) {
594 change (auth_context,
595 ticket->client,
596 version,
598 sa, sa_size,
599 &out_data);
600 memset (out_data.data, 0, out_data.length);
601 krb5_free_ticket (context, ticket);
604 out:
605 krb5_data_free (&out_data);
606 krb5_auth_con_free (context, auth_context);
609 #ifdef INETD_SUPPORT
611 * XXX this code relies on getsockname() returning a valid local
612 * address for a "connected" DGRAM socket. This is true for most, but
613 * probably not all systems. For some systems, this could be done
614 * cleaner by using the IP_RECVDSTADDR option + recvmsg().
616 static int
617 get_local_addr(struct sockaddr *remote, int remlen,
618 struct sockaddr *local, socklen_t *loclen)
620 int s, ret;
622 s = socket(remote->sa_family, SOCK_DGRAM, 0);
623 if (s < 0)
624 return -1;
626 if (connect(s, remote, remlen) < 0) {
627 close(s);
628 return -1;
631 ret = getsockname(s, local, loclen);
632 close(s);
633 return ret;
635 #endif
637 static int
638 doit (krb5_keytab keytab, int port)
640 krb5_error_code ret;
641 int *sockets;
642 int maxfd;
643 krb5_realm *realms;
644 krb5_addresses addrs;
645 krb5_address *my_addrp;
646 unsigned n, i;
647 fd_set real_fdset;
648 struct sockaddr_storage __ss;
649 struct sockaddr *sa = (struct sockaddr *)&__ss;
650 #ifdef INETD_SUPPORT
651 int fdz;
652 int from_inetd;
653 socklen_t fromlen;
654 krb5_address my_addr;
655 struct sockaddr_storage __local;
656 struct sockaddr *localsa = (struct sockaddr *)&__local;
657 #endif
659 ret = krb5_get_default_realms(context, &realms);
660 if (ret)
661 krb5_err (context, 1, ret, "krb5_get_default_realms");
663 #ifdef INETD_SUPPORT
664 fromlen = sizeof __ss;
665 from_inetd = (getsockname(0, sa, &fromlen) == 0);
667 if (!from_inetd) {
668 #endif
669 if (explicit_addresses.len) {
670 addrs = explicit_addresses;
671 } else {
672 ret = krb5_get_all_server_addrs (context, &addrs);
673 if (ret)
674 krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
676 n = addrs.len;
678 sockets = malloc (n * sizeof(*sockets));
679 if (sockets == NULL)
680 krb5_errx (context, 1, "out of memory");
681 maxfd = -1;
682 FD_ZERO(&real_fdset);
683 for (i = 0; i < n; ++i) {
684 krb5_socklen_t sa_size = sizeof(__ss);
686 krb5_addr2sockaddr (context, &addrs.val[i], sa, &sa_size, port);
688 sockets[i] = socket (sa->sa_family, SOCK_DGRAM, 0);
689 if (sockets[i] < 0)
690 krb5_err (context, 1, errno, "socket");
691 if (bind (sockets[i], sa, sa_size) < 0) {
692 char str[128];
693 size_t len;
694 int save_errno = errno;
696 ret = krb5_print_address (&addrs.val[i], str, sizeof(str), &len);
697 if (ret)
698 strlcpy(str, "unknown address", sizeof(str));
699 krb5_warn (context, save_errno, "bind(%s)", str);
700 continue;
702 maxfd = max (maxfd, sockets[i]);
703 if (maxfd >= FD_SETSIZE)
704 krb5_errx (context, 1, "fd too large");
705 FD_SET(sockets[i], &real_fdset);
707 #ifdef INETD_SUPPORT
708 } else {
709 n = 1;
710 maxfd = 0;
711 fdz = 0;
712 sockets = &fdz;
713 FD_ZERO(&real_fdset);
714 FD_SET(0, &real_fdset);
716 #endif
717 if (maxfd == -1)
718 krb5_errx (context, 1, "No sockets!");
720 while(exit_flag == 0) {
721 int ret;
722 fd_set fdset = real_fdset;
724 ret = select (maxfd + 1, &fdset, NULL, NULL, NULL);
725 if (ret < 0) {
726 if (errno == EINTR)
727 continue;
728 else
729 krb5_err (context, 1, errno, "select");
731 for (i = 0; i < n; ++i)
732 if (FD_ISSET(sockets[i], &fdset)) {
733 u_char buf[BUFSIZ];
734 socklen_t addrlen = sizeof(__ss);
736 ret = recvfrom (sockets[i], buf, sizeof(buf), 0,
737 sa, &addrlen);
738 if (ret < 0) {
739 if(errno == EINTR)
740 break;
741 else
742 krb5_err (context, 1, errno, "recvfrom");
744 #ifdef INETD_SUPPORT
745 if (from_inetd) {
746 socklen_t loclen = sizeof(__local);
747 int ret2;
749 ret2 = get_local_addr(sa, addrlen, localsa, &loclen);
750 if (ret2 < 0)
751 krb5_errx (context, errno, "get_local_addr");
752 ret2 = krb5_sockaddr2address(context, localsa,
753 &my_addr);
754 if (ret2)
755 krb5_errx (context, ret2,
756 "krb5_sockaddr2address");
757 my_addrp = &my_addr;
758 } else
759 #endif
760 my_addrp = &addrs.val[i];
762 process (realms, keytab, sockets[i],
763 my_addrp,
764 sa, addrlen,
765 buf, ret);
766 #ifdef INETD_SUPPORT
767 if (from_inetd) {
768 krb5_free_address(context, &my_addr);
770 #endif
772 #ifdef INETD_SUPPORT
773 if (from_inetd)
774 break;
775 #endif
778 for (i = 0; i < n; ++i)
779 close(sockets[i]);
780 free(sockets);
782 #ifdef INETD_SUPPORT
783 if (!from_inetd)
784 #endif
785 krb5_free_addresses (context, &addrs);
786 krb5_free_host_realm (context, realms);
787 krb5_free_context (context);
788 return 0;
791 static RETSIGTYPE
792 sigterm(int sig)
794 exit_flag = 1;
797 static const char *check_library = NULL;
798 static const char *check_function = NULL;
799 static getarg_strings policy_libraries = { 0, NULL };
800 static char *keytab_str = "HDB:";
801 static char *realm_str;
802 static int version_flag;
803 static int help_flag;
804 static char *port_str;
805 static char *config_file;
807 struct getargs args[] = {
808 #ifdef HAVE_DLOPEN
809 { "check-library", 0, arg_string, &check_library,
810 "library to load password check function from", "library" },
811 { "check-function", 0, arg_string, &check_function,
812 "password check function to load", "function" },
813 { "policy-libraries", 0, arg_strings, &policy_libraries,
814 "password check function to load", "function" },
815 #endif
816 { "addresses", 0, arg_strings, &addresses_str,
817 "addresses to listen on", "list of addresses" },
818 { "keytab", 'k', arg_string, &keytab_str,
819 "keytab to get authentication key from", "kspec" },
820 { "config-file", 'c', arg_string, &config_file },
821 { "realm", 'r', arg_string, &realm_str, "default realm", "realm" },
822 { "port", 'p', arg_string, &port_str, "port" },
823 { "version", 0, arg_flag, &version_flag },
824 { "help", 0, arg_flag, &help_flag }
826 int num_args = sizeof(args) / sizeof(args[0]);
829 main (int argc, char **argv)
831 int optind;
832 krb5_keytab keytab;
833 krb5_error_code ret;
834 char **files;
835 int port, i;
837 optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
839 if(help_flag)
840 krb5_std_usage(0, args, num_args);
841 if(version_flag) {
842 print_version(NULL);
843 exit(0);
846 if (config_file == NULL) {
847 asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
848 if (config_file == NULL)
849 errx(1, "out of memory");
852 ret = krb5_prepend_config_files_default(config_file, &files);
853 if (ret)
854 krb5_err(context, 1, ret, "getting configuration files");
856 ret = krb5_set_config_files(context, files);
857 krb5_free_config_files(files);
858 if (ret)
859 krb5_err(context, 1, ret, "reading configuration files");
861 if(realm_str)
862 krb5_set_default_realm(context, realm_str);
864 krb5_openlog (context, "kpasswdd", &log_facility);
865 krb5_set_warn_dest(context, log_facility);
867 if (port_str != NULL) {
868 struct servent *s = roken_getservbyname (port_str, "udp");
870 if (s != NULL)
871 port = s->s_port;
872 else {
873 char *ptr;
875 port = strtol (port_str, &ptr, 10);
876 if (port == 0 && ptr == port_str)
877 krb5_errx (context, 1, "bad port `%s'", port_str);
878 port = htons(port);
880 } else
881 port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT);
883 ret = krb5_kt_register(context, &hdb_kt_ops);
884 if(ret)
885 krb5_err(context, 1, ret, "krb5_kt_register");
887 ret = krb5_kt_resolve(context, keytab_str, &keytab);
888 if(ret)
889 krb5_err(context, 1, ret, "%s", keytab_str);
891 kadm5_setup_passwd_quality_check (context, check_library, check_function);
893 for (i = 0; i < policy_libraries.num_strings; i++) {
894 ret = kadm5_add_passwd_quality_verifier(context,
895 policy_libraries.strings[i]);
896 if (ret)
897 krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
899 ret = kadm5_add_passwd_quality_verifier(context, NULL);
900 if (ret)
901 krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
904 explicit_addresses.len = 0;
906 if (addresses_str.num_strings) {
907 int i;
909 for (i = 0; i < addresses_str.num_strings; ++i)
910 add_one_address (addresses_str.strings[i], i == 0);
911 free_getarg_strings (&addresses_str);
912 } else {
913 char **foo = krb5_config_get_strings (context, NULL,
914 "kdc", "addresses", NULL);
916 if (foo != NULL) {
917 add_one_address (*foo++, TRUE);
918 while (*foo)
919 add_one_address (*foo++, FALSE);
923 #ifdef HAVE_SIGACTION
925 struct sigaction sa;
927 sa.sa_flags = 0;
928 sa.sa_handler = sigterm;
929 sigemptyset(&sa.sa_mask);
931 sigaction(SIGINT, &sa, NULL);
932 sigaction(SIGTERM, &sa, NULL);
934 #else
935 signal(SIGINT, sigterm);
936 signal(SIGTERM, sigterm);
937 #endif
939 pidfile(NULL);
941 return doit (keytab, port);