1/* $NetBSD: kpasswdd.c,v 1.2 2011/04/14 18:18:16 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.2 2011/04/14 18:18:16 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