1 /* 2 * lib/krb5/os/changepw.c 3 * 4 * Copyright 1990,1999,2001 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 * 26 */ 27 /* 28 * krb5_set_password - Implements set password per RFC 3244 29 * Added by Paul W. Nelson, Thursby Software Systems, Inc. 30 * Modified by Todd Stecher, Isilon Systems, to use krb1.4 socket infrastructure 31 */ 32 33 #include "fake-addrinfo.h" 34 #include "k5-int.h" 35 #include "os-proto.h" 36 #include "cm.h" 37 38 #include <stdio.h> 39 #include <errno.h> 40 41 #ifndef GETSOCKNAME_ARG3_TYPE 42 #define GETSOCKNAME_ARG3_TYPE int 43 #endif 44 45 struct sendto_callback_context { 46 krb5_context context; 47 krb5_auth_context auth_context; 48 krb5_principal set_password_for; 49 char *newpw; 50 krb5_data ap_req; 51 }; 52 53 54 /* 55 * Wrapper function for the two backends 56 */ 57 58 static krb5_error_code 59 krb5_locate_kpasswd(krb5_context context, const krb5_data *realm, 60 struct addrlist *addrlist, krb5_boolean useTcp) 61 { 62 krb5_error_code code; 63 int sockType = (useTcp ? SOCK_STREAM : SOCK_DGRAM); 64 65 code = krb5int_locate_server (context, realm, addrlist, 66 locate_service_kpasswd, sockType, 0); 67 68 if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) { 69 code = krb5int_locate_server (context, realm, addrlist, 70 locate_service_kadmin, SOCK_STREAM, 0); 71 if (!code) { 72 /* Success with admin_server but now we need to change the 73 port number to use DEFAULT_KPASSWD_PORT and the socktype. */ 74 int i; 75 for (i=0; i<addrlist->naddrs; i++) { 76 struct addrinfo *a = addrlist->addrs[i].ai; 77 if (a->ai_family == AF_INET) 78 sa2sin (a->ai_addr)->sin_port = htons(DEFAULT_KPASSWD_PORT); 79 if (sockType != SOCK_STREAM) 80 a->ai_socktype = sockType; 81 } 82 } 83 } 84 return (code); 85 } 86 87 88 /** 89 * This routine is used for a callback in sendto_kdc.c code. Simply 90 * put, we need the client addr to build the krb_priv portion of the 91 * password request. 92 */ 93 94 95 static void kpasswd_sendto_msg_cleanup (void* callback_context, krb5_data* message) 96 { 97 struct sendto_callback_context *ctx = callback_context; 98 krb5_free_data_contents(ctx->context, message); 99 } 100 101 102 static int kpasswd_sendto_msg_callback(struct conn_state *conn, void *callback_context, krb5_data* message) 103 { 104 krb5_error_code code = 0; 105 struct sockaddr_storage local_addr; 106 krb5_address local_kaddr; 107 struct sendto_callback_context *ctx = callback_context; 108 GETSOCKNAME_ARG3_TYPE addrlen; 109 krb5_data output; 110 111 memset (message, 0, sizeof(krb5_data)); 112 113 /* 114 * We need the local addr from the connection socket 115 */ 116 addrlen = sizeof(local_addr); 117 118 if (getsockname(conn->fd, ss2sa(&local_addr), &addrlen) < 0) { 119 code = SOCKET_ERRNO; 120 goto cleanup; 121 } 122 123 /* some brain-dead OS's don't return useful information from 124 * the getsockname call. Namely, windows and solaris. */ 125 126 if (ss2sin(&local_addr)->sin_addr.s_addr != 0) { 127 local_kaddr.addrtype = ADDRTYPE_INET; 128 local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr); 129 local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr; 130 } else { 131 krb5_address **addrs; 132 133 code = krb5_os_localaddr(ctx->context, &addrs); 134 if (code) 135 goto cleanup; 136 137 local_kaddr.magic = addrs[0]->magic; 138 local_kaddr.addrtype = addrs[0]->addrtype; 139 local_kaddr.length = addrs[0]->length; 140 local_kaddr.contents = malloc(addrs[0]->length); 141 if (local_kaddr.contents == NULL && addrs[0]->length != 0) { 142 code = errno; 143 krb5_free_addresses(ctx->context, addrs); 144 goto cleanup; 145 } 146 memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); 147 148 krb5_free_addresses(ctx->context, addrs); 149 } 150 151 152 /* 153 * TBD: Does this tamper w/ the auth context in such a way 154 * to break us? Yes - provide 1 per conn-state / host... 155 */ 156 157 158 if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context, 159 &local_kaddr, NULL))) 160 goto cleanup; 161 162 if (ctx->set_password_for) 163 code = krb5int_mk_setpw_req(ctx->context, 164 ctx->auth_context, 165 &ctx->ap_req, 166 ctx->set_password_for, 167 ctx->newpw, 168 &output); 169 else 170 code = krb5int_mk_chpw_req(ctx->context, 171 ctx->auth_context, 172 &ctx->ap_req, 173 ctx->newpw, 174 &output); 175 if (code) 176 goto cleanup; 177 178 message->length = output.length; 179 message->data = output.data; 180 181 cleanup: 182 return code; 183 } 184 185 186 /* 187 ** The logic for setting and changing a password is mostly the same 188 ** krb5_change_set_password handles both cases 189 ** if set_password_for is NULL, then a password change is performed, 190 ** otherwise, the password is set for the principal indicated in set_password_for 191 */ 192 krb5_error_code KRB5_CALLCONV 193 krb5_change_set_password(krb5_context context, krb5_creds *creds, char *newpw, 194 krb5_principal set_password_for, 195 int *result_code, krb5_data *result_code_string, 196 krb5_data *result_string) 197 { 198 krb5_data chpw_rep; 199 krb5_address remote_kaddr; 200 krb5_boolean useTcp = 0; 201 GETSOCKNAME_ARG3_TYPE addrlen; 202 krb5_error_code code = 0; 203 char *code_string; 204 int local_result_code; 205 206 struct sendto_callback_context callback_ctx; 207 struct sendto_callback_info callback_info; 208 struct sockaddr_storage remote_addr; 209 struct addrlist al = ADDRLIST_INIT; 210 211 memset( &callback_ctx, 0, sizeof(struct sendto_callback_context)); 212 callback_ctx.context = context; 213 callback_ctx.newpw = newpw; 214 callback_ctx.set_password_for = set_password_for; 215 216 if ((code = krb5_auth_con_init(callback_ctx.context, 217 &callback_ctx.auth_context))) 218 goto cleanup; 219 220 if ((code = krb5_mk_req_extended(callback_ctx.context, 221 &callback_ctx.auth_context, 222 AP_OPTS_USE_SUBKEY, 223 NULL, 224 creds, 225 &callback_ctx.ap_req))) 226 goto cleanup; 227 228 do { 229 if ((code = krb5_locate_kpasswd(callback_ctx.context, 230 krb5_princ_realm(callback_ctx.context, 231 creds->server), 232 &al, useTcp))) 233 break; 234 235 addrlen = sizeof(remote_addr); 236 237 callback_info.context = (void*) &callback_ctx; 238 callback_info.pfn_callback = kpasswd_sendto_msg_callback; 239 callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; 240 241 if ((code = krb5int_sendto(callback_ctx.context, 242 NULL, 243 &al, 244 &callback_info, 245 &chpw_rep, 246 NULL, 247 NULL, 248 ss2sa(&remote_addr), 249 &addrlen, 250 NULL, 251 NULL, 252 NULL 253 ))) { 254 255 /* 256 * Here we may want to switch to TCP on some errors. 257 * right? 258 */ 259 break; 260 } 261 262 remote_kaddr.addrtype = ADDRTYPE_INET; 263 remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); 264 remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; 265 266 if ((code = krb5_auth_con_setaddrs(callback_ctx.context, 267 callback_ctx.auth_context, 268 NULL, 269 &remote_kaddr))) 270 break; 271 272 if (set_password_for) 273 code = krb5int_rd_setpw_rep(callback_ctx.context, 274 callback_ctx.auth_context, 275 &chpw_rep, 276 &local_result_code, 277 result_string); 278 else 279 code = krb5int_rd_chpw_rep(callback_ctx.context, 280 callback_ctx.auth_context, 281 &chpw_rep, 282 &local_result_code, 283 result_string); 284 285 if (code) { 286 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) { 287 krb5int_free_addrlist (&al); 288 useTcp = 1; 289 continue; 290 } 291 292 break; 293 } 294 295 if (result_code) 296 *result_code = local_result_code; 297 298 if (result_code_string) { 299 if (set_password_for) 300 code = krb5int_setpw_result_code_string(callback_ctx.context, 301 local_result_code, 302 (const char **)&code_string); 303 else 304 code = krb5_chpw_result_code_string(callback_ctx.context, 305 local_result_code, 306 &code_string); 307 if(code) 308 goto cleanup; 309 310 result_code_string->length = strlen(code_string); 311 result_code_string->data = malloc(result_code_string->length); 312 if (result_code_string->data == NULL) { 313 code = ENOMEM; 314 goto cleanup; 315 } 316 strncpy(result_code_string->data, code_string, result_code_string->length); 317 } 318 319 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) { 320 krb5int_free_addrlist (&al); 321 useTcp = 1; 322 } else { 323 break; 324 } 325 } while (TRUE); 326 327 cleanup: 328 if (callback_ctx.auth_context != NULL) 329 krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context); 330 331 krb5int_free_addrlist (&al); 332 krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req); 333 334 return(code); 335 } 336 337 krb5_error_code KRB5_CALLCONV 338 krb5_change_password(krb5_context context, krb5_creds *creds, char *newpw, int *result_code, krb5_data *result_code_string, krb5_data *result_string) 339 { 340 return krb5_change_set_password( 341 context, creds, newpw, NULL, result_code, result_code_string, result_string ); 342 } 343 344 /* 345 * krb5_set_password - Implements set password per RFC 3244 346 * 347 */ 348 349 krb5_error_code KRB5_CALLCONV 350 krb5_set_password( 351 krb5_context context, 352 krb5_creds *creds, 353 char *newpw, 354 krb5_principal change_password_for, 355 int *result_code, krb5_data *result_code_string, krb5_data *result_string 356 ) 357 { 358 return krb5_change_set_password( 359 context, creds, newpw, change_password_for, result_code, result_code_string, result_string ); 360 } 361 362 krb5_error_code KRB5_CALLCONV 363 krb5_set_password_using_ccache( 364 krb5_context context, 365 krb5_ccache ccache, 366 char *newpw, 367 krb5_principal change_password_for, 368 int *result_code, krb5_data *result_code_string, krb5_data *result_string 369 ) 370 { 371 krb5_creds creds; 372 krb5_creds *credsp; 373 krb5_error_code code; 374 375 /* 376 ** get the proper creds for use with krb5_set_password - 377 */ 378 memset (&creds, 0, sizeof(creds)); 379 /* 380 ** first get the principal for the password service - 381 */ 382 code = krb5_cc_get_principal (context, ccache, &creds.client); 383 if (!code) { 384 code = krb5_build_principal(context, &creds.server, 385 krb5_princ_realm(context, change_password_for)->length, 386 krb5_princ_realm(context, change_password_for)->data, 387 "kadmin", "changepw", NULL); 388 if (!code) { 389 code = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 390 if (!code) { 391 code = krb5_set_password(context, credsp, newpw, change_password_for, 392 result_code, result_code_string, 393 result_string); 394 krb5_free_creds(context, credsp); 395 } 396 } 397 krb5_free_cred_contents(context, &creds); 398 } 399 return code; 400 } 401