155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 34233294Sstas#include "krb5_locl.h" 3555682Smarkm 36233294Sstas#undef __attribute__ 37233294Sstas#define __attribute__(X) 3855682Smarkm 39233294Sstas 40142403Snectarstatic void 41142403Snectarstr2data (krb5_data *d, 42142403Snectar const char *fmt, 43142403Snectar ...) __attribute__ ((format (printf, 2, 3))); 44142403Snectar 45142403Snectarstatic void 46142403Snectarstr2data (krb5_data *d, 47142403Snectar const char *fmt, 48142403Snectar ...) 49142403Snectar{ 50142403Snectar va_list args; 51178825Sdfr char *str; 52142403Snectar 53142403Snectar va_start(args, fmt); 54178825Sdfr d->length = vasprintf (&str, fmt, args); 55142403Snectar va_end(args); 56178825Sdfr d->data = str; 57142403Snectar} 58142403Snectar 59142403Snectar/* 60142403Snectar * Change password protocol defined by 61142403Snectar * draft-ietf-cat-kerb-chg-password-02.txt 62233294Sstas * 63142403Snectar * Share the response part of the protocol with MS set password 64142403Snectar * (RFC3244) 65142403Snectar */ 66142403Snectar 6755682Smarkmstatic krb5_error_code 68142403Snectarchgpw_send_request (krb5_context context, 69142403Snectar krb5_auth_context *auth_context, 70142403Snectar krb5_creds *creds, 71142403Snectar krb5_principal targprinc, 72142403Snectar int is_stream, 73233294Sstas rk_socket_t sock, 74178825Sdfr const char *passwd, 75142403Snectar const char *host) 7655682Smarkm{ 7755682Smarkm krb5_error_code ret; 7855682Smarkm krb5_data ap_req_data; 7955682Smarkm krb5_data krb_priv_data; 8055682Smarkm krb5_data passwd_data; 8155682Smarkm size_t len; 8255682Smarkm u_char header[6]; 8355682Smarkm struct iovec iov[3]; 8455682Smarkm struct msghdr msghdr; 8555682Smarkm 86142403Snectar if (is_stream) 87142403Snectar return KRB5_KPASSWD_MALFORMED; 88142403Snectar 89142403Snectar if (targprinc && 90142403Snectar krb5_principal_compare(context, creds->client, targprinc) != TRUE) 91142403Snectar return KRB5_KPASSWD_MALFORMED; 92142403Snectar 9355682Smarkm krb5_data_zero (&ap_req_data); 9455682Smarkm 9555682Smarkm ret = krb5_mk_req_extended (context, 9655682Smarkm auth_context, 97103423Snectar AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 9855682Smarkm NULL, /* in_data */ 9955682Smarkm creds, 10055682Smarkm &ap_req_data); 10155682Smarkm if (ret) 10255682Smarkm return ret; 10355682Smarkm 104178825Sdfr passwd_data.data = rk_UNCONST(passwd); 10555682Smarkm passwd_data.length = strlen(passwd); 10655682Smarkm 10755682Smarkm krb5_data_zero (&krb_priv_data); 10855682Smarkm 10955682Smarkm ret = krb5_mk_priv (context, 11055682Smarkm *auth_context, 11155682Smarkm &passwd_data, 11255682Smarkm &krb_priv_data, 11355682Smarkm NULL); 11455682Smarkm if (ret) 11555682Smarkm goto out2; 11655682Smarkm 11755682Smarkm len = 6 + ap_req_data.length + krb_priv_data.length; 118233294Sstas header[0] = (len >> 8) & 0xFF; 119233294Sstas header[1] = (len >> 0) & 0xFF; 120233294Sstas header[2] = 0; 121233294Sstas header[3] = 1; 122233294Sstas header[4] = (ap_req_data.length >> 8) & 0xFF; 123233294Sstas header[5] = (ap_req_data.length >> 0) & 0xFF; 12455682Smarkm 12555682Smarkm memset(&msghdr, 0, sizeof(msghdr)); 12690926Snectar msghdr.msg_name = NULL; 12790926Snectar msghdr.msg_namelen = 0; 12855682Smarkm msghdr.msg_iov = iov; 12955682Smarkm msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); 13055682Smarkm#if 0 13155682Smarkm msghdr.msg_control = NULL; 13255682Smarkm msghdr.msg_controllen = 0; 13355682Smarkm#endif 13455682Smarkm 13555682Smarkm iov[0].iov_base = (void*)header; 13655682Smarkm iov[0].iov_len = 6; 13755682Smarkm iov[1].iov_base = ap_req_data.data; 13855682Smarkm iov[1].iov_len = ap_req_data.length; 13955682Smarkm iov[2].iov_base = krb_priv_data.data; 14055682Smarkm iov[2].iov_len = krb_priv_data.length; 14155682Smarkm 142233294Sstas if (rk_IS_SOCKET_ERROR( sendmsg (sock, &msghdr, 0) )) { 143233294Sstas ret = rk_SOCK_ERRNO; 144233294Sstas krb5_set_error_message(context, ret, "sendmsg %s: %s", 145233294Sstas host, strerror(ret)); 14678527Sassar } 14755682Smarkm 14855682Smarkm krb5_data_free (&krb_priv_data); 14955682Smarkmout2: 15055682Smarkm krb5_data_free (&ap_req_data); 15155682Smarkm return ret; 15255682Smarkm} 15355682Smarkm 154142403Snectar/* 155142403Snectar * Set password protocol as defined by RFC3244 -- 156142403Snectar * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols 157142403Snectar */ 15872445Sassar 159142403Snectarstatic krb5_error_code 160142403Snectarsetpw_send_request (krb5_context context, 161142403Snectar krb5_auth_context *auth_context, 162142403Snectar krb5_creds *creds, 163142403Snectar krb5_principal targprinc, 164142403Snectar int is_stream, 165233294Sstas rk_socket_t sock, 166178825Sdfr const char *passwd, 167142403Snectar const char *host) 16855682Smarkm{ 169142403Snectar krb5_error_code ret; 170142403Snectar krb5_data ap_req_data; 171142403Snectar krb5_data krb_priv_data; 172142403Snectar krb5_data pwd_data; 173142403Snectar ChangePasswdDataMS chpw; 174233294Sstas size_t len = 0; 175142403Snectar u_char header[4 + 6]; 176142403Snectar u_char *p; 177142403Snectar struct iovec iov[3]; 178142403Snectar struct msghdr msghdr; 17955682Smarkm 180142403Snectar krb5_data_zero (&ap_req_data); 181142403Snectar 182142403Snectar ret = krb5_mk_req_extended (context, 183142403Snectar auth_context, 184142403Snectar AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 185142403Snectar NULL, /* in_data */ 186142403Snectar creds, 187142403Snectar &ap_req_data); 188142403Snectar if (ret) 189142403Snectar return ret; 190142403Snectar 191142403Snectar chpw.newpasswd.length = strlen(passwd); 192178825Sdfr chpw.newpasswd.data = rk_UNCONST(passwd); 193142403Snectar if (targprinc) { 194142403Snectar chpw.targname = &targprinc->name; 195142403Snectar chpw.targrealm = &targprinc->realm; 196142403Snectar } else { 197142403Snectar chpw.targname = NULL; 198142403Snectar chpw.targrealm = NULL; 199142403Snectar } 200233294Sstas 201142403Snectar ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, 202142403Snectar &chpw, &len, ret); 203142403Snectar if (ret) { 204142403Snectar krb5_data_free (&ap_req_data); 205142403Snectar return ret; 206142403Snectar } 207142403Snectar 208142403Snectar if(pwd_data.length != len) 209142403Snectar krb5_abortx(context, "internal error in ASN.1 encoder"); 210142403Snectar 211142403Snectar ret = krb5_mk_priv (context, 212142403Snectar *auth_context, 213142403Snectar &pwd_data, 214142403Snectar &krb_priv_data, 215142403Snectar NULL); 216142403Snectar if (ret) 217142403Snectar goto out2; 218142403Snectar 219142403Snectar len = 6 + ap_req_data.length + krb_priv_data.length; 220142403Snectar p = header; 221142403Snectar if (is_stream) { 222142403Snectar _krb5_put_int(p, len, 4); 223142403Snectar p += 4; 224142403Snectar } 225142403Snectar *p++ = (len >> 8) & 0xFF; 226142403Snectar *p++ = (len >> 0) & 0xFF; 227142403Snectar *p++ = 0xff; 228142403Snectar *p++ = 0x80; 229142403Snectar *p++ = (ap_req_data.length >> 8) & 0xFF; 230233294Sstas *p = (ap_req_data.length >> 0) & 0xFF; 231142403Snectar 232142403Snectar memset(&msghdr, 0, sizeof(msghdr)); 233142403Snectar msghdr.msg_name = NULL; 234142403Snectar msghdr.msg_namelen = 0; 235142403Snectar msghdr.msg_iov = iov; 236142403Snectar msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); 237142403Snectar#if 0 238142403Snectar msghdr.msg_control = NULL; 239142403Snectar msghdr.msg_controllen = 0; 240142403Snectar#endif 241142403Snectar 242142403Snectar iov[0].iov_base = (void*)header; 243142403Snectar if (is_stream) 244142403Snectar iov[0].iov_len = 10; 245142403Snectar else 246142403Snectar iov[0].iov_len = 6; 247142403Snectar iov[1].iov_base = ap_req_data.data; 248142403Snectar iov[1].iov_len = ap_req_data.length; 249142403Snectar iov[2].iov_base = krb_priv_data.data; 250142403Snectar iov[2].iov_len = krb_priv_data.length; 251142403Snectar 252233294Sstas if (rk_IS_SOCKET_ERROR( sendmsg (sock, &msghdr, 0) )) { 253233294Sstas ret = rk_SOCK_ERRNO; 254233294Sstas krb5_set_error_message(context, ret, "sendmsg %s: %s", 255233294Sstas host, strerror(ret)); 256142403Snectar } 257142403Snectar 258142403Snectar krb5_data_free (&krb_priv_data); 259142403Snectarout2: 260142403Snectar krb5_data_free (&ap_req_data); 261142403Snectar krb5_data_free (&pwd_data); 262142403Snectar return ret; 26355682Smarkm} 26455682Smarkm 26555682Smarkmstatic krb5_error_code 26655682Smarkmprocess_reply (krb5_context context, 26755682Smarkm krb5_auth_context auth_context, 268142403Snectar int is_stream, 269233294Sstas rk_socket_t sock, 27055682Smarkm int *result_code, 27155682Smarkm krb5_data *result_code_string, 27278527Sassar krb5_data *result_string, 27378527Sassar const char *host) 27455682Smarkm{ 27555682Smarkm krb5_error_code ret; 276142403Snectar u_char reply[1024 * 3]; 277233294Sstas size_t len; 278178825Sdfr uint16_t pkt_len, pkt_ver; 279142403Snectar krb5_data ap_rep_data; 28078527Sassar int save_errno; 28155682Smarkm 282142403Snectar len = 0; 283142403Snectar if (is_stream) { 284142403Snectar while (len < sizeof(reply)) { 285142403Snectar unsigned long size; 286142403Snectar 287233294Sstas ret = recvfrom (sock, reply + len, sizeof(reply) - len, 288142403Snectar 0, NULL, NULL); 289233294Sstas if (rk_IS_SOCKET_ERROR(ret)) { 290233294Sstas save_errno = rk_SOCK_ERRNO; 291233294Sstas krb5_set_error_message(context, save_errno, 292233294Sstas "recvfrom %s: %s", 293233294Sstas host, strerror(save_errno)); 294142403Snectar return save_errno; 295142403Snectar } else if (ret == 0) { 296233294Sstas krb5_set_error_message(context, 1,"recvfrom timeout %s", host); 297142403Snectar return 1; 298142403Snectar } 299142403Snectar len += ret; 300142403Snectar if (len < 4) 301142403Snectar continue; 302142403Snectar _krb5_get_int(reply, &size, 4); 303142403Snectar if (size + 4 < len) 304142403Snectar continue; 305233294Sstas memmove(reply, reply + 4, size); 306142403Snectar len = size; 307142403Snectar break; 308142403Snectar } 309142403Snectar if (len == sizeof(reply)) { 310233294Sstas krb5_set_error_message(context, ENOMEM, 311233294Sstas N_("Message too large from %s", "host"), 312233294Sstas host); 313142403Snectar return ENOMEM; 314142403Snectar } 315142403Snectar } else { 316142403Snectar ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); 317233294Sstas if (rk_IS_SOCKET_ERROR(ret)) { 318233294Sstas save_errno = rk_SOCK_ERRNO; 319233294Sstas krb5_set_error_message(context, save_errno, 320233294Sstas "recvfrom %s: %s", 321233294Sstas host, strerror(save_errno)); 322142403Snectar return save_errno; 323142403Snectar } 324142403Snectar len = ret; 32578527Sassar } 32655682Smarkm 327142403Snectar if (len < 6) { 328142403Snectar str2data (result_string, "server %s sent to too short message " 329233294Sstas "(%zu bytes)", host, len); 330142403Snectar *result_code = KRB5_KPASSWD_MALFORMED; 331142403Snectar return 0; 332142403Snectar } 333142403Snectar 33455682Smarkm pkt_len = (reply[0] << 8) | (reply[1]); 33555682Smarkm pkt_ver = (reply[2] << 8) | (reply[3]); 33655682Smarkm 337142403Snectar if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) { 338142403Snectar KRB_ERROR error; 339142403Snectar size_t size; 340142403Snectar u_char *p; 341142403Snectar 342142403Snectar memset(&error, 0, sizeof(error)); 343142403Snectar 344142403Snectar ret = decode_KRB_ERROR(reply, len, &error, &size); 345142403Snectar if (ret) 346142403Snectar return ret; 347142403Snectar 348142403Snectar if (error.e_data->length < 2) { 349142403Snectar str2data(result_string, "server %s sent too short " 350142403Snectar "e_data to print anything usable", host); 351142403Snectar free_KRB_ERROR(&error); 352142403Snectar *result_code = KRB5_KPASSWD_MALFORMED; 353142403Snectar return 0; 354142403Snectar } 355142403Snectar 356142403Snectar p = error.e_data->data; 357142403Snectar *result_code = (p[0] << 8) | p[1]; 358142403Snectar if (error.e_data->length == 2) 359142403Snectar str2data(result_string, "server only sent error code"); 360233294Sstas else 361142403Snectar krb5_data_copy (result_string, 362142403Snectar p + 2, 363142403Snectar error.e_data->length - 2); 364142403Snectar free_KRB_ERROR(&error); 365142403Snectar return 0; 366142403Snectar } 367142403Snectar 36855682Smarkm if (pkt_len != len) { 36955682Smarkm str2data (result_string, "client: wrong len in reply"); 37055682Smarkm *result_code = KRB5_KPASSWD_MALFORMED; 37155682Smarkm return 0; 37255682Smarkm } 373142403Snectar if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { 37455682Smarkm str2data (result_string, 37555682Smarkm "client: wrong version number (%d)", pkt_ver); 37655682Smarkm *result_code = KRB5_KPASSWD_MALFORMED; 37755682Smarkm return 0; 37855682Smarkm } 37955682Smarkm 38055682Smarkm ap_rep_data.data = reply + 6; 38155682Smarkm ap_rep_data.length = (reply[4] << 8) | (reply[5]); 382233294Sstas 383142403Snectar if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) { 384142403Snectar str2data (result_string, "client: wrong AP len in reply"); 385142403Snectar *result_code = KRB5_KPASSWD_MALFORMED; 386142403Snectar return 0; 387142403Snectar } 388142403Snectar 38955682Smarkm if (ap_rep_data.length) { 39055682Smarkm krb5_ap_rep_enc_part *ap_rep; 391142403Snectar krb5_data priv_data; 39255682Smarkm u_char *p; 39355682Smarkm 394142403Snectar priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; 395142403Snectar priv_data.length = len - ap_rep_data.length - 6; 396142403Snectar 39755682Smarkm ret = krb5_rd_rep (context, 39855682Smarkm auth_context, 39955682Smarkm &ap_rep_data, 40055682Smarkm &ap_rep); 40155682Smarkm if (ret) 40255682Smarkm return ret; 40355682Smarkm 40455682Smarkm krb5_free_ap_rep_enc_part (context, ap_rep); 40555682Smarkm 40655682Smarkm ret = krb5_rd_priv (context, 40755682Smarkm auth_context, 40855682Smarkm &priv_data, 40955682Smarkm result_code_string, 41055682Smarkm NULL); 41155682Smarkm if (ret) { 41255682Smarkm krb5_data_free (result_code_string); 41355682Smarkm return ret; 41455682Smarkm } 41555682Smarkm 41655682Smarkm if (result_code_string->length < 2) { 41755682Smarkm *result_code = KRB5_KPASSWD_MALFORMED; 41855682Smarkm str2data (result_string, 41955682Smarkm "client: bad length in result"); 42055682Smarkm return 0; 42155682Smarkm } 422142403Snectar 423142403Snectar p = result_code_string->data; 424233294Sstas 425142403Snectar *result_code = (p[0] << 8) | p[1]; 426142403Snectar krb5_data_copy (result_string, 427142403Snectar (unsigned char*)result_code_string->data + 2, 428142403Snectar result_code_string->length - 2); 429142403Snectar return 0; 43055682Smarkm } else { 43155682Smarkm KRB_ERROR error; 43255682Smarkm size_t size; 43355682Smarkm u_char *p; 434233294Sstas 43555682Smarkm ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); 43655682Smarkm if (ret) { 43755682Smarkm return ret; 43855682Smarkm } 43955682Smarkm if (error.e_data->length < 2) { 44055682Smarkm krb5_warnx (context, "too short e_data to print anything usable"); 44178527Sassar return 1; /* XXX */ 44255682Smarkm } 44355682Smarkm 44455682Smarkm p = error.e_data->data; 44555682Smarkm *result_code = (p[0] << 8) | p[1]; 44655682Smarkm krb5_data_copy (result_string, 44755682Smarkm p + 2, 44855682Smarkm error.e_data->length - 2); 44955682Smarkm return 0; 45055682Smarkm } 45155682Smarkm} 45255682Smarkm 453142403Snectar 45478527Sassar/* 45578527Sassar * change the password using the credentials in `creds' (for the 45678527Sassar * principal indicated in them) to `newpw', storing the result of 45778527Sassar * the operation in `result_*' and an error code or 0. 45878527Sassar */ 45978527Sassar 460142403Snectartypedef krb5_error_code (*kpwd_send_request) (krb5_context, 461142403Snectar krb5_auth_context *, 462142403Snectar krb5_creds *, 463142403Snectar krb5_principal, 464142403Snectar int, 465233294Sstas rk_socket_t, 466178825Sdfr const char *, 467142403Snectar const char *); 468142403Snectartypedef krb5_error_code (*kpwd_process_reply) (krb5_context, 469142403Snectar krb5_auth_context, 470142403Snectar int, 471233294Sstas rk_socket_t, 472142403Snectar int *, 473142403Snectar krb5_data *, 474142403Snectar krb5_data *, 475142403Snectar const char *); 476142403Snectar 477178825Sdfrstatic struct kpwd_proc { 478142403Snectar const char *name; 479142403Snectar int flags; 480142403Snectar#define SUPPORT_TCP 1 481142403Snectar#define SUPPORT_UDP 2 482142403Snectar kpwd_send_request send_req; 483142403Snectar kpwd_process_reply process_rep; 484142403Snectar} procs[] = { 485142403Snectar { 486233294Sstas "MS set password", 487142403Snectar SUPPORT_TCP|SUPPORT_UDP, 488233294Sstas setpw_send_request, 489142403Snectar process_reply 490142403Snectar }, 491142403Snectar { 492142403Snectar "change password", 493142403Snectar SUPPORT_UDP, 494142403Snectar chgpw_send_request, 495142403Snectar process_reply 496142403Snectar }, 497233294Sstas { NULL, 0, NULL, NULL } 498142403Snectar}; 499142403Snectar 500142403Snectar/* 501142403Snectar * 502142403Snectar */ 503142403Snectar 504142403Snectarstatic krb5_error_code 505142403Snectarchange_password_loop (krb5_context context, 50655682Smarkm krb5_creds *creds, 507142403Snectar krb5_principal targprinc, 508178825Sdfr const char *newpw, 50955682Smarkm int *result_code, 51055682Smarkm krb5_data *result_code_string, 511142403Snectar krb5_data *result_string, 512142403Snectar struct kpwd_proc *proc) 51355682Smarkm{ 51455682Smarkm krb5_error_code ret; 51555682Smarkm krb5_auth_context auth_context = NULL; 51690926Snectar krb5_krbhst_handle handle = NULL; 51790926Snectar krb5_krbhst_info *hi; 518233294Sstas rk_socket_t sock; 519233294Sstas unsigned int i; 52072445Sassar int done = 0; 521178825Sdfr krb5_realm realm; 52255682Smarkm 523178825Sdfr if (targprinc) 524178825Sdfr realm = targprinc->realm; 525178825Sdfr else 526178825Sdfr realm = creds->client->realm; 527178825Sdfr 52855682Smarkm ret = krb5_auth_con_init (context, &auth_context); 52955682Smarkm if (ret) 53055682Smarkm return ret; 53155682Smarkm 53290926Snectar krb5_auth_con_setflags (context, auth_context, 53390926Snectar KRB5_AUTH_CONTEXT_DO_SEQUENCE); 53490926Snectar 53590926Snectar ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle); 53655682Smarkm if (ret) 53755682Smarkm goto out; 53855682Smarkm 539102644Snectar while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) { 54090926Snectar struct addrinfo *ai, *a; 541142403Snectar int is_stream; 54255682Smarkm 543142403Snectar switch (hi->proto) { 544142403Snectar case KRB5_KRBHST_UDP: 545142403Snectar if ((proc->flags & SUPPORT_UDP) == 0) 546142403Snectar continue; 547142403Snectar is_stream = 0; 548142403Snectar break; 549142403Snectar case KRB5_KRBHST_TCP: 550142403Snectar if ((proc->flags & SUPPORT_TCP) == 0) 551142403Snectar continue; 552142403Snectar is_stream = 1; 553142403Snectar break; 554142403Snectar default: 555142403Snectar continue; 556142403Snectar } 557142403Snectar 55890926Snectar ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 55990926Snectar if (ret) 56055682Smarkm continue; 56155682Smarkm 56290926Snectar for (a = ai; !done && a != NULL; a = a->ai_next) { 56390926Snectar int replied = 0; 56455682Smarkm 565233294Sstas sock = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); 566233294Sstas if (rk_IS_BAD_SOCKET(sock)) 56790926Snectar continue; 568233294Sstas rk_cloexec(sock); 56990926Snectar 57090926Snectar ret = connect(sock, a->ai_addr, a->ai_addrlen); 571233294Sstas if (rk_IS_SOCKET_ERROR(ret)) { 572233294Sstas rk_closesocket (sock); 57390926Snectar goto out; 57472445Sassar } 57590926Snectar 57690926Snectar ret = krb5_auth_con_genaddrs (context, auth_context, sock, 57790926Snectar KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); 57890926Snectar if (ret) { 579233294Sstas rk_closesocket (sock); 58055682Smarkm goto out; 58172445Sassar } 58255682Smarkm 58390926Snectar for (i = 0; !done && i < 5; ++i) { 58490926Snectar fd_set fdset; 58590926Snectar struct timeval tv; 58655682Smarkm 58790926Snectar if (!replied) { 58890926Snectar replied = 0; 589233294Sstas 590142403Snectar ret = (*proc->send_req) (context, 591142403Snectar &auth_context, 592142403Snectar creds, 593142403Snectar targprinc, 594142403Snectar is_stream, 595142403Snectar sock, 596142403Snectar newpw, 597142403Snectar hi->hostname); 59890926Snectar if (ret) { 599233294Sstas rk_closesocket(sock); 60090926Snectar goto out; 60190926Snectar } 60290926Snectar } 603233294Sstas 604233294Sstas#ifndef NO_LIMIT_FD_SETSIZE 60590926Snectar if (sock >= FD_SETSIZE) { 60690926Snectar ret = ERANGE; 607233294Sstas krb5_set_error_message(context, ret, 608233294Sstas "fd %d too large", sock); 609233294Sstas rk_closesocket (sock); 61090926Snectar goto out; 61190926Snectar } 612233294Sstas#endif 61390926Snectar 61490926Snectar FD_ZERO(&fdset); 61590926Snectar FD_SET(sock, &fdset); 61690926Snectar tv.tv_usec = 0; 61790926Snectar tv.tv_sec = 1 + (1 << i); 61890926Snectar 61990926Snectar ret = select (sock + 1, &fdset, NULL, NULL, &tv); 620233294Sstas if (rk_IS_SOCKET_ERROR(ret) && rk_SOCK_ERRNO != EINTR) { 621233294Sstas rk_closesocket(sock); 62290926Snectar goto out; 62390926Snectar } 62490926Snectar if (ret == 1) { 625142403Snectar ret = (*proc->process_rep) (context, 626142403Snectar auth_context, 627142403Snectar is_stream, 628142403Snectar sock, 629142403Snectar result_code, 630142403Snectar result_code_string, 631142403Snectar result_string, 632142403Snectar hi->hostname); 63390926Snectar if (ret == 0) 63490926Snectar done = 1; 63590926Snectar else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL) 63690926Snectar replied = 1; 63790926Snectar } else { 63890926Snectar ret = KRB5_KDC_UNREACH; 63990926Snectar } 64072445Sassar } 641233294Sstas rk_closesocket (sock); 64255682Smarkm } 64355682Smarkm } 64455682Smarkm 64590926Snectar out: 64690926Snectar krb5_krbhst_free (context, handle); 64755682Smarkm krb5_auth_con_free (context, auth_context); 648233294Sstas 649233294Sstas if (ret == KRB5_KDC_UNREACH) { 650233294Sstas krb5_set_error_message(context, 651233294Sstas ret, 652233294Sstas N_("Unable to reach any changepw server " 653233294Sstas " in realm %s", "realm"), realm); 654233294Sstas *result_code = KRB5_KPASSWD_HARDERROR; 65578527Sassar } 656233294Sstas return ret; 65755682Smarkm} 65890926Snectar 659233294Sstas#ifndef HEIMDAL_SMALLER 660142403Snectar 661233294Sstasstatic struct kpwd_proc * 662233294Sstasfind_chpw_proto(const char *name) 663233294Sstas{ 664233294Sstas struct kpwd_proc *p; 665233294Sstas for (p = procs; p->name != NULL; p++) { 666233294Sstas if (strcmp(p->name, name) == 0) 667233294Sstas return p; 668233294Sstas } 669233294Sstas return NULL; 670233294Sstas} 671233294Sstas 672233294Sstas/** 673233294Sstas * Deprecated: krb5_change_password() is deprecated, use krb5_set_password(). 674233294Sstas * 675233294Sstas * @param context a Keberos context 676233294Sstas * @param creds 677233294Sstas * @param newpw 678233294Sstas * @param result_code 679233294Sstas * @param result_code_string 680233294Sstas * @param result_string 681233294Sstas * 682233294Sstas * @return On sucess password is changed. 683233294Sstas 684233294Sstas * @ingroup @krb5_deprecated 685142403Snectar */ 686142403Snectar 687233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 688142403Snectarkrb5_change_password (krb5_context context, 689142403Snectar krb5_creds *creds, 690178825Sdfr const char *newpw, 691142403Snectar int *result_code, 692142403Snectar krb5_data *result_code_string, 693142403Snectar krb5_data *result_string) 694233294Sstas KRB5_DEPRECATED_FUNCTION("Use X instead") 695142403Snectar{ 696142403Snectar struct kpwd_proc *p = find_chpw_proto("change password"); 697142403Snectar 698142403Snectar *result_code = KRB5_KPASSWD_MALFORMED; 699142403Snectar result_code_string->data = result_string->data = NULL; 700142403Snectar result_code_string->length = result_string->length = 0; 701142403Snectar 702142403Snectar if (p == NULL) 703142403Snectar return KRB5_KPASSWD_MALFORMED; 704142403Snectar 705233294Sstas return change_password_loop(context, creds, NULL, newpw, 706233294Sstas result_code, result_code_string, 707142403Snectar result_string, p); 708142403Snectar} 709233294Sstas#endif /* HEIMDAL_SMALLER */ 710142403Snectar 711233294Sstas/** 712233294Sstas * Change password using creds. 713142403Snectar * 714233294Sstas * @param context a Keberos context 715233294Sstas * @param creds The initial kadmin/passwd for the principal or an admin principal 716233294Sstas * @param newpw The new password to set 717233294Sstas * @param targprinc if unset, the default principal is used. 718233294Sstas * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed. 719233294Sstas * @param result_code_string binary message from the server, contains 720233294Sstas * at least the result_code. 721233294Sstas * @param result_string A message from the kpasswd service or the 722233294Sstas * library in human printable form. The string is NUL terminated. 723233294Sstas * 724233294Sstas * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed. 725233294Sstas 726233294Sstas * @ingroup @krb5 727142403Snectar */ 728142403Snectar 729233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 730142403Snectarkrb5_set_password(krb5_context context, 731142403Snectar krb5_creds *creds, 732178825Sdfr const char *newpw, 733142403Snectar krb5_principal targprinc, 734142403Snectar int *result_code, 735142403Snectar krb5_data *result_code_string, 736142403Snectar krb5_data *result_string) 737142403Snectar{ 738142403Snectar krb5_principal principal = NULL; 739142403Snectar krb5_error_code ret = 0; 740142403Snectar int i; 741142403Snectar 742142403Snectar *result_code = KRB5_KPASSWD_MALFORMED; 743233294Sstas krb5_data_zero(result_code_string); 744233294Sstas krb5_data_zero(result_string); 745142403Snectar 746142403Snectar if (targprinc == NULL) { 747142403Snectar ret = krb5_get_default_principal(context, &principal); 748142403Snectar if (ret) 749142403Snectar return ret; 750142403Snectar } else 751142403Snectar principal = targprinc; 752142403Snectar 753142403Snectar for (i = 0; procs[i].name != NULL; i++) { 754142403Snectar *result_code = 0; 755233294Sstas ret = change_password_loop(context, creds, principal, newpw, 756233294Sstas result_code, result_code_string, 757233294Sstas result_string, 758142403Snectar &procs[i]); 759142403Snectar if (ret == 0 && *result_code == 0) 760142403Snectar break; 761142403Snectar } 762142403Snectar 763142403Snectar if (targprinc == NULL) 764142403Snectar krb5_free_principal(context, principal); 765142403Snectar return ret; 766142403Snectar} 767142403Snectar 768142403Snectar/* 769142403Snectar * 770142403Snectar */ 771142403Snectar 772233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 773142403Snectarkrb5_set_password_using_ccache(krb5_context context, 774142403Snectar krb5_ccache ccache, 775178825Sdfr const char *newpw, 776142403Snectar krb5_principal targprinc, 777142403Snectar int *result_code, 778142403Snectar krb5_data *result_code_string, 779142403Snectar krb5_data *result_string) 780142403Snectar{ 781142403Snectar krb5_creds creds, *credsp; 782142403Snectar krb5_error_code ret; 783142403Snectar krb5_principal principal = NULL; 784142403Snectar 785142403Snectar *result_code = KRB5_KPASSWD_MALFORMED; 786142403Snectar result_code_string->data = result_string->data = NULL; 787142403Snectar result_code_string->length = result_string->length = 0; 788142403Snectar 789142403Snectar memset(&creds, 0, sizeof(creds)); 790142403Snectar 791142403Snectar if (targprinc == NULL) { 792142403Snectar ret = krb5_cc_get_principal(context, ccache, &principal); 793142403Snectar if (ret) 794142403Snectar return ret; 795142403Snectar } else 796142403Snectar principal = targprinc; 797142403Snectar 798233294Sstas ret = krb5_make_principal(context, &creds.server, 799142403Snectar krb5_principal_get_realm(context, principal), 800142403Snectar "kadmin", "changepw", NULL); 801142403Snectar if (ret) 802142403Snectar goto out; 803142403Snectar 804142403Snectar ret = krb5_cc_get_principal(context, ccache, &creds.client); 805142403Snectar if (ret) { 806142403Snectar krb5_free_principal(context, creds.server); 807142403Snectar goto out; 808142403Snectar } 809142403Snectar 810142403Snectar ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 811142403Snectar krb5_free_principal(context, creds.server); 812142403Snectar krb5_free_principal(context, creds.client); 813142403Snectar if (ret) 814142403Snectar goto out; 815142403Snectar 816142403Snectar ret = krb5_set_password(context, 817142403Snectar credsp, 818142403Snectar newpw, 819142403Snectar principal, 820142403Snectar result_code, 821142403Snectar result_code_string, 822142403Snectar result_string); 823142403Snectar 824233294Sstas krb5_free_creds(context, credsp); 825142403Snectar 826142403Snectar return ret; 827142403Snectar out: 828142403Snectar if (targprinc == NULL) 829142403Snectar krb5_free_principal(context, principal); 830142403Snectar return ret; 831142403Snectar} 832142403Snectar 833142403Snectar/* 834142403Snectar * 835142403Snectar */ 836142403Snectar 837233294SstasKRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 83890926Snectarkrb5_passwd_result_to_string (krb5_context context, 83990926Snectar int result) 84090926Snectar{ 84190926Snectar static const char *strings[] = { 84290926Snectar "Success", 84390926Snectar "Malformed", 84490926Snectar "Hard error", 84590926Snectar "Auth error", 846142403Snectar "Soft error" , 847142403Snectar "Access denied", 848142403Snectar "Bad version", 849142403Snectar "Initial flag needed" 85090926Snectar }; 85190926Snectar 852142403Snectar if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) 85390926Snectar return "unknown result code"; 85490926Snectar else 85590926Snectar return strings[result]; 85690926Snectar} 857