1 /* 2 * Copyright (c) 1997 - 2002 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <krb5_locl.h> 35 36 RCSID("$Id: changepw.c,v 1.38 2002/09/29 11:48:34 joda Exp $"); 37 38 static krb5_error_code 39 send_request (krb5_context context, 40 krb5_auth_context *auth_context, 41 krb5_creds *creds, 42 int sock, 43 char *passwd, 44 const char *host) 45 { 46 krb5_error_code ret; 47 krb5_data ap_req_data; 48 krb5_data krb_priv_data; 49 krb5_data passwd_data; 50 size_t len; 51 u_char header[6]; 52 u_char *p; 53 struct iovec iov[3]; 54 struct msghdr msghdr; 55 56 krb5_data_zero (&ap_req_data); 57 58 ret = krb5_mk_req_extended (context, 59 auth_context, 60 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 61 NULL, /* in_data */ 62 creds, 63 &ap_req_data); 64 if (ret) 65 return ret; 66 67 passwd_data.data = passwd; 68 passwd_data.length = strlen(passwd); 69 70 krb5_data_zero (&krb_priv_data); 71 72 ret = krb5_mk_priv (context, 73 *auth_context, 74 &passwd_data, 75 &krb_priv_data, 76 NULL); 77 if (ret) 78 goto out2; 79 80 len = 6 + ap_req_data.length + krb_priv_data.length; 81 p = header; 82 *p++ = (len >> 8) & 0xFF; 83 *p++ = (len >> 0) & 0xFF; 84 *p++ = 0; 85 *p++ = 1; 86 *p++ = (ap_req_data.length >> 8) & 0xFF; 87 *p++ = (ap_req_data.length >> 0) & 0xFF; 88 89 memset(&msghdr, 0, sizeof(msghdr)); 90 msghdr.msg_name = NULL; 91 msghdr.msg_namelen = 0; 92 msghdr.msg_iov = iov; 93 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); 94 #if 0 95 msghdr.msg_control = NULL; 96 msghdr.msg_controllen = 0; 97 #endif 98 99 iov[0].iov_base = (void*)header; 100 iov[0].iov_len = 6; 101 iov[1].iov_base = ap_req_data.data; 102 iov[1].iov_len = ap_req_data.length; 103 iov[2].iov_base = krb_priv_data.data; 104 iov[2].iov_len = krb_priv_data.length; 105 106 if (sendmsg (sock, &msghdr, 0) < 0) { 107 ret = errno; 108 krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret)); 109 } 110 111 krb5_data_free (&krb_priv_data); 112 out2: 113 krb5_data_free (&ap_req_data); 114 return ret; 115 } 116 117 static void 118 str2data (krb5_data *d, 119 const char *fmt, 120 ...) __attribute__ ((format (printf, 2, 3))); 121 122 static void 123 str2data (krb5_data *d, 124 const char *fmt, 125 ...) 126 { 127 va_list args; 128 129 va_start(args, fmt); 130 d->length = vasprintf ((char **)&d->data, fmt, args); 131 va_end(args); 132 } 133 134 static krb5_error_code 135 process_reply (krb5_context context, 136 krb5_auth_context auth_context, 137 int sock, 138 int *result_code, 139 krb5_data *result_code_string, 140 krb5_data *result_string, 141 const char *host) 142 { 143 krb5_error_code ret; 144 u_char reply[BUFSIZ]; 145 size_t len; 146 u_int16_t pkt_len, pkt_ver; 147 krb5_data ap_rep_data, priv_data; 148 int save_errno; 149 150 ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); 151 if (ret < 0) { 152 save_errno = errno; 153 krb5_set_error_string(context, "recvfrom %s: %s", 154 host, strerror(save_errno)); 155 return save_errno; 156 } 157 158 len = ret; 159 pkt_len = (reply[0] << 8) | (reply[1]); 160 pkt_ver = (reply[2] << 8) | (reply[3]); 161 162 if (pkt_len != len) { 163 str2data (result_string, "client: wrong len in reply"); 164 *result_code = KRB5_KPASSWD_MALFORMED; 165 return 0; 166 } 167 if (pkt_ver != 0x0001) { 168 str2data (result_string, 169 "client: wrong version number (%d)", pkt_ver); 170 *result_code = KRB5_KPASSWD_MALFORMED; 171 return 0; 172 } 173 174 ap_rep_data.data = reply + 6; 175 ap_rep_data.length = (reply[4] << 8) | (reply[5]); 176 priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; 177 priv_data.length = len - ap_rep_data.length - 6; 178 if ((u_char *)priv_data.data + priv_data.length > reply + len) 179 return KRB5_KPASSWD_MALFORMED; 180 181 if (ap_rep_data.length) { 182 krb5_ap_rep_enc_part *ap_rep; 183 u_char *p; 184 185 ret = krb5_rd_rep (context, 186 auth_context, 187 &ap_rep_data, 188 &ap_rep); 189 if (ret) 190 return ret; 191 192 krb5_free_ap_rep_enc_part (context, ap_rep); 193 194 ret = krb5_rd_priv (context, 195 auth_context, 196 &priv_data, 197 result_code_string, 198 NULL); 199 if (ret) { 200 krb5_data_free (result_code_string); 201 return ret; 202 } 203 204 if (result_code_string->length < 2) { 205 *result_code = KRB5_KPASSWD_MALFORMED; 206 str2data (result_string, 207 "client: bad length in result"); 208 return 0; 209 } 210 p = result_code_string->data; 211 212 *result_code = (p[0] << 8) | p[1]; 213 krb5_data_copy (result_string, 214 (unsigned char*)result_code_string->data + 2, 215 result_code_string->length - 2); 216 return 0; 217 } else { 218 KRB_ERROR error; 219 size_t size; 220 u_char *p; 221 222 ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); 223 if (ret) { 224 return ret; 225 } 226 if (error.e_data->length < 2) { 227 krb5_warnx (context, "too short e_data to print anything usable"); 228 return 1; /* XXX */ 229 } 230 231 p = error.e_data->data; 232 *result_code = (p[0] << 8) | p[1]; 233 krb5_data_copy (result_string, 234 p + 2, 235 error.e_data->length - 2); 236 return 0; 237 } 238 } 239 240 /* 241 * change the password using the credentials in `creds' (for the 242 * principal indicated in them) to `newpw', storing the result of 243 * the operation in `result_*' and an error code or 0. 244 */ 245 246 krb5_error_code 247 krb5_change_password (krb5_context context, 248 krb5_creds *creds, 249 char *newpw, 250 int *result_code, 251 krb5_data *result_code_string, 252 krb5_data *result_string) 253 { 254 krb5_error_code ret; 255 krb5_auth_context auth_context = NULL; 256 krb5_krbhst_handle handle = NULL; 257 krb5_krbhst_info *hi; 258 int sock; 259 int i; 260 int done = 0; 261 krb5_realm realm = creds->client->realm; 262 263 ret = krb5_auth_con_init (context, &auth_context); 264 if (ret) 265 return ret; 266 267 krb5_auth_con_setflags (context, auth_context, 268 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 269 270 ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle); 271 if (ret) 272 goto out; 273 274 while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) { 275 struct addrinfo *ai, *a; 276 277 ret = krb5_krbhst_get_addrinfo(context, hi, &ai); 278 if (ret) 279 continue; 280 281 for (a = ai; !done && a != NULL; a = a->ai_next) { 282 int replied = 0; 283 284 sock = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 285 if (sock < 0) 286 continue; 287 288 ret = connect(sock, a->ai_addr, a->ai_addrlen); 289 if (ret < 0) { 290 close (sock); 291 goto out; 292 } 293 294 ret = krb5_auth_con_genaddrs (context, auth_context, sock, 295 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); 296 if (ret) { 297 close (sock); 298 goto out; 299 } 300 301 for (i = 0; !done && i < 5; ++i) { 302 fd_set fdset; 303 struct timeval tv; 304 305 if (!replied) { 306 replied = 0; 307 ret = send_request (context, 308 &auth_context, 309 creds, 310 sock, 311 newpw, 312 hi->hostname); 313 if (ret) { 314 close(sock); 315 goto out; 316 } 317 } 318 319 if (sock >= FD_SETSIZE) { 320 krb5_set_error_string(context, "fd %d too large", sock); 321 ret = ERANGE; 322 close (sock); 323 goto out; 324 } 325 326 FD_ZERO(&fdset); 327 FD_SET(sock, &fdset); 328 tv.tv_usec = 0; 329 tv.tv_sec = 1 + (1 << i); 330 331 ret = select (sock + 1, &fdset, NULL, NULL, &tv); 332 if (ret < 0 && errno != EINTR) { 333 close(sock); 334 goto out; 335 } 336 if (ret == 1) { 337 ret = process_reply (context, 338 auth_context, 339 sock, 340 result_code, 341 result_code_string, 342 result_string, 343 hi->hostname); 344 if (ret == 0) 345 done = 1; 346 else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL) 347 replied = 1; 348 } else { 349 ret = KRB5_KDC_UNREACH; 350 } 351 } 352 close (sock); 353 } 354 } 355 356 out: 357 krb5_krbhst_free (context, handle); 358 krb5_auth_con_free (context, auth_context); 359 if (done) 360 return 0; 361 else { 362 if (ret == KRB5_KDC_UNREACH) 363 krb5_set_error_string(context, 364 "unable to reach any changepw server " 365 " in realm %s", realm); 366 return ret; 367 } 368 } 369 370 const char * 371 krb5_passwd_result_to_string (krb5_context context, 372 int result) 373 { 374 static const char *strings[] = { 375 "Success", 376 "Malformed", 377 "Hard error", 378 "Auth error", 379 "Soft error" 380 }; 381 382 if (result < 0 || result > KRB5_KPASSWD_SOFTERROR) 383 return "unknown result code"; 384 else 385 return strings[result]; 386 } 387