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