1 /* $NetBSD: changepw.c,v 1.3 2019/12/15 22:50:50 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 "krb5_locl.h" 37 38 #undef __attribute__ 39 #define __attribute__(X) 40 41 42 static void 43 str2data (krb5_data *d, 44 const char *fmt, 45 ...) __attribute__ ((__format__ (__printf__, 2, 3))); 46 47 static void 48 str2data (krb5_data *d, 49 const char *fmt, 50 ...) 51 { 52 va_list args; 53 char *str; 54 55 va_start(args, fmt); 56 d->length = vasprintf (&str, fmt, args); 57 va_end(args); 58 d->data = str; 59 } 60 61 /* 62 * Change password protocol defined by 63 * draft-ietf-cat-kerb-chg-password-02.txt 64 * 65 * Share the response part of the protocol with MS set password 66 * (RFC3244) 67 */ 68 69 static krb5_error_code 70 chgpw_send_request (krb5_context context, 71 krb5_auth_context *auth_context, 72 krb5_creds *creds, 73 krb5_principal targprinc, 74 int is_stream, 75 rk_socket_t sock, 76 const char *passwd, 77 const char *host) 78 { 79 krb5_error_code ret; 80 krb5_data ap_req_data; 81 krb5_data krb_priv_data; 82 krb5_data passwd_data; 83 size_t len; 84 u_char header[6]; 85 struct iovec iov[3]; 86 struct msghdr msghdr; 87 88 if (is_stream) 89 return KRB5_KPASSWD_MALFORMED; 90 91 if (targprinc && 92 krb5_principal_compare(context, creds->client, targprinc) != TRUE) 93 return KRB5_KPASSWD_MALFORMED; 94 95 krb5_data_zero (&ap_req_data); 96 97 ret = krb5_mk_req_extended (context, 98 auth_context, 99 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 100 NULL, /* in_data */ 101 creds, 102 &ap_req_data); 103 if (ret) 104 return ret; 105 106 passwd_data.data = rk_UNCONST(passwd); 107 passwd_data.length = strlen(passwd); 108 109 krb5_data_zero (&krb_priv_data); 110 111 ret = krb5_mk_priv (context, 112 *auth_context, 113 &passwd_data, 114 &krb_priv_data, 115 NULL); 116 if (ret) 117 goto out2; 118 119 len = 6 + ap_req_data.length + krb_priv_data.length; 120 header[0] = (len >> 8) & 0xFF; 121 header[1] = (len >> 0) & 0xFF; 122 header[2] = 0; 123 header[3] = 1; 124 header[4] = (ap_req_data.length >> 8) & 0xFF; 125 header[5] = (ap_req_data.length >> 0) & 0xFF; 126 127 memset(&msghdr, 0, sizeof(msghdr)); 128 msghdr.msg_name = NULL; 129 msghdr.msg_namelen = 0; 130 msghdr.msg_iov = iov; 131 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); 132 #if 0 133 msghdr.msg_control = NULL; 134 msghdr.msg_controllen = 0; 135 #endif 136 137 iov[0].iov_base = (void*)header; 138 iov[0].iov_len = 6; 139 iov[1].iov_base = ap_req_data.data; 140 iov[1].iov_len = ap_req_data.length; 141 iov[2].iov_base = krb_priv_data.data; 142 iov[2].iov_len = krb_priv_data.length; 143 144 if (rk_IS_SOCKET_ERROR( sendmsg (sock, &msghdr, 0) )) { 145 ret = rk_SOCK_ERRNO; 146 krb5_set_error_message(context, ret, "sendmsg %s: %s", 147 host, strerror(ret)); 148 } 149 150 krb5_data_free (&krb_priv_data); 151 out2: 152 krb5_data_free (&ap_req_data); 153 return ret; 154 } 155 156 /* 157 * Set password protocol as defined by RFC3244 -- 158 * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols 159 */ 160 161 static krb5_error_code 162 setpw_send_request (krb5_context context, 163 krb5_auth_context *auth_context, 164 krb5_creds *creds, 165 krb5_principal targprinc, 166 int is_stream, 167 rk_socket_t sock, 168 const char *passwd, 169 const char *host) 170 { 171 krb5_error_code ret; 172 krb5_data ap_req_data; 173 krb5_data krb_priv_data; 174 krb5_data pwd_data; 175 ChangePasswdDataMS chpw; 176 size_t len = 0; 177 u_char header[4 + 6]; 178 u_char *p; 179 struct iovec iov[3]; 180 struct msghdr msghdr; 181 182 krb5_data_zero (&ap_req_data); 183 184 ret = krb5_mk_req_extended (context, 185 auth_context, 186 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 187 NULL, /* in_data */ 188 creds, 189 &ap_req_data); 190 if (ret) 191 return ret; 192 193 chpw.newpasswd.length = strlen(passwd); 194 chpw.newpasswd.data = rk_UNCONST(passwd); 195 if (targprinc) { 196 chpw.targname = &targprinc->name; 197 chpw.targrealm = &targprinc->realm; 198 } else { 199 chpw.targname = NULL; 200 chpw.targrealm = NULL; 201 } 202 203 ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, 204 &chpw, &len, ret); 205 if (ret) { 206 krb5_data_free (&ap_req_data); 207 return ret; 208 } 209 210 if(pwd_data.length != len) 211 krb5_abortx(context, "internal error in ASN.1 encoder"); 212 213 ret = krb5_mk_priv (context, 214 *auth_context, 215 &pwd_data, 216 &krb_priv_data, 217 NULL); 218 if (ret) 219 goto out2; 220 221 len = 6 + ap_req_data.length + krb_priv_data.length; 222 p = header; 223 if (is_stream) { 224 _krb5_put_int(p, len, 4); 225 p += 4; 226 } 227 *p++ = (len >> 8) & 0xFF; 228 *p++ = (len >> 0) & 0xFF; 229 *p++ = 0xff; 230 *p++ = 0x80; 231 *p++ = (ap_req_data.length >> 8) & 0xFF; 232 *p = (ap_req_data.length >> 0) & 0xFF; 233 234 memset(&msghdr, 0, sizeof(msghdr)); 235 msghdr.msg_name = NULL; 236 msghdr.msg_namelen = 0; 237 msghdr.msg_iov = iov; 238 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); 239 #if 0 240 msghdr.msg_control = NULL; 241 msghdr.msg_controllen = 0; 242 #endif 243 244 iov[0].iov_base = (void*)header; 245 if (is_stream) 246 iov[0].iov_len = 10; 247 else 248 iov[0].iov_len = 6; 249 iov[1].iov_base = ap_req_data.data; 250 iov[1].iov_len = ap_req_data.length; 251 iov[2].iov_base = krb_priv_data.data; 252 iov[2].iov_len = krb_priv_data.length; 253 254 if (rk_IS_SOCKET_ERROR( sendmsg (sock, &msghdr, 0) )) { 255 ret = rk_SOCK_ERRNO; 256 krb5_set_error_message(context, ret, "sendmsg %s: %s", 257 host, strerror(ret)); 258 } 259 260 krb5_data_free (&krb_priv_data); 261 out2: 262 krb5_data_free (&ap_req_data); 263 krb5_data_free (&pwd_data); 264 return ret; 265 } 266 267 static krb5_error_code 268 process_reply (krb5_context context, 269 krb5_auth_context auth_context, 270 int is_stream, 271 rk_socket_t sock, 272 int *result_code, 273 krb5_data *result_code_string, 274 krb5_data *result_string, 275 const char *host) 276 { 277 krb5_error_code ret; 278 u_char reply[1024 * 3]; 279 size_t len; 280 uint16_t pkt_len, pkt_ver; 281 krb5_data ap_rep_data; 282 int save_errno; 283 284 len = 0; 285 if (is_stream) { 286 while (len < sizeof(reply)) { 287 unsigned long size; 288 289 ret = recvfrom (sock, reply + len, sizeof(reply) - len, 290 0, NULL, NULL); 291 if (rk_IS_SOCKET_ERROR(ret)) { 292 save_errno = rk_SOCK_ERRNO; 293 krb5_set_error_message(context, save_errno, 294 "recvfrom %s: %s", 295 host, strerror(save_errno)); 296 return save_errno; 297 } else if (ret == 0) { 298 krb5_set_error_message(context, 1,"recvfrom timeout %s", host); 299 return 1; 300 } 301 len += ret; 302 if (len < 4) 303 continue; 304 _krb5_get_int(reply, &size, 4); 305 if (size + 4 < len) 306 continue; 307 if (sizeof(reply) - 4 < size) { 308 krb5_set_error_message(context, ERANGE, "size from server too large %s", host); 309 return ERANGE; 310 } 311 memmove(reply, reply + 4, size); 312 len = size; 313 break; 314 } 315 if (len == sizeof(reply)) { 316 krb5_set_error_message(context, ENOMEM, 317 N_("Message too large from %s", "host"), 318 host); 319 return ENOMEM; 320 } 321 } else { 322 ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); 323 if (rk_IS_SOCKET_ERROR(ret)) { 324 save_errno = rk_SOCK_ERRNO; 325 krb5_set_error_message(context, save_errno, 326 "recvfrom %s: %s", 327 host, strerror(save_errno)); 328 return save_errno; 329 } 330 len = ret; 331 } 332 333 if (len < 6) { 334 str2data (result_string, "server %s sent to too short message " 335 "(%llu bytes)", host, (unsigned long long)len); 336 *result_code = KRB5_KPASSWD_MALFORMED; 337 return 0; 338 } 339 340 pkt_len = (reply[0] << 8) | (reply[1]); 341 pkt_ver = (reply[2] << 8) | (reply[3]); 342 343 if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) { 344 KRB_ERROR error; 345 size_t size; 346 u_char *p; 347 348 memset(&error, 0, sizeof(error)); 349 350 ret = decode_KRB_ERROR(reply, len, &error, &size); 351 if (ret) 352 return ret; 353 354 if (error.e_data->length < 2) { 355 str2data(result_string, "server %s sent too short " 356 "e_data to print anything usable", host); 357 free_KRB_ERROR(&error); 358 *result_code = KRB5_KPASSWD_MALFORMED; 359 return 0; 360 } 361 362 p = error.e_data->data; 363 *result_code = (p[0] << 8) | p[1]; 364 if (error.e_data->length == 2) 365 str2data(result_string, "server only sent error code"); 366 else 367 krb5_data_copy (result_string, 368 p + 2, 369 error.e_data->length - 2); 370 free_KRB_ERROR(&error); 371 return 0; 372 } 373 374 if (pkt_len != len) { 375 str2data (result_string, "client: wrong len in reply"); 376 *result_code = KRB5_KPASSWD_MALFORMED; 377 return 0; 378 } 379 if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { 380 str2data (result_string, 381 "client: wrong version number (%d)", pkt_ver); 382 *result_code = KRB5_KPASSWD_MALFORMED; 383 return 0; 384 } 385 386 ap_rep_data.data = reply + 6; 387 ap_rep_data.length = (reply[4] << 8) | (reply[5]); 388 389 if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) { 390 str2data (result_string, "client: wrong AP len in reply"); 391 *result_code = KRB5_KPASSWD_MALFORMED; 392 return 0; 393 } 394 395 if (ap_rep_data.length) { 396 krb5_ap_rep_enc_part *ap_rep; 397 krb5_data priv_data; 398 u_char *p; 399 400 priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; 401 priv_data.length = len - ap_rep_data.length - 6; 402 403 ret = krb5_rd_rep (context, 404 auth_context, 405 &ap_rep_data, 406 &ap_rep); 407 if (ret) 408 return ret; 409 410 krb5_free_ap_rep_enc_part (context, ap_rep); 411 412 ret = krb5_rd_priv (context, 413 auth_context, 414 &priv_data, 415 result_code_string, 416 NULL); 417 if (ret) { 418 krb5_data_free (result_code_string); 419 return ret; 420 } 421 422 if (result_code_string->length < 2) { 423 *result_code = KRB5_KPASSWD_MALFORMED; 424 str2data (result_string, 425 "client: bad length in result"); 426 return 0; 427 } 428 429 p = result_code_string->data; 430 431 *result_code = (p[0] << 8) | p[1]; 432 krb5_data_copy (result_string, 433 (unsigned char*)result_code_string->data + 2, 434 result_code_string->length - 2); 435 return 0; 436 } else { 437 KRB_ERROR error; 438 size_t size; 439 u_char *p; 440 441 ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); 442 if (ret) { 443 return ret; 444 } 445 if (error.e_data->length < 2) { 446 krb5_warnx (context, "too short e_data to print anything usable"); 447 return 1; /* XXX */ 448 } 449 450 p = error.e_data->data; 451 *result_code = (p[0] << 8) | p[1]; 452 krb5_data_copy (result_string, 453 p + 2, 454 error.e_data->length - 2); 455 return 0; 456 } 457 } 458 459 460 /* 461 * change the password using the credentials in `creds' (for the 462 * principal indicated in them) to `newpw', storing the result of 463 * the operation in `result_*' and an error code or 0. 464 */ 465 466 typedef krb5_error_code (*kpwd_send_request) (krb5_context, 467 krb5_auth_context *, 468 krb5_creds *, 469 krb5_principal, 470 int, 471 rk_socket_t, 472 const char *, 473 const char *); 474 typedef krb5_error_code (*kpwd_process_reply) (krb5_context, 475 krb5_auth_context, 476 int, 477 rk_socket_t, 478 int *, 479 krb5_data *, 480 krb5_data *, 481 const char *); 482 483 static struct kpwd_proc { 484 const char *name; 485 int flags; 486 #define SUPPORT_TCP 1 487 #define SUPPORT_UDP 2 488 kpwd_send_request send_req; 489 kpwd_process_reply process_rep; 490 } procs[] = { 491 { 492 "MS set password", 493 SUPPORT_TCP|SUPPORT_UDP, 494 setpw_send_request, 495 process_reply 496 }, 497 { 498 "change password", 499 SUPPORT_UDP, 500 chgpw_send_request, 501 process_reply 502 }, 503 { NULL, 0, NULL, NULL } 504 }; 505 506 /* 507 * 508 */ 509 510 static krb5_error_code 511 change_password_loop (krb5_context context, 512 krb5_creds *creds, 513 krb5_principal targprinc, 514 const char *newpw, 515 int *result_code, 516 krb5_data *result_code_string, 517 krb5_data *result_string, 518 struct kpwd_proc *proc) 519 { 520 krb5_error_code ret; 521 krb5_auth_context auth_context = NULL; 522 krb5_krbhst_handle handle = NULL; 523 krb5_krbhst_info *hi; 524 rk_socket_t sock; 525 unsigned int i; 526 int done = 0; 527 krb5_realm realm; 528 529 if (targprinc) 530 realm = targprinc->realm; 531 else 532 realm = creds->client->realm; 533 534 ret = krb5_auth_con_init (context, &auth_context); 535 if (ret) 536 return ret; 537 538 krb5_auth_con_setflags (context, auth_context, 539 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 540 541 ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle); 542 if (ret) 543 goto out; 544 545 while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) { 546 struct addrinfo *ai, *a; 547 int is_stream; 548 549 switch (hi->proto) { 550 case KRB5_KRBHST_UDP: 551 if ((proc->flags & SUPPORT_UDP) == 0) 552 continue; 553 is_stream = 0; 554 break; 555 case KRB5_KRBHST_TCP: 556 if ((proc->flags & SUPPORT_TCP) == 0) 557 continue; 558 is_stream = 1; 559 break; 560 default: 561 continue; 562 } 563 564 ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 565 if (ret) 566 continue; 567 568 for (a = ai; !done && a != NULL; a = a->ai_next) { 569 int replied = 0; 570 571 sock = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 572 if (rk_IS_BAD_SOCKET(sock)) 573 continue; 574 rk_cloexec(sock); 575 576 ret = connect(sock, a->ai_addr, a->ai_addrlen); 577 if (rk_IS_SOCKET_ERROR(ret)) { 578 rk_closesocket (sock); 579 goto out; 580 } 581 582 ret = krb5_auth_con_genaddrs (context, auth_context, sock, 583 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); 584 if (ret) { 585 rk_closesocket (sock); 586 goto out; 587 } 588 589 for (i = 0; !done && i < 5; ++i) { 590 fd_set fdset; 591 struct timeval tv; 592 593 if (!replied) { 594 replied = 0; 595 596 ret = (*proc->send_req) (context, 597 &auth_context, 598 creds, 599 targprinc, 600 is_stream, 601 sock, 602 newpw, 603 hi->hostname); 604 if (ret) { 605 rk_closesocket(sock); 606 goto out; 607 } 608 } 609 610 #ifndef NO_LIMIT_FD_SETSIZE 611 if (sock >= FD_SETSIZE) { 612 ret = ERANGE; 613 krb5_set_error_message(context, ret, 614 "fd %d too large", sock); 615 rk_closesocket (sock); 616 goto out; 617 } 618 #endif 619 620 FD_ZERO(&fdset); 621 FD_SET(sock, &fdset); 622 tv.tv_usec = 0; 623 tv.tv_sec = 1 + (1 << i); 624 625 ret = select (sock + 1, &fdset, NULL, NULL, &tv); 626 if (rk_IS_SOCKET_ERROR(ret) && rk_SOCK_ERRNO != EINTR) { 627 rk_closesocket(sock); 628 goto out; 629 } 630 if (ret == 1) { 631 ret = (*proc->process_rep) (context, 632 auth_context, 633 is_stream, 634 sock, 635 result_code, 636 result_code_string, 637 result_string, 638 hi->hostname); 639 if (ret == 0) 640 done = 1; 641 else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL) 642 replied = 1; 643 } else { 644 ret = KRB5_KDC_UNREACH; 645 } 646 } 647 rk_closesocket (sock); 648 } 649 } 650 651 out: 652 krb5_krbhst_free (context, handle); 653 krb5_auth_con_free (context, auth_context); 654 655 if (ret == KRB5_KDC_UNREACH) { 656 krb5_set_error_message(context, 657 ret, 658 N_("Unable to reach any changepw server " 659 " in realm %s", "realm"), realm); 660 *result_code = KRB5_KPASSWD_HARDERROR; 661 } 662 return ret; 663 } 664 665 #ifndef HEIMDAL_SMALLER 666 667 static struct kpwd_proc * 668 find_chpw_proto(const char *name) 669 { 670 struct kpwd_proc *p; 671 for (p = procs; p->name != NULL; p++) { 672 if (strcmp(p->name, name) == 0) 673 return p; 674 } 675 return NULL; 676 } 677 678 /** 679 * Deprecated: krb5_change_password() is deprecated, use krb5_set_password(). 680 * 681 * @param context a Keberos context 682 * @param creds 683 * @param newpw 684 * @param result_code 685 * @param result_code_string 686 * @param result_string 687 * 688 * @return On sucess password is changed. 689 690 * @ingroup @krb5_deprecated 691 */ 692 693 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 694 krb5_change_password (krb5_context context, 695 krb5_creds *creds, 696 const char *newpw, 697 int *result_code, 698 krb5_data *result_code_string, 699 krb5_data *result_string) 700 KRB5_DEPRECATED_FUNCTION("Use X instead") 701 { 702 struct kpwd_proc *p = find_chpw_proto("change password"); 703 704 *result_code = KRB5_KPASSWD_MALFORMED; 705 result_code_string->data = result_string->data = NULL; 706 result_code_string->length = result_string->length = 0; 707 708 if (p == NULL) 709 return KRB5_KPASSWD_MALFORMED; 710 711 return change_password_loop(context, creds, NULL, newpw, 712 result_code, result_code_string, 713 result_string, p); 714 } 715 #endif /* HEIMDAL_SMALLER */ 716 717 /** 718 * Change password using creds. 719 * 720 * @param context a Keberos context 721 * @param creds The initial kadmin/passwd for the principal or an admin principal 722 * @param newpw The new password to set 723 * @param targprinc if unset, the default principal is used. 724 * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed. 725 * @param result_code_string binary message from the server, contains 726 * at least the result_code. 727 * @param result_string A message from the kpasswd service or the 728 * library in human printable form. The string is NUL terminated. 729 * 730 * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed. 731 732 * @ingroup @krb5 733 */ 734 735 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 736 krb5_set_password(krb5_context context, 737 krb5_creds *creds, 738 const char *newpw, 739 krb5_principal targprinc, 740 int *result_code, 741 krb5_data *result_code_string, 742 krb5_data *result_string) 743 { 744 krb5_principal principal = NULL; 745 krb5_error_code ret = 0; 746 int i; 747 748 *result_code = KRB5_KPASSWD_MALFORMED; 749 krb5_data_zero(result_code_string); 750 krb5_data_zero(result_string); 751 752 if (targprinc == NULL) { 753 ret = krb5_get_default_principal(context, &principal); 754 if (ret) 755 return ret; 756 } else 757 principal = targprinc; 758 759 for (i = 0; procs[i].name != NULL; i++) { 760 *result_code = 0; 761 ret = change_password_loop(context, creds, principal, newpw, 762 result_code, result_code_string, 763 result_string, 764 &procs[i]); 765 if (ret == 0 && *result_code == 0) 766 break; 767 } 768 769 if (targprinc == NULL) 770 krb5_free_principal(context, principal); 771 return ret; 772 } 773 774 /* 775 * 776 */ 777 778 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 779 krb5_set_password_using_ccache(krb5_context context, 780 krb5_ccache ccache, 781 const char *newpw, 782 krb5_principal targprinc, 783 int *result_code, 784 krb5_data *result_code_string, 785 krb5_data *result_string) 786 { 787 krb5_creds creds, *credsp; 788 krb5_error_code ret; 789 krb5_principal principal = NULL; 790 791 *result_code = KRB5_KPASSWD_MALFORMED; 792 result_code_string->data = result_string->data = NULL; 793 result_code_string->length = result_string->length = 0; 794 795 memset(&creds, 0, sizeof(creds)); 796 797 if (targprinc == NULL) { 798 ret = krb5_cc_get_principal(context, ccache, &principal); 799 if (ret) 800 return ret; 801 } else 802 principal = targprinc; 803 804 ret = krb5_make_principal(context, &creds.server, 805 krb5_principal_get_realm(context, principal), 806 "kadmin", "changepw", NULL); 807 if (ret) 808 goto out; 809 810 ret = krb5_cc_get_principal(context, ccache, &creds.client); 811 if (ret) { 812 krb5_free_principal(context, creds.server); 813 goto out; 814 } 815 816 ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 817 krb5_free_principal(context, creds.server); 818 krb5_free_principal(context, creds.client); 819 if (ret) 820 goto out; 821 822 ret = krb5_set_password(context, 823 credsp, 824 newpw, 825 principal, 826 result_code, 827 result_code_string, 828 result_string); 829 830 krb5_free_creds(context, credsp); 831 832 return ret; 833 out: 834 if (targprinc == NULL) 835 krb5_free_principal(context, principal); 836 return ret; 837 } 838 839 /* 840 * 841 */ 842 843 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 844 krb5_passwd_result_to_string (krb5_context context, 845 int result) 846 { 847 static const char *strings[] = { 848 "Success", 849 "Malformed", 850 "Hard error", 851 "Auth error", 852 "Soft error" , 853 "Access denied", 854 "Bad version", 855 "Initial flag needed" 856 }; 857 858 if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) 859 return "unknown result code"; 860 else 861 return strings[result]; 862 } 863