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