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 void terminated(void *) __attribute__((__noreturn__)); 45static void usage(int) __attribute__((__noreturn__)); 46static void doit(int) __attribute__((__noreturn__)); 47 48static krb5_context context; 49static krb5_log_facility *log_facility; 50 51static struct getarg_strings addresses_str; 52krb5_addresses explicit_addresses; 53 54static krb5_keytab global_keytab = NULL; 55 56static void 57add_one_address (const char *str, int first) 58{ 59 krb5_error_code ret; 60 krb5_addresses tmp; 61 62 ret = krb5_parse_address (context, str, &tmp); 63 if (ret) 64 krb5_err (context, 1, ret, "parse_address `%s'", str); 65 if (first) 66 krb5_copy_addresses(context, &tmp, &explicit_addresses); 67 else 68 krb5_append_addresses(context, &explicit_addresses, &tmp); 69 krb5_free_addresses (context, &tmp); 70} 71 72static void 73send_reply(krb5_data *ap_rep, krb5_data *rest, 74 krb5_data *out_data) 75{ 76 uint16_t ap_rep_len; 77 u_char *p; 78 79 if (ap_rep) 80 ap_rep_len = ap_rep->length; 81 else 82 ap_rep_len = 0; 83 84 if (krb5_data_alloc(out_data, 6 + ap_rep_len + rest->length)) 85 return; 86 87 p = out_data->data; 88 *p++ = (out_data->length >> 8) & 0xFF; 89 *p++ = (out_data->length >> 0) & 0xFF; 90 *p++ = 0; 91 *p++ = 1; 92 *p++ = (ap_rep_len >> 8) & 0xFF; 93 *p++ = (ap_rep_len >> 0) & 0xFF; 94 95 if (ap_rep_len) { 96 memcpy(p, ap_rep->data, ap_rep->length); 97 p += ap_rep->length; 98 } 99 100 memcpy(p, rest->data, rest->length); 101} 102 103static int 104make_result (krb5_data *data, 105 uint16_t result_code, 106 const char *expl) 107{ 108 char *str; 109 krb5_data_zero (data); 110 111 data->length = asprintf (&str, 112 "%c%c%s", 113 (result_code >> 8) & 0xFF, 114 result_code & 0xFF, 115 expl); 116 117 if (str == NULL) { 118 krb5_warnx (context, "Out of memory generating error reply"); 119 return 1; 120 } 121 data->data = str; 122 return 0; 123} 124 125static void 126reply_error(krb5_realm realm, 127 krb5_error_code error_code, 128 uint16_t result_code, 129 const char *expl, 130 krb5_data *out_data) 131{ 132 krb5_error_code ret; 133 krb5_data error_data; 134 krb5_data e_data; 135 krb5_principal server = NULL; 136 137 if (make_result(&e_data, result_code, expl)) 138 return; 139 140 if (realm) { 141 ret = krb5_make_principal (context, &server, realm, 142 "kadmin", "changepw", NULL); 143 if (ret) { 144 krb5_data_free (&e_data); 145 return; 146 } 147 } 148 149 ret = krb5_mk_error (context, 150 error_code, 151 NULL, 152 &e_data, 153 NULL, 154 server, 155 NULL, 156 NULL, 157 &error_data); 158 if (server) 159 krb5_free_principal(context, server); 160 krb5_data_free (&e_data); 161 if (ret) { 162 krb5_warn (context, ret, "Could not even generate error reply"); 163 return; 164 } 165 send_reply(NULL, &error_data, out_data); 166 krb5_data_free (&error_data); 167} 168 169static void 170reply_priv(krb5_auth_context auth_context, 171 uint16_t result_code, 172 const char *expl, 173 krb5_data *out_data) 174{ 175 krb5_error_code ret; 176 krb5_data krb_priv_data; 177 krb5_data ap_rep_data; 178 krb5_data e_data; 179 180 ret = krb5_mk_rep (context, 181 auth_context, 182 &ap_rep_data); 183 if (ret) { 184 krb5_warn (context, ret, "Could not even generate error reply"); 185 return; 186 } 187 188 if (make_result(&e_data, result_code, expl)) 189 return; 190 191 ret = krb5_mk_priv (context, 192 auth_context, 193 &e_data, 194 &krb_priv_data, 195 NULL); 196 krb5_data_free (&e_data); 197 if (ret) { 198 krb5_warn (context, ret, "Could not even generate error reply"); 199 return; 200 } 201 send_reply(&ap_rep_data, &krb_priv_data, out_data); 202 krb5_data_free (&ap_rep_data); 203 krb5_data_free (&krb_priv_data); 204} 205 206/* 207 * Change the password for `principal', sending the reply back on `s' 208 * (`sa', `sa_size') to `pwd_data'. 209 */ 210 211static void 212change(krb5_auth_context auth_context, 213 krb5_principal admin_principal, 214 uint16_t version, 215 krb5_data *in_data, 216 krb5_data *out_data) 217{ 218 krb5_error_code ret; 219 char *client = NULL, *admin = NULL; 220 const char *pwd_reason; 221 kadm5_config_params conf; 222 void *kadm5_handle = NULL; 223 krb5_principal principal = NULL; 224 krb5_data *pwd_data = NULL; 225 char *tmp; 226 ChangePasswdDataMS chpw; 227 228 memset (&conf, 0, sizeof(conf)); 229 memset(&chpw, 0, sizeof(chpw)); 230 231 if (version == KRB5_KPASSWD_VERS_CHANGEPW) { 232 ret = krb5_copy_data(context, in_data, &pwd_data); 233 if (ret) { 234 krb5_warn (context, ret, "krb5_copy_data"); 235 reply_priv(auth_context, KRB5_KPASSWD_MALFORMED, 236 "out out memory copying password", out_data); 237 return; 238 } 239 principal = admin_principal; 240 } else if (version == KRB5_KPASSWD_VERS_SETPW) { 241 size_t len; 242 243 ret = decode_ChangePasswdDataMS(in_data->data, in_data->length, 244 &chpw, &len); 245 if (ret) { 246 krb5_warn (context, ret, "decode_ChangePasswdDataMS"); 247 reply_priv(auth_context, KRB5_KPASSWD_MALFORMED, 248 "malformed ChangePasswdData", out_data); 249 return; 250 } 251 252 253 ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data); 254 if (ret) { 255 krb5_warn (context, ret, "krb5_copy_data"); 256 reply_priv(auth_context, KRB5_KPASSWD_MALFORMED, 257 "out out memory copying password", out_data); 258 goto out; 259 } 260 261 if (chpw.targname == NULL && chpw.targrealm != NULL) { 262 krb5_warn (context, ret, "kadm5_init_with_password_ctx"); 263 reply_priv(auth_context, KRB5_KPASSWD_MALFORMED, 264 "targrealm but not targname", out_data); 265 goto out; 266 } 267 268 if (chpw.targname) { 269 krb5_principal_data princ; 270 271 princ.name = *chpw.targname; 272 princ.realm = *chpw.targrealm; 273 if (princ.realm == NULL) { 274 ret = krb5_get_default_realm(context, &princ.realm); 275 276 if (ret) { 277 krb5_warnx (context, 278 "kadm5_init_with_password_ctx: " 279 "failed to allocate realm"); 280 reply_priv(auth_context, KRB5_KPASSWD_SOFTERROR, 281 "failed to allocate realm", out_data); 282 goto out; 283 } 284 } 285 ret = krb5_copy_principal(context, &princ, &principal); 286 if (*chpw.targrealm == NULL) 287 free(princ.realm); 288 if (ret) { 289 krb5_warn(context, ret, "krb5_copy_principal"); 290 reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, 291 "failed to allocate principal", out_data); 292 goto out; 293 } 294 } else 295 principal = admin_principal; 296 } else { 297 krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto"); 298 reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, 299 "Unknown protocol used", out_data); 300 return; 301 } 302 303 ret = krb5_unparse_name (context, admin_principal, &admin); 304 if (ret) { 305 krb5_warn (context, ret, "unparse_name failed"); 306 reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, "out of memory error", out_data); 307 goto out; 308 } 309 310 conf.realm = principal->realm; 311 conf.mask |= KADM5_CONFIG_REALM; 312 313 ret = kadm5_s_init_with_password_ctx(context, 314 admin, 315 NULL, 316 KADM5_ADMIN_SERVICE, 317 &conf, 0, 0, 318 &kadm5_handle); 319 if (ret) { 320 krb5_warn (context, ret, "kadm5_init_with_password_ctx"); 321 reply_priv(auth_context, 2, "Internal error", out_data); 322 goto out; 323 } 324 325 ret = krb5_unparse_name(context, principal, &client); 326 if (ret) { 327 krb5_warn (context, ret, "unparse_name failed"); 328 reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, "out of memory error", out_data); 329 goto out; 330 } 331 332 /* 333 * Check password quality if not changing as administrator 334 */ 335 336 if (krb5_principal_compare(context, admin_principal, principal) == TRUE) { 337 338 pwd_reason = kadm5_check_password_quality (context, principal, 339 pwd_data); 340 if (pwd_reason != NULL ) { 341 krb5_warnx (context, 342 "%s didn't pass password quality check with error: %s", 343 client, pwd_reason); 344 reply_priv(auth_context, KRB5_KPASSWD_SOFTERROR, pwd_reason, out_data); 345 goto out; 346 } 347 krb5_warnx (context, "Changing password for %s", client); 348 } else { 349 ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW, 350 principal); 351 if (ret) { 352 krb5_warn (context, ret, 353 "Check ACL failed for %s for changing %s password", 354 admin, client); 355 reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, "permission denied", out_data); 356 goto out; 357 } 358 krb5_warnx (context, "%s is changing password for %s", admin, client); 359 } 360 361 ret = krb5_data_realloc(pwd_data, pwd_data->length + 1); 362 if (ret) { 363 krb5_warn (context, ret, "malloc: out of memory"); 364 reply_priv(auth_context, KRB5_KPASSWD_HARDERROR, 365 "Internal error", out_data); 366 goto out; 367 } 368 tmp = pwd_data->data; 369 tmp[pwd_data->length - 1] = '\0'; 370 371 ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, 1, tmp, 0, NULL); 372 krb5_free_data (context, pwd_data); 373 pwd_data = NULL; 374 if (ret) { 375 const char *str = krb5_get_error_message(context, ret); 376 krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str); 377 reply_priv(auth_context, KRB5_KPASSWD_SOFTERROR, 378 str ? str : "Internal error", out_data); 379 krb5_free_error_message(context, str); 380 goto out; 381 } 382 reply_priv(auth_context, KRB5_KPASSWD_SUCCESS, 383 "Password changed", out_data); 384out: 385 free_ChangePasswdDataMS(&chpw); 386 if (principal != admin_principal) 387 krb5_free_principal(context, principal); 388 if (admin) 389 free(admin); 390 if (client) 391 free(client); 392 if (pwd_data) 393 krb5_free_data(context, pwd_data); 394 if (kadm5_handle) 395 kadm5_s_destroy (kadm5_handle); 396} 397 398static int 399verify(krb5_auth_context *auth_context, 400 krb5_realm *realms, 401 krb5_keytab keytab, 402 krb5_ticket **ticket, 403 uint16_t *version, 404 int s, 405 struct sockaddr *sa, 406 int sa_size, 407 u_char *msg, 408 size_t len, 409 krb5_data *out_data) 410{ 411 krb5_error_code ret; 412 uint16_t pkt_len, pkt_ver, ap_req_len; 413 krb5_data ap_req_data; 414 krb5_data krb_priv_data; 415 krb5_realm *r; 416 417 /* 418 * Only send an error reply if the request passes basic length 419 * verification. Otherwise, kpasswdd would reply to every UDP packet, 420 * allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP 421 * packet with a source address of another UDP service that also replies 422 * to every packet. 423 * 424 * Also suppress the error reply if ap_req_len is 0, which indicates 425 * either an invalid request or an error packet. An error packet may be 426 * the result of a ping-pong attacker pointing us at another kpasswdd. 427 */ 428 pkt_len = (msg[0] << 8) | (msg[1]); 429 pkt_ver = (msg[2] << 8) | (msg[3]); 430 ap_req_len = (msg[4] << 8) | (msg[5]); 431 if (pkt_len != len) { 432 krb5_warnx (context, "Strange len: %ld != %ld", 433 (long)pkt_len, (long)len); 434 return 1; 435 } 436 if (ap_req_len == 0) { 437 krb5_warnx (context, "Request is error packet (ap_req_len == 0)"); 438 return 1; 439 } 440 if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW && 441 pkt_ver != KRB5_KPASSWD_VERS_SETPW) { 442 krb5_warnx (context, "Bad version (%d)", pkt_ver); 443 reply_error (NULL, 0, 1, "Wrong program version", out_data); 444 return 1; 445 } 446 *version = pkt_ver; 447 448 ap_req_data.data = msg + 6; 449 ap_req_data.length = ap_req_len; 450 451 ret = krb5_rd_req (context, 452 auth_context, 453 &ap_req_data, 454 NULL, 455 keytab, 456 NULL, 457 ticket); 458 if (ret) { 459 krb5_warn (context, ret, "krb5_rd_req"); 460 reply_error (NULL, ret, 3, "Authentication failed", out_data); 461 return 1; 462 } 463 464 /* verify realm and principal */ 465 for (r = realms; *r != NULL; r++) { 466 krb5_principal principal; 467 krb5_boolean same; 468 469 ret = krb5_make_principal (context, 470 &principal, 471 *r, 472 "kadmin", 473 "changepw", 474 NULL); 475 if (ret) 476 krb5_err (context, 1, ret, "krb5_make_principal"); 477 478 same = krb5_principal_compare(context, principal, (*ticket)->server); 479 krb5_free_principal(context, principal); 480 if (same == TRUE) 481 break; 482 } 483 if (*r == NULL) { 484 char *str; 485 krb5_unparse_name(context, (*ticket)->server, &str); 486 krb5_warnx (context, "client used not valid principal %s", str); 487 free(str); 488 reply_error (NULL, ret, 1, 489 "Bad request", out_data); 490 goto out; 491 } 492 493 if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) { 494 krb5_warnx (context, "server realm (%s) not same a client realm (%s)", 495 (*ticket)->server->realm, (*ticket)->client->realm); 496 reply_error ((*ticket)->server->realm, ret, 1, 497 "Bad request", out_data); 498 goto out; 499 } 500 501 if (!(*ticket)->ticket.flags.initial) { 502 krb5_warnx (context, "initial flag not set"); 503 reply_error ((*ticket)->server->realm, ret, 1, 504 "Bad request", out_data); 505 goto out; 506 } 507 krb_priv_data.data = msg + 6 + ap_req_len; 508 krb_priv_data.length = len - 6 - ap_req_len; 509 510 ret = krb5_rd_priv (context, 511 *auth_context, 512 &krb_priv_data, 513 out_data, 514 NULL); 515 516 if (ret) { 517 krb5_warn (context, ret, "krb5_rd_priv"); 518 reply_error ((*ticket)->server->realm, ret, 3, 519 "Bad request", out_data); 520 goto out; 521 } 522 return 0; 523out: 524 krb5_free_ticket (context, *ticket); 525 ticket = NULL; 526 return 1; 527} 528 529static krb5_error_code 530process(int s, 531 struct sockaddr *server, 532 int server_size, 533 struct sockaddr *client, 534 int client_size, 535 u_char *msg, 536 size_t len, 537 heim_idata *out_data) 538{ 539 krb5_error_code ret; 540 krb5_auth_context auth_context = NULL; 541 krb5_ticket *ticket; 542 uint16_t version; 543 krb5_realm *realms = NULL; 544 krb5_data clear_data; 545 krb5_address client_addr, server_addr; 546 547 krb5_data_zero(&clear_data); 548 memset(&client_addr, 0, sizeof(client_addr)); 549 memset(&server_addr, 0, sizeof(server_addr)); 550 551 ret = krb5_sockaddr2address(context, client, &client_addr); 552 if (ret) { 553 krb5_warn(context, ret, "krb5_sockaddr2address"); 554 goto out; 555 } 556 ret = krb5_sockaddr2address(context, server, &server_addr); 557 if (ret) { 558 krb5_warn(context, ret, "krb5_sockaddr2address"); 559 goto out; 560 } 561 562 { 563 char cstr[200]; 564 size_t ss; 565 krb5_print_address(&client_addr, cstr, sizeof(cstr), &ss); 566 krb5_warnx(context, "request from client: %s", cstr); 567 } 568 569 ret = krb5_get_default_realms(context, &realms); 570 if (ret) { 571 krb5_warn(context, ret, "krb5_get_default_realms"); 572 return ret; 573 } 574 575 ret = krb5_auth_con_init(context, &auth_context); 576 if (ret) { 577 krb5_warn (context, ret, "krb5_auth_con_init"); 578 goto out; 579 } 580 581 krb5_auth_con_setflags(context, auth_context, 582 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 583 584 if (verify(&auth_context, realms, global_keytab, &ticket, 585 &version, s, client, client_size, msg, len, &clear_data) == 0) 586 { 587 ret = krb5_auth_con_setaddrs(context, 588 auth_context, 589 &server_addr, 590 &client_addr); 591 if (ret) { 592 krb5_warn(context, ret, "krb5_sockaddr2address"); 593 goto out; 594 } 595 596 change(auth_context, ticket->client, 597 version, &clear_data, out_data); 598 599 krb5_free_ticket(context, ticket); 600 } 601 602out: 603 krb5_free_address(context, &client_addr); 604 krb5_free_address(context, &server_addr); 605 if (clear_data.data) 606 memset(clear_data.data, 0, clear_data.length); 607 krb5_data_free(&clear_data); 608 609 krb5_auth_con_free(context, auth_context); 610 if (realms) 611 krb5_free_host_realm(context, realms); 612 return ret; 613} 614 615struct data { 616 rk_socket_t s; 617 struct sockaddr *sa; 618 struct sockaddr_storage __ss; 619 krb5_socklen_t sa_size; 620 heim_sipc service; 621 struct data *next; 622}; 623 624/* 625 * Linked list to not leak memory 626 */ 627static struct data *head_data = NULL; 628 629/* 630 * 631 */ 632 633static void 634passwd_service(void *ctx, const heim_idata *req, 635 const heim_icred cred, 636 heim_ipc_complete complete, 637 heim_sipc_call cctx) 638{ 639 struct data *data = ctx; 640 heim_idata out_data; 641 krb5_error_code ret; 642 struct sockaddr *client, *server; 643 krb5_socklen_t client_size, server_size; 644 645 client = heim_ipc_cred_get_client_address(cred, &client_size); 646 heim_assert(client != NULL, "no address from client"); 647 648 server = heim_ipc_cred_get_server_address(cred, &server_size); 649 heim_assert(server != NULL, "no address from server"); 650 651 krb5_data_zero(&out_data); 652 653 ret = process(data->s, 654 server, server_size, 655 client, client_size, 656 req->data, req->length, &out_data); 657 658 complete(cctx, ret, &out_data); 659 660 memset (out_data.data, 0, out_data.length); 661 krb5_data_free(&out_data); 662} 663 664 665 666static void 667listen_on(krb5_address *addr, int type, int port) 668{ 669 krb5_error_code ret; 670 struct data *data; 671 672 data = calloc(1, sizeof(*data)); 673 if (data == NULL) 674 krb5_errx(context, 1, "out of memory"); 675 676 data->sa = (struct sockaddr *)&data->__ss; 677 data->sa_size = sizeof(data->__ss); 678 679 krb5_addr2sockaddr(context, addr, data->sa, &data->sa_size, port); 680 681 data->s = socket(data->sa->sa_family, type, 0); 682 if (data->s < 0) { 683 krb5_warn(context, errno, "socket"); 684 free(data); 685 return; 686 } 687 socket_set_nopipe(data->s, 1); 688 socket_set_reuseaddr(data->s, 1); 689#ifdef HAVE_IPV6 690 if (data->sa->sa_family == AF_INET6) 691 socket_set_ipv6only(data->s, 1); 692#endif 693 694 socket_set_ipv6only(data->s, 1); 695 socket_set_reuseaddr(data->s, 1); 696 697 if (bind(data->s, data->sa, data->sa_size) < 0) { 698 char str[128]; 699 size_t len; 700 int save_errno = errno; 701 702 ret = krb5_print_address (addr, str, sizeof(str), &len); 703 if (ret) 704 strlcpy(str, "unknown address", sizeof(str)); 705 krb5_warn (context, save_errno, "bind(%s/%d)", str, ntohs(port)); 706 rk_closesocket(data->s); 707 free(data); 708 return; 709 } 710 711 if(type == SOCK_STREAM) { 712 if (listen(data->s, SOMAXCONN) < 0){ 713 char a_str[256]; 714 size_t len; 715 716 krb5_print_address(addr, a_str, sizeof(a_str), &len); 717 krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port)); 718 rk_closesocket(data->s); 719 free(data); 720 return; 721 } 722 723 ret = heim_sipc_stream_listener(data->s, 724 HEIM_SIPC_TYPE_UINT32 | HEIM_SIPC_TYPE_ONE_REQUEST, 725 passwd_service, data, &data->service); 726 if (ret) 727 errx(1, "heim_sipc_stream_listener: %d", ret); 728 } else { 729 ret = heim_sipc_service_dgram(data->s, 0, 730 passwd_service, data, &data->service); 731 if (ret) 732 errx(1, "heim_sipc_service_dgram: %d", ret); 733 } 734 735 data->next = head_data; 736 head_data = data; 737} 738 739static void 740doit(int port) 741{ 742 krb5_error_code ret; 743 krb5_addresses addrs; 744 unsigned i; 745 746 if (explicit_addresses.len) { 747 addrs = explicit_addresses; 748 } else { 749#if defined(IPV6_PKTINFO) && defined(IP_PKTINFO) 750 ret = krb5_get_all_any_addrs(context, &addrs); 751#else 752 ret = krb5_get_all_server_addrs(context, &addrs); 753#endif 754 if (ret) 755 krb5_err (context, 1, ret, "krb5_get_all_server_addrs"); 756 } 757 758 for (i = 0; i < addrs.len; ++i) { 759 listen_on(&addrs.val[i], SOCK_STREAM, port); 760 listen_on(&addrs.val[i], SOCK_DGRAM, port); 761 } 762 763 heim_ipc_main(); 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 doit(port); 917} 918