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