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