1 /* 2 * Copyright (c) 1997-2002 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: kpasswdd.c,v 1.54 2002/12/02 14:31:52 joda Exp $"); 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 sig_atomic_t exit_flag = 0; 48 49 static void 50 send_reply (int s, 51 struct sockaddr *sa, 52 int sa_size, 53 krb5_data *ap_rep, 54 krb5_data *rest) 55 { 56 struct msghdr msghdr; 57 struct iovec iov[3]; 58 u_int16_t len, ap_rep_len; 59 u_char header[6]; 60 u_char *p; 61 62 if (ap_rep) 63 ap_rep_len = ap_rep->length; 64 else 65 ap_rep_len = 0; 66 67 len = 6 + ap_rep_len + rest->length; 68 p = header; 69 *p++ = (len >> 8) & 0xFF; 70 *p++ = (len >> 0) & 0xFF; 71 *p++ = 0; 72 *p++ = 1; 73 *p++ = (ap_rep_len >> 8) & 0xFF; 74 *p++ = (ap_rep_len >> 0) & 0xFF; 75 76 memset (&msghdr, 0, sizeof(msghdr)); 77 msghdr.msg_name = (void *)sa; 78 msghdr.msg_namelen = sa_size; 79 msghdr.msg_iov = iov; 80 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); 81 #if 0 82 msghdr.msg_control = NULL; 83 msghdr.msg_controllen = 0; 84 #endif 85 86 iov[0].iov_base = (char *)header; 87 iov[0].iov_len = 6; 88 if (ap_rep_len) { 89 iov[1].iov_base = ap_rep->data; 90 iov[1].iov_len = ap_rep->length; 91 } else { 92 iov[1].iov_base = NULL; 93 iov[1].iov_len = 0; 94 } 95 iov[2].iov_base = rest->data; 96 iov[2].iov_len = rest->length; 97 98 if (sendmsg (s, &msghdr, 0) < 0) 99 krb5_warn (context, errno, "sendmsg"); 100 } 101 102 static int 103 make_result (krb5_data *data, 104 u_int16_t result_code, 105 const char *expl) 106 { 107 krb5_data_zero (data); 108 109 data->length = asprintf ((char **)&data->data, 110 "%c%c%s", 111 (result_code >> 8) & 0xFF, 112 result_code & 0xFF, 113 expl); 114 115 if (data->data == NULL) { 116 krb5_warnx (context, "Out of memory generating error reply"); 117 return 1; 118 } 119 return 0; 120 } 121 122 static void 123 reply_error (krb5_principal server, 124 int s, 125 struct sockaddr *sa, 126 int sa_size, 127 krb5_error_code error_code, 128 u_int16_t result_code, 129 const char *expl) 130 { 131 krb5_error_code ret; 132 krb5_data error_data; 133 krb5_data e_data; 134 135 if (make_result(&e_data, result_code, expl)) 136 return; 137 138 ret = krb5_mk_error (context, 139 error_code, 140 NULL, 141 &e_data, 142 NULL, 143 server, 144 NULL, 145 NULL, 146 &error_data); 147 krb5_data_free (&e_data); 148 if (ret) { 149 krb5_warn (context, ret, "Could not even generate error reply"); 150 return; 151 } 152 send_reply (s, sa, sa_size, NULL, &error_data); 153 krb5_data_free (&error_data); 154 } 155 156 static void 157 reply_priv (krb5_auth_context auth_context, 158 int s, 159 struct sockaddr *sa, 160 int sa_size, 161 u_int16_t result_code, 162 const char *expl) 163 { 164 krb5_error_code ret; 165 krb5_data krb_priv_data; 166 krb5_data ap_rep_data; 167 krb5_data e_data; 168 169 ret = krb5_mk_rep (context, 170 auth_context, 171 &ap_rep_data); 172 if (ret) { 173 krb5_warn (context, ret, "Could not even generate error reply"); 174 return; 175 } 176 177 if (make_result(&e_data, result_code, expl)) 178 return; 179 180 ret = krb5_mk_priv (context, 181 auth_context, 182 &e_data, 183 &krb_priv_data, 184 NULL); 185 krb5_data_free (&e_data); 186 if (ret) { 187 krb5_warn (context, ret, "Could not even generate error reply"); 188 return; 189 } 190 send_reply (s, sa, sa_size, &ap_rep_data, &krb_priv_data); 191 krb5_data_free (&ap_rep_data); 192 krb5_data_free (&krb_priv_data); 193 } 194 195 /* 196 * Change the password for `principal', sending the reply back on `s' 197 * (`sa', `sa_size') to `pwd_data'. 198 */ 199 200 static void 201 change (krb5_auth_context auth_context, 202 krb5_principal principal, 203 int s, 204 struct sockaddr *sa, 205 int sa_size, 206 krb5_data *pwd_data) 207 { 208 krb5_error_code ret; 209 char *client; 210 const char *pwd_reason; 211 kadm5_config_params conf; 212 void *kadm5_handle; 213 char *tmp; 214 215 memset (&conf, 0, sizeof(conf)); 216 217 krb5_unparse_name (context, principal, &client); 218 219 ret = kadm5_init_with_password_ctx(context, 220 client, 221 NULL, 222 KADM5_ADMIN_SERVICE, 223 &conf, 0, 0, 224 &kadm5_handle); 225 if (ret) { 226 free (client); 227 krb5_warn (context, ret, "kadm5_init_with_password_ctx"); 228 reply_priv (auth_context, s, sa, sa_size, 2, 229 "Internal error"); 230 return; 231 } 232 233 krb5_warnx (context, "Changing password for %s", client); 234 free (client); 235 236 pwd_reason = kadm5_check_password_quality (context, principal, pwd_data); 237 if (pwd_reason != NULL ) { 238 krb5_warnx (context, "%s", pwd_reason); 239 reply_priv (auth_context, s, sa, sa_size, 4, pwd_reason); 240 kadm5_destroy (kadm5_handle); 241 return; 242 } 243 244 tmp = malloc (pwd_data->length + 1); 245 if (tmp == NULL) { 246 krb5_warnx (context, "malloc: out of memory"); 247 reply_priv (auth_context, s, sa, sa_size, 2, 248 "Internal error"); 249 goto out; 250 } 251 memcpy (tmp, pwd_data->data, pwd_data->length); 252 tmp[pwd_data->length] = '\0'; 253 254 ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp); 255 memset (tmp, 0, pwd_data->length); 256 free (tmp); 257 if (ret) { 258 krb5_warn (context, ret, "kadm5_s_chpass_principal_cond"); 259 reply_priv (auth_context, s, sa, sa_size, 2, 260 "Internal error"); 261 goto out; 262 } 263 reply_priv (auth_context, s, sa, sa_size, 0, "Password changed"); 264 out: 265 kadm5_destroy (kadm5_handle); 266 } 267 268 static int 269 verify (krb5_auth_context *auth_context, 270 krb5_principal server, 271 krb5_keytab keytab, 272 krb5_ticket **ticket, 273 krb5_data *out_data, 274 int s, 275 struct sockaddr *sa, 276 int sa_size, 277 u_char *msg, 278 size_t len) 279 { 280 krb5_error_code ret; 281 u_int16_t pkt_len, pkt_ver, ap_req_len; 282 krb5_data ap_req_data; 283 krb5_data krb_priv_data; 284 285 pkt_len = (msg[0] << 8) | (msg[1]); 286 pkt_ver = (msg[2] << 8) | (msg[3]); 287 ap_req_len = (msg[4] << 8) | (msg[5]); 288 if (pkt_len != len) { 289 krb5_warnx (context, "Strange len: %ld != %ld", 290 (long)pkt_len, (long)len); 291 reply_error (server, s, sa, sa_size, 0, 1, "Bad request"); 292 return 1; 293 } 294 if (pkt_ver != 0x0001) { 295 krb5_warnx (context, "Bad version (%d)", pkt_ver); 296 reply_error (server, s, sa, sa_size, 0, 1, "Wrong program version"); 297 return 1; 298 } 299 300 ap_req_data.data = msg + 6; 301 ap_req_data.length = ap_req_len; 302 303 ret = krb5_rd_req (context, 304 auth_context, 305 &ap_req_data, 306 server, 307 keytab, 308 NULL, 309 ticket); 310 if (ret) { 311 if(ret == KRB5_KT_NOTFOUND) { 312 char *name; 313 krb5_unparse_name(context, server, &name); 314 krb5_warnx (context, "krb5_rd_req: %s (%s)", 315 krb5_get_err_text(context, ret), name); 316 free(name); 317 } else 318 krb5_warn (context, ret, "krb5_rd_req"); 319 reply_error (server, s, sa, sa_size, ret, 3, "Authentication failed"); 320 return 1; 321 } 322 323 if (!(*ticket)->ticket.flags.initial) { 324 krb5_warnx (context, "initial flag not set"); 325 reply_error (server, s, sa, sa_size, ret, 1, 326 "Bad request"); 327 goto out; 328 } 329 krb_priv_data.data = msg + 6 + ap_req_len; 330 krb_priv_data.length = len - 6 - ap_req_len; 331 332 ret = krb5_rd_priv (context, 333 *auth_context, 334 &krb_priv_data, 335 out_data, 336 NULL); 337 338 if (ret) { 339 krb5_warn (context, ret, "krb5_rd_priv"); 340 reply_error (server, s, sa, sa_size, ret, 3, "Bad request"); 341 goto out; 342 } 343 return 0; 344 out: 345 krb5_free_ticket (context, *ticket); 346 return 1; 347 } 348 349 static void 350 process (krb5_principal server, 351 krb5_keytab keytab, 352 int s, 353 krb5_address *this_addr, 354 struct sockaddr *sa, 355 int sa_size, 356 u_char *msg, 357 int len) 358 { 359 krb5_error_code ret; 360 krb5_auth_context auth_context = NULL; 361 krb5_data out_data; 362 krb5_ticket *ticket; 363 krb5_address other_addr; 364 365 krb5_data_zero (&out_data); 366 367 ret = krb5_auth_con_init (context, &auth_context); 368 if (ret) { 369 krb5_warn (context, ret, "krb5_auth_con_init"); 370 return; 371 } 372 373 krb5_auth_con_setflags (context, auth_context, 374 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 375 376 ret = krb5_sockaddr2address (context, sa, &other_addr); 377 if (ret) { 378 krb5_warn (context, ret, "krb5_sockaddr2address"); 379 goto out; 380 } 381 382 ret = krb5_auth_con_setaddrs (context, 383 auth_context, 384 this_addr, 385 &other_addr); 386 krb5_free_address (context, &other_addr); 387 if (ret) { 388 krb5_warn (context, ret, "krb5_auth_con_setaddr"); 389 goto out; 390 } 391 392 if (verify (&auth_context, server, keytab, &ticket, &out_data, 393 s, sa, sa_size, msg, len) == 0) { 394 change (auth_context, 395 ticket->client, 396 s, 397 sa, sa_size, 398 &out_data); 399 memset (out_data.data, 0, out_data.length); 400 krb5_free_ticket (context, ticket); 401 free (ticket); 402 } 403 404 out: 405 krb5_data_free (&out_data); 406 krb5_auth_con_free (context, auth_context); 407 } 408 409 static int 410 doit (krb5_keytab keytab, int port) 411 { 412 krb5_error_code ret; 413 krb5_principal server; 414 int *sockets; 415 int maxfd; 416 char *realm; 417 krb5_addresses addrs; 418 unsigned n, i; 419 fd_set real_fdset; 420 struct sockaddr_storage __ss; 421 struct sockaddr *sa = (struct sockaddr *)&__ss; 422 423 ret = krb5_get_default_realm (context, &realm); 424 if (ret) 425 krb5_err (context, 1, ret, "krb5_get_default_realm"); 426 427 ret = krb5_build_principal (context, 428 &server, 429 strlen(realm), 430 realm, 431 "kadmin", 432 "changepw", 433 NULL); 434 if (ret) 435 krb5_err (context, 1, ret, "krb5_build_principal"); 436 437 free (realm); 438 439 ret = krb5_get_all_server_addrs (context, &addrs); 440 if (ret) 441 krb5_err (context, 1, ret, "krb5_get_all_server_addrs"); 442 443 n = addrs.len; 444 445 sockets = malloc (n * sizeof(*sockets)); 446 if (sockets == NULL) 447 krb5_errx (context, 1, "out of memory"); 448 maxfd = -1; 449 FD_ZERO(&real_fdset); 450 for (i = 0; i < n; ++i) { 451 int sa_size = sizeof(__ss); 452 453 krb5_addr2sockaddr (context, &addrs.val[i], sa, &sa_size, port); 454 455 sockets[i] = socket (sa->sa_family, SOCK_DGRAM, 0); 456 if (sockets[i] < 0) 457 krb5_err (context, 1, errno, "socket"); 458 if (bind (sockets[i], sa, sa_size) < 0) { 459 char str[128]; 460 size_t len; 461 int save_errno = errno; 462 463 ret = krb5_print_address (&addrs.val[i], str, sizeof(str), &len); 464 if (ret) 465 strlcpy(str, "unknown address", sizeof(str)); 466 krb5_warn (context, save_errno, "bind(%s)", str); 467 continue; 468 } 469 maxfd = max (maxfd, sockets[i]); 470 if (maxfd >= FD_SETSIZE) 471 krb5_errx (context, 1, "fd too large"); 472 FD_SET(sockets[i], &real_fdset); 473 } 474 if (maxfd == -1) 475 krb5_errx (context, 1, "No sockets!"); 476 477 while(exit_flag == 0) { 478 int ret; 479 fd_set fdset = real_fdset; 480 481 ret = select (maxfd + 1, &fdset, NULL, NULL, NULL); 482 if (ret < 0) { 483 if (errno == EINTR) 484 continue; 485 else 486 krb5_err (context, 1, errno, "select"); 487 } 488 for (i = 0; i < n; ++i) 489 if (FD_ISSET(sockets[i], &fdset)) { 490 u_char buf[BUFSIZ]; 491 socklen_t addrlen = sizeof(__ss); 492 493 ret = recvfrom (sockets[i], buf, sizeof(buf), 0, 494 sa, &addrlen); 495 if (ret < 0) { 496 if(errno == EINTR) 497 break; 498 else 499 krb5_err (context, 1, errno, "recvfrom"); 500 } 501 502 process (server, keytab, sockets[i], 503 &addrs.val[i], 504 sa, addrlen, 505 buf, ret); 506 } 507 } 508 krb5_free_addresses (context, &addrs); 509 krb5_free_principal (context, server); 510 krb5_free_context (context); 511 return 0; 512 } 513 514 static RETSIGTYPE 515 sigterm(int sig) 516 { 517 exit_flag = 1; 518 } 519 520 const char *check_library = NULL; 521 const char *check_function = NULL; 522 char *keytab_str = "HDB:"; 523 char *realm_str; 524 int version_flag; 525 int help_flag; 526 char *port_str; 527 528 struct getargs args[] = { 529 #ifdef HAVE_DLOPEN 530 { "check-library", 0, arg_string, &check_library, 531 "library to load password check function from", "library" }, 532 { "check-function", 0, arg_string, &check_function, 533 "password check function to load", "function" }, 534 #endif 535 { "keytab", 'k', arg_string, &keytab_str, 536 "keytab to get authentication key from", "kspec" }, 537 { "realm", 'r', arg_string, &realm_str, "default realm", "realm" }, 538 { "port", 'p', arg_string, &port_str, "port" }, 539 { "version", 0, arg_flag, &version_flag }, 540 { "help", 0, arg_flag, &help_flag } 541 }; 542 int num_args = sizeof(args) / sizeof(args[0]); 543 544 int 545 main (int argc, char **argv) 546 { 547 int optind; 548 krb5_keytab keytab; 549 krb5_error_code ret; 550 int port; 551 552 optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL); 553 554 if(help_flag) 555 krb5_std_usage(0, args, num_args); 556 if(version_flag) { 557 print_version(NULL); 558 exit(0); 559 } 560 561 if(realm_str) 562 krb5_set_default_realm(context, realm_str); 563 564 krb5_openlog (context, "kpasswdd", &log_facility); 565 krb5_set_warn_dest(context, log_facility); 566 567 if (port_str != NULL) { 568 struct servent *s = roken_getservbyname (port_str, "udp"); 569 570 if (s != NULL) 571 port = s->s_port; 572 else { 573 char *ptr; 574 575 port = strtol (port_str, &ptr, 10); 576 if (port == 0 && ptr == port_str) 577 krb5_errx (context, 1, "bad port `%s'", port_str); 578 port = htons(port); 579 } 580 } else 581 port = krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT); 582 583 ret = krb5_kt_register(context, &hdb_kt_ops); 584 if(ret) 585 krb5_err(context, 1, ret, "krb5_kt_register"); 586 587 ret = krb5_kt_resolve(context, keytab_str, &keytab); 588 if(ret) 589 krb5_err(context, 1, ret, "%s", keytab_str); 590 591 kadm5_setup_passwd_quality_check (context, check_library, check_function); 592 593 #ifdef HAVE_SIGACTION 594 { 595 struct sigaction sa; 596 597 sa.sa_flags = 0; 598 sa.sa_handler = sigterm; 599 sigemptyset(&sa.sa_mask); 600 601 sigaction(SIGINT, &sa, NULL); 602 sigaction(SIGTERM, &sa, NULL); 603 } 604 #else 605 signal(SIGINT, sigterm); 606 signal(SIGTERM, sigterm); 607 #endif 608 609 pidfile(NULL); 610 611 return doit (keytab, port); 612 } 613