1 /* $NetBSD: changepw.c,v 1.1.1.2 2014/04/24 12:45:49 pettai 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 memmove(reply, reply + 4, size); 308 len = size; 309 break; 310 } 311 if (len == sizeof(reply)) { 312 krb5_set_error_message(context, ENOMEM, 313 N_("Message too large from %s", "host"), 314 host); 315 return ENOMEM; 316 } 317 } else { 318 ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); 319 if (rk_IS_SOCKET_ERROR(ret)) { 320 save_errno = rk_SOCK_ERRNO; 321 krb5_set_error_message(context, save_errno, 322 "recvfrom %s: %s", 323 host, strerror(save_errno)); 324 return save_errno; 325 } 326 len = ret; 327 } 328 329 if (len < 6) { 330 str2data (result_string, "server %s sent to too short message " 331 "(%zu bytes)", host, len); 332 *result_code = KRB5_KPASSWD_MALFORMED; 333 return 0; 334 } 335 336 pkt_len = (reply[0] << 8) | (reply[1]); 337 pkt_ver = (reply[2] << 8) | (reply[3]); 338 339 if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) { 340 KRB_ERROR error; 341 size_t size; 342 u_char *p; 343 344 memset(&error, 0, sizeof(error)); 345 346 ret = decode_KRB_ERROR(reply, len, &error, &size); 347 if (ret) 348 return ret; 349 350 if (error.e_data->length < 2) { 351 str2data(result_string, "server %s sent too short " 352 "e_data to print anything usable", host); 353 free_KRB_ERROR(&error); 354 *result_code = KRB5_KPASSWD_MALFORMED; 355 return 0; 356 } 357 358 p = error.e_data->data; 359 *result_code = (p[0] << 8) | p[1]; 360 if (error.e_data->length == 2) 361 str2data(result_string, "server only sent error code"); 362 else 363 krb5_data_copy (result_string, 364 p + 2, 365 error.e_data->length - 2); 366 free_KRB_ERROR(&error); 367 return 0; 368 } 369 370 if (pkt_len != len) { 371 str2data (result_string, "client: wrong len in reply"); 372 *result_code = KRB5_KPASSWD_MALFORMED; 373 return 0; 374 } 375 if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { 376 str2data (result_string, 377 "client: wrong version number (%d)", pkt_ver); 378 *result_code = KRB5_KPASSWD_MALFORMED; 379 return 0; 380 } 381 382 ap_rep_data.data = reply + 6; 383 ap_rep_data.length = (reply[4] << 8) | (reply[5]); 384 385 if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) { 386 str2data (result_string, "client: wrong AP len in reply"); 387 *result_code = KRB5_KPASSWD_MALFORMED; 388 return 0; 389 } 390 391 if (ap_rep_data.length) { 392 krb5_ap_rep_enc_part *ap_rep; 393 krb5_data priv_data; 394 u_char *p; 395 396 priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; 397 priv_data.length = len - ap_rep_data.length - 6; 398 399 ret = krb5_rd_rep (context, 400 auth_context, 401 &ap_rep_data, 402 &ap_rep); 403 if (ret) 404 return ret; 405 406 krb5_free_ap_rep_enc_part (context, ap_rep); 407 408 ret = krb5_rd_priv (context, 409 auth_context, 410 &priv_data, 411 result_code_string, 412 NULL); 413 if (ret) { 414 krb5_data_free (result_code_string); 415 return ret; 416 } 417 418 if (result_code_string->length < 2) { 419 *result_code = KRB5_KPASSWD_MALFORMED; 420 str2data (result_string, 421 "client: bad length in result"); 422 return 0; 423 } 424 425 p = result_code_string->data; 426 427 *result_code = (p[0] << 8) | p[1]; 428 krb5_data_copy (result_string, 429 (unsigned char*)result_code_string->data + 2, 430 result_code_string->length - 2); 431 return 0; 432 } else { 433 KRB_ERROR error; 434 size_t size; 435 u_char *p; 436 437 ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); 438 if (ret) { 439 return ret; 440 } 441 if (error.e_data->length < 2) { 442 krb5_warnx (context, "too short e_data to print anything usable"); 443 return 1; /* XXX */ 444 } 445 446 p = error.e_data->data; 447 *result_code = (p[0] << 8) | p[1]; 448 krb5_data_copy (result_string, 449 p + 2, 450 error.e_data->length - 2); 451 return 0; 452 } 453 } 454 455 456 /* 457 * change the password using the credentials in `creds' (for the 458 * principal indicated in them) to `newpw', storing the result of 459 * the operation in `result_*' and an error code or 0. 460 */ 461 462 typedef krb5_error_code (*kpwd_send_request) (krb5_context, 463 krb5_auth_context *, 464 krb5_creds *, 465 krb5_principal, 466 int, 467 rk_socket_t, 468 const char *, 469 const char *); 470 typedef krb5_error_code (*kpwd_process_reply) (krb5_context, 471 krb5_auth_context, 472 int, 473 rk_socket_t, 474 int *, 475 krb5_data *, 476 krb5_data *, 477 const char *); 478 479 static struct kpwd_proc { 480 const char *name; 481 int flags; 482 #define SUPPORT_TCP 1 483 #define SUPPORT_UDP 2 484 kpwd_send_request send_req; 485 kpwd_process_reply process_rep; 486 } procs[] = { 487 { 488 "MS set password", 489 SUPPORT_TCP|SUPPORT_UDP, 490 setpw_send_request, 491 process_reply 492 }, 493 { 494 "change password", 495 SUPPORT_UDP, 496 chgpw_send_request, 497 process_reply 498 }, 499 { NULL, 0, NULL, NULL } 500 }; 501 502 /* 503 * 504 */ 505 506 static krb5_error_code 507 change_password_loop (krb5_context context, 508 krb5_creds *creds, 509 krb5_principal targprinc, 510 const char *newpw, 511 int *result_code, 512 krb5_data *result_code_string, 513 krb5_data *result_string, 514 struct kpwd_proc *proc) 515 { 516 krb5_error_code ret; 517 krb5_auth_context auth_context = NULL; 518 krb5_krbhst_handle handle = NULL; 519 krb5_krbhst_info *hi; 520 rk_socket_t sock; 521 unsigned int i; 522 int done = 0; 523 krb5_realm realm; 524 525 if (targprinc) 526 realm = targprinc->realm; 527 else 528 realm = creds->client->realm; 529 530 ret = krb5_auth_con_init (context, &auth_context); 531 if (ret) 532 return ret; 533 534 krb5_auth_con_setflags (context, auth_context, 535 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 536 537 ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle); 538 if (ret) 539 goto out; 540 541 while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) { 542 struct addrinfo *ai, *a; 543 int is_stream; 544 545 switch (hi->proto) { 546 case KRB5_KRBHST_UDP: 547 if ((proc->flags & SUPPORT_UDP) == 0) 548 continue; 549 is_stream = 0; 550 break; 551 case KRB5_KRBHST_TCP: 552 if ((proc->flags & SUPPORT_TCP) == 0) 553 continue; 554 is_stream = 1; 555 break; 556 default: 557 continue; 558 } 559 560 ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 561 if (ret) 562 continue; 563 564 for (a = ai; !done && a != NULL; a = a->ai_next) { 565 int replied = 0; 566 567 sock = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 568 if (rk_IS_BAD_SOCKET(sock)) 569 continue; 570 rk_cloexec(sock); 571 572 ret = connect(sock, a->ai_addr, a->ai_addrlen); 573 if (rk_IS_SOCKET_ERROR(ret)) { 574 rk_closesocket (sock); 575 goto out; 576 } 577 578 ret = krb5_auth_con_genaddrs (context, auth_context, sock, 579 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); 580 if (ret) { 581 rk_closesocket (sock); 582 goto out; 583 } 584 585 for (i = 0; !done && i < 5; ++i) { 586 fd_set fdset; 587 struct timeval tv; 588 589 if (!replied) { 590 replied = 0; 591 592 ret = (*proc->send_req) (context, 593 &auth_context, 594 creds, 595 targprinc, 596 is_stream, 597 sock, 598 newpw, 599 hi->hostname); 600 if (ret) { 601 rk_closesocket(sock); 602 goto out; 603 } 604 } 605 606 #ifndef NO_LIMIT_FD_SETSIZE 607 if (sock >= FD_SETSIZE) { 608 ret = ERANGE; 609 krb5_set_error_message(context, ret, 610 "fd %d too large", sock); 611 rk_closesocket (sock); 612 goto out; 613 } 614 #endif 615 616 FD_ZERO(&fdset); 617 FD_SET(sock, &fdset); 618 tv.tv_usec = 0; 619 tv.tv_sec = 1 + (1 << i); 620 621 ret = select (sock + 1, &fdset, NULL, NULL, &tv); 622 if (rk_IS_SOCKET_ERROR(ret) && rk_SOCK_ERRNO != EINTR) { 623 rk_closesocket(sock); 624 goto out; 625 } 626 if (ret == 1) { 627 ret = (*proc->process_rep) (context, 628 auth_context, 629 is_stream, 630 sock, 631 result_code, 632 result_code_string, 633 result_string, 634 hi->hostname); 635 if (ret == 0) 636 done = 1; 637 else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL) 638 replied = 1; 639 } else { 640 ret = KRB5_KDC_UNREACH; 641 } 642 } 643 rk_closesocket (sock); 644 } 645 } 646 647 out: 648 krb5_krbhst_free (context, handle); 649 krb5_auth_con_free (context, auth_context); 650 651 if (ret == KRB5_KDC_UNREACH) { 652 krb5_set_error_message(context, 653 ret, 654 N_("Unable to reach any changepw server " 655 " in realm %s", "realm"), realm); 656 *result_code = KRB5_KPASSWD_HARDERROR; 657 } 658 return ret; 659 } 660 661 #ifndef HEIMDAL_SMALLER 662 663 static struct kpwd_proc * 664 find_chpw_proto(const char *name) 665 { 666 struct kpwd_proc *p; 667 for (p = procs; p->name != NULL; p++) { 668 if (strcmp(p->name, name) == 0) 669 return p; 670 } 671 return NULL; 672 } 673 674 /** 675 * Deprecated: krb5_change_password() is deprecated, use krb5_set_password(). 676 * 677 * @param context a Keberos context 678 * @param creds 679 * @param newpw 680 * @param result_code 681 * @param result_code_string 682 * @param result_string 683 * 684 * @return On sucess password is changed. 685 686 * @ingroup @krb5_deprecated 687 */ 688 689 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 690 krb5_change_password (krb5_context context, 691 krb5_creds *creds, 692 const char *newpw, 693 int *result_code, 694 krb5_data *result_code_string, 695 krb5_data *result_string) 696 KRB5_DEPRECATED_FUNCTION("Use X instead") 697 { 698 struct kpwd_proc *p = find_chpw_proto("change password"); 699 700 *result_code = KRB5_KPASSWD_MALFORMED; 701 result_code_string->data = result_string->data = NULL; 702 result_code_string->length = result_string->length = 0; 703 704 if (p == NULL) 705 return KRB5_KPASSWD_MALFORMED; 706 707 return change_password_loop(context, creds, NULL, newpw, 708 result_code, result_code_string, 709 result_string, p); 710 } 711 #endif /* HEIMDAL_SMALLER */ 712 713 /** 714 * Change password using creds. 715 * 716 * @param context a Keberos context 717 * @param creds The initial kadmin/passwd for the principal or an admin principal 718 * @param newpw The new password to set 719 * @param targprinc if unset, the default principal is used. 720 * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed. 721 * @param result_code_string binary message from the server, contains 722 * at least the result_code. 723 * @param result_string A message from the kpasswd service or the 724 * library in human printable form. The string is NUL terminated. 725 * 726 * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed. 727 728 * @ingroup @krb5 729 */ 730 731 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 732 krb5_set_password(krb5_context context, 733 krb5_creds *creds, 734 const char *newpw, 735 krb5_principal targprinc, 736 int *result_code, 737 krb5_data *result_code_string, 738 krb5_data *result_string) 739 { 740 krb5_principal principal = NULL; 741 krb5_error_code ret = 0; 742 int i; 743 744 *result_code = KRB5_KPASSWD_MALFORMED; 745 krb5_data_zero(result_code_string); 746 krb5_data_zero(result_string); 747 748 if (targprinc == NULL) { 749 ret = krb5_get_default_principal(context, &principal); 750 if (ret) 751 return ret; 752 } else 753 principal = targprinc; 754 755 for (i = 0; procs[i].name != NULL; i++) { 756 *result_code = 0; 757 ret = change_password_loop(context, creds, principal, newpw, 758 result_code, result_code_string, 759 result_string, 760 &procs[i]); 761 if (ret == 0 && *result_code == 0) 762 break; 763 } 764 765 if (targprinc == NULL) 766 krb5_free_principal(context, principal); 767 return ret; 768 } 769 770 /* 771 * 772 */ 773 774 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 775 krb5_set_password_using_ccache(krb5_context context, 776 krb5_ccache ccache, 777 const char *newpw, 778 krb5_principal targprinc, 779 int *result_code, 780 krb5_data *result_code_string, 781 krb5_data *result_string) 782 { 783 krb5_creds creds, *credsp; 784 krb5_error_code ret; 785 krb5_principal principal = NULL; 786 787 *result_code = KRB5_KPASSWD_MALFORMED; 788 result_code_string->data = result_string->data = NULL; 789 result_code_string->length = result_string->length = 0; 790 791 memset(&creds, 0, sizeof(creds)); 792 793 if (targprinc == NULL) { 794 ret = krb5_cc_get_principal(context, ccache, &principal); 795 if (ret) 796 return ret; 797 } else 798 principal = targprinc; 799 800 ret = krb5_make_principal(context, &creds.server, 801 krb5_principal_get_realm(context, principal), 802 "kadmin", "changepw", NULL); 803 if (ret) 804 goto out; 805 806 ret = krb5_cc_get_principal(context, ccache, &creds.client); 807 if (ret) { 808 krb5_free_principal(context, creds.server); 809 goto out; 810 } 811 812 ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 813 krb5_free_principal(context, creds.server); 814 krb5_free_principal(context, creds.client); 815 if (ret) 816 goto out; 817 818 ret = krb5_set_password(context, 819 credsp, 820 newpw, 821 principal, 822 result_code, 823 result_code_string, 824 result_string); 825 826 krb5_free_creds(context, credsp); 827 828 return ret; 829 out: 830 if (targprinc == NULL) 831 krb5_free_principal(context, principal); 832 return ret; 833 } 834 835 /* 836 * 837 */ 838 839 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 840 krb5_passwd_result_to_string (krb5_context context, 841 int result) 842 { 843 static const char *strings[] = { 844 "Success", 845 "Malformed", 846 "Hard error", 847 "Auth error", 848 "Soft error" , 849 "Access denied", 850 "Bad version", 851 "Initial flag needed" 852 }; 853 854 if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) 855 return "unknown result code"; 856 else 857 return strings[result]; 858 } 859