1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 1998-2001 by Sun Microsystems, Inc. 3*0Sstevel@tonic-gate * All rights reserved. 4*0Sstevel@tonic-gate */ 5*0Sstevel@tonic-gate 6*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 7*0Sstevel@tonic-gate 8*0Sstevel@tonic-gate /* 9*0Sstevel@tonic-gate * lib/krb5/os/changepw.c 10*0Sstevel@tonic-gate * 11*0Sstevel@tonic-gate * Copyright 1990,1999 by the Massachusetts Institute of Technology. 12*0Sstevel@tonic-gate * All Rights Reserved. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * Export of this software from the United States of America may 15*0Sstevel@tonic-gate * require a specific license from the United States Government. 16*0Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 17*0Sstevel@tonic-gate * export to obtain such a license before exporting. 18*0Sstevel@tonic-gate * 19*0Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20*0Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 21*0Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 22*0Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 23*0Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 24*0Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 25*0Sstevel@tonic-gate * to distribution of the software without specific, written prior 26*0Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 27*0Sstevel@tonic-gate * your software as modified software and not distribute it in such a 28*0Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 29*0Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 30*0Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 31*0Sstevel@tonic-gate * or implied warranty. 32*0Sstevel@tonic-gate * 33*0Sstevel@tonic-gate */ 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #define NEED_SOCKETS 36*0Sstevel@tonic-gate #include <krb5.h> 37*0Sstevel@tonic-gate #include <k5-int.h> 38*0Sstevel@tonic-gate #include <kadm5/admin.h> 39*0Sstevel@tonic-gate #include <client_internal.h> 40*0Sstevel@tonic-gate #include <gssapi/gssapi.h> 41*0Sstevel@tonic-gate #include <gssapi_krb5.h> 42*0Sstevel@tonic-gate #include <gssapiP_krb5.h> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate /* #include "adm_err.h" */ 45*0Sstevel@tonic-gate #include <stdio.h> 46*0Sstevel@tonic-gate #include <errno.h> 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate extern krb5_error_code krb5_mk_chpw_req(krb5_context context, 49*0Sstevel@tonic-gate krb5_auth_context auth_context, 50*0Sstevel@tonic-gate krb5_data *ap_req, char *passwd, 51*0Sstevel@tonic-gate krb5_data *packet); 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate extern krb5_error_code krb5_rd_chpw_rep(krb5_context context, 54*0Sstevel@tonic-gate krb5_auth_context auth_context, 55*0Sstevel@tonic-gate krb5_data *packet, int *result_code, 56*0Sstevel@tonic-gate krb5_data *result_data); 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate /* 59*0Sstevel@tonic-gate * _kadm5_get_kpasswd_protocol 60*0Sstevel@tonic-gate * 61*0Sstevel@tonic-gate * returns the password change protocol value to the caller. 62*0Sstevel@tonic-gate * Since the 'handle' is an opaque value to higher up callers, 63*0Sstevel@tonic-gate * this method is needed to provide a way for them to get a peek 64*0Sstevel@tonic-gate * at the protocol being used without having to expose the entire 65*0Sstevel@tonic-gate * handle structure. 66*0Sstevel@tonic-gate */ 67*0Sstevel@tonic-gate krb5_chgpwd_prot 68*0Sstevel@tonic-gate _kadm5_get_kpasswd_protocol(void *handle) 69*0Sstevel@tonic-gate { 70*0Sstevel@tonic-gate kadm5_server_handle_t srvrhdl = (kadm5_server_handle_t)handle; 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate return (srvrhdl->params.kpasswd_protocol); 73*0Sstevel@tonic-gate } 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate /* 76*0Sstevel@tonic-gate * krb5_change_password 77*0Sstevel@tonic-gate * 78*0Sstevel@tonic-gate * Prepare and send a CHANGEPW request to a password server 79*0Sstevel@tonic-gate * using UDP datagrams. This is only used for sending to 80*0Sstevel@tonic-gate * non-SEAM servers which support the Marc Horowitz defined 81*0Sstevel@tonic-gate * protocol (1998) for password changing. 82*0Sstevel@tonic-gate * 83*0Sstevel@tonic-gate */ 84*0Sstevel@tonic-gate static krb5_error_code 85*0Sstevel@tonic-gate krb5_change_password(context, params, creds, newpw, srvr_rsp_code, 86*0Sstevel@tonic-gate srvr_msg) 87*0Sstevel@tonic-gate krb5_context context; 88*0Sstevel@tonic-gate kadm5_config_params *params; 89*0Sstevel@tonic-gate krb5_creds *creds; 90*0Sstevel@tonic-gate char *newpw; 91*0Sstevel@tonic-gate kadm5_ret_t *srvr_rsp_code; 92*0Sstevel@tonic-gate krb5_data *srvr_msg; 93*0Sstevel@tonic-gate { 94*0Sstevel@tonic-gate krb5_auth_context auth_context; 95*0Sstevel@tonic-gate krb5_data ap_req, chpw_req, chpw_rep; 96*0Sstevel@tonic-gate krb5_address local_kaddr, remote_kaddr; 97*0Sstevel@tonic-gate krb5_error_code code = 0; 98*0Sstevel@tonic-gate int i, addrlen; 99*0Sstevel@tonic-gate struct sockaddr *addr_p, local_addr, remote_addr, tmp_addr; 100*0Sstevel@tonic-gate struct sockaddr_in *sin_p; 101*0Sstevel@tonic-gate struct hostent *hp; 102*0Sstevel@tonic-gate int naddr_p; 103*0Sstevel@tonic-gate int cc, local_result_code, tmp_len; 104*0Sstevel@tonic-gate SOCKET s1 = INVALID_SOCKET; 105*0Sstevel@tonic-gate SOCKET s2 = INVALID_SOCKET; 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate /* Initialize values so that cleanup call can safely check for NULL */ 109*0Sstevel@tonic-gate auth_context = NULL; 110*0Sstevel@tonic-gate addr_p = NULL; 111*0Sstevel@tonic-gate memset(&chpw_req, 0, sizeof (krb5_data)); 112*0Sstevel@tonic-gate memset(&chpw_rep, 0, sizeof (krb5_data)); 113*0Sstevel@tonic-gate memset(&ap_req, 0, sizeof (krb5_data)); 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate /* initialize auth_context so that we know we have to free it */ 116*0Sstevel@tonic-gate if ((code = krb5_auth_con_init(context, &auth_context))) 117*0Sstevel@tonic-gate goto cleanup; 118*0Sstevel@tonic-gate 119*0Sstevel@tonic-gate if (code = krb5_mk_req_extended(context, &auth_context, 120*0Sstevel@tonic-gate AP_OPTS_USE_SUBKEY, 121*0Sstevel@tonic-gate NULL, creds, &ap_req)) 122*0Sstevel@tonic-gate goto cleanup; 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate /* 125*0Sstevel@tonic-gate * find the address of the kpasswd_server. 126*0Sstevel@tonic-gate */ 127*0Sstevel@tonic-gate addr_p = (struct sockaddr *)malloc(sizeof (struct sockaddr)); 128*0Sstevel@tonic-gate if (!addr_p) 129*0Sstevel@tonic-gate goto cleanup; 130*0Sstevel@tonic-gate memset(addr_p, 0, sizeof (struct sockaddr)); 131*0Sstevel@tonic-gate if ((hp = gethostbyname(params->kpasswd_server)) == NULL) { 132*0Sstevel@tonic-gate code = KRB5_REALM_CANT_RESOLVE; 133*0Sstevel@tonic-gate goto cleanup; 134*0Sstevel@tonic-gate } 135*0Sstevel@tonic-gate sin_p = (struct sockaddr_in *)addr_p; 136*0Sstevel@tonic-gate memset((char *)sin_p, 0, sizeof (struct sockaddr)); 137*0Sstevel@tonic-gate sin_p->sin_family = hp->h_addrtype; 138*0Sstevel@tonic-gate sin_p->sin_port = htons(params->kpasswd_port); 139*0Sstevel@tonic-gate memcpy((char *)&sin_p->sin_addr, (char *)hp->h_addr, hp->h_length); 140*0Sstevel@tonic-gate naddr_p = 1; 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate /* 144*0Sstevel@tonic-gate * this is really obscure. s1 is used for all communications. it 145*0Sstevel@tonic-gate * is left unconnected in case the server is multihomed and routes 146*0Sstevel@tonic-gate * are asymmetric. s2 is connected to resolve routes and get 147*0Sstevel@tonic-gate * addresses. this is the *only* way to get proper addresses for 148*0Sstevel@tonic-gate * multihomed hosts if routing is asymmetric. 149*0Sstevel@tonic-gate * 150*0Sstevel@tonic-gate * A related problem in the server, but not the client, is that 151*0Sstevel@tonic-gate * many os's have no way to disconnect a connected udp socket, so 152*0Sstevel@tonic-gate * the s2 socket needs to be closed and recreated for each 153*0Sstevel@tonic-gate * request. The s1 socket must not be closed, or else queued 154*0Sstevel@tonic-gate * requests will be lost. 155*0Sstevel@tonic-gate * 156*0Sstevel@tonic-gate * A "naive" client implementation (one socket, no connect, 157*0Sstevel@tonic-gate * hostname resolution to get the local ip addr) will work and 158*0Sstevel@tonic-gate * interoperate if the client is single-homed. 159*0Sstevel@tonic-gate */ 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) 162*0Sstevel@tonic-gate { 163*0Sstevel@tonic-gate code = errno; 164*0Sstevel@tonic-gate goto cleanup; 165*0Sstevel@tonic-gate } 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) 168*0Sstevel@tonic-gate { 169*0Sstevel@tonic-gate code = errno; 170*0Sstevel@tonic-gate goto cleanup; 171*0Sstevel@tonic-gate } 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate for (i = 0; i < naddr_p; i++) 174*0Sstevel@tonic-gate { 175*0Sstevel@tonic-gate fd_set fdset; 176*0Sstevel@tonic-gate struct timeval timeout; 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate if (connect(s2, &addr_p[i], sizeof (addr_p[i])) == 179*0Sstevel@tonic-gate SOCKET_ERROR) 180*0Sstevel@tonic-gate { 181*0Sstevel@tonic-gate if ((errno == ECONNREFUSED) || 182*0Sstevel@tonic-gate (errno == EHOSTUNREACH)) 183*0Sstevel@tonic-gate continue; /* try the next addr */ 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gate code = errno; 186*0Sstevel@tonic-gate goto cleanup; 187*0Sstevel@tonic-gate } 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate addrlen = sizeof (local_addr); 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate if (getsockname(s2, &local_addr, &addrlen) < 0) 192*0Sstevel@tonic-gate { 193*0Sstevel@tonic-gate if ((errno == ECONNREFUSED) || 194*0Sstevel@tonic-gate (errno == EHOSTUNREACH)) 195*0Sstevel@tonic-gate continue; /* try the next addr */ 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate code = errno; 198*0Sstevel@tonic-gate goto cleanup; 199*0Sstevel@tonic-gate } 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate /* 202*0Sstevel@tonic-gate * some brain-dead OS's don't return useful information from 203*0Sstevel@tonic-gate * the getsockname call. Namely, windows and solaris. 204*0Sstevel@tonic-gate */ 205*0Sstevel@tonic-gate if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) 206*0Sstevel@tonic-gate { 207*0Sstevel@tonic-gate local_kaddr.addrtype = ADDRTYPE_INET; 208*0Sstevel@tonic-gate local_kaddr.length = sizeof (((struct sockaddr_in *) 209*0Sstevel@tonic-gate &local_addr)->sin_addr); 210*0Sstevel@tonic-gate local_kaddr.contents = (krb5_octet *) 211*0Sstevel@tonic-gate &(((struct sockaddr_in *) 212*0Sstevel@tonic-gate &local_addr)->sin_addr); 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate else 215*0Sstevel@tonic-gate { 216*0Sstevel@tonic-gate krb5_address **addrs; 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate krb5_os_localaddr(context, &addrs); 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate local_kaddr.magic = addrs[0]->magic; 221*0Sstevel@tonic-gate local_kaddr.addrtype = addrs[0]->addrtype; 222*0Sstevel@tonic-gate local_kaddr.length = addrs[0]->length; 223*0Sstevel@tonic-gate local_kaddr.contents = malloc(addrs[0]->length); 224*0Sstevel@tonic-gate memcpy(local_kaddr.contents, addrs[0]->contents, 225*0Sstevel@tonic-gate addrs[0]->length); 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gate krb5_free_addresses(context, addrs); 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate addrlen = sizeof (remote_addr); 231*0Sstevel@tonic-gate if (getpeername(s2, &remote_addr, &addrlen) < 0) 232*0Sstevel@tonic-gate { 233*0Sstevel@tonic-gate if ((errno == ECONNREFUSED) || 234*0Sstevel@tonic-gate (errno == EHOSTUNREACH)) 235*0Sstevel@tonic-gate continue; /* try the next addr */ 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate code = errno; 238*0Sstevel@tonic-gate goto cleanup; 239*0Sstevel@tonic-gate } 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET; 242*0Sstevel@tonic-gate remote_kaddr.length = sizeof (((struct sockaddr_in *) 243*0Sstevel@tonic-gate &remote_addr)->sin_addr); 244*0Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) 245*0Sstevel@tonic-gate &(((struct sockaddr_in *)&remote_addr)->sin_addr); 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate /* 248*0Sstevel@tonic-gate * mk_priv requires that the local address be set. 249*0Sstevel@tonic-gate * getsockname is used for this. rd_priv requires that the 250*0Sstevel@tonic-gate * remote address be set. recvfrom is used for this. If 251*0Sstevel@tonic-gate * rd_priv is given a local address, and the message has the 252*0Sstevel@tonic-gate * recipient addr in it, this will be checked. However, there 253*0Sstevel@tonic-gate * is simply no way to know ahead of time what address the 254*0Sstevel@tonic-gate * message will be delivered *to*. Therefore, it is important 255*0Sstevel@tonic-gate * that either no recipient address is in the messages when 256*0Sstevel@tonic-gate * mk_priv is called, or that no local address is passed to 257*0Sstevel@tonic-gate * rd_priv. Both is a better idea, and I have done that. In 258*0Sstevel@tonic-gate * summary, when mk_priv is called, *only* a local address is 259*0Sstevel@tonic-gate * specified. when rd_priv is called, *only* a remote address 260*0Sstevel@tonic-gate * is specified. Are we having fun yet? 261*0Sstevel@tonic-gate */ 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate if (code = krb5_auth_con_setaddrs(context, auth_context, 264*0Sstevel@tonic-gate &local_kaddr, NULL)) 265*0Sstevel@tonic-gate { 266*0Sstevel@tonic-gate code = errno; 267*0Sstevel@tonic-gate goto cleanup; 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate if (code = krb5_mk_chpw_req(context, auth_context, 271*0Sstevel@tonic-gate &ap_req, newpw, &chpw_req)) 272*0Sstevel@tonic-gate { 273*0Sstevel@tonic-gate code = errno; 274*0Sstevel@tonic-gate goto cleanup; 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate if ((cc = sendto(s1, chpw_req.data, chpw_req.length, 0, 278*0Sstevel@tonic-gate (struct sockaddr *)&addr_p[i], 279*0Sstevel@tonic-gate sizeof (addr_p[i]))) != chpw_req.length) 280*0Sstevel@tonic-gate { 281*0Sstevel@tonic-gate if ((cc < 0) && ((errno == ECONNREFUSED) || 282*0Sstevel@tonic-gate (errno == EHOSTUNREACH))) 283*0Sstevel@tonic-gate continue; /* try the next addr */ 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate code = (cc < 0) ? errno : ECONNABORTED; 286*0Sstevel@tonic-gate goto cleanup; 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate chpw_rep.length = 1500; 290*0Sstevel@tonic-gate chpw_rep.data = (char *)malloc(chpw_rep.length); 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate /* XXX need a timeout/retry loop here */ 293*0Sstevel@tonic-gate FD_ZERO(&fdset); 294*0Sstevel@tonic-gate FD_SET(s1, &fdset); 295*0Sstevel@tonic-gate timeout.tv_sec = 120; 296*0Sstevel@tonic-gate timeout.tv_usec = 0; 297*0Sstevel@tonic-gate switch (select(s1 + 1, &fdset, 0, 0, &timeout)) { 298*0Sstevel@tonic-gate case -1: 299*0Sstevel@tonic-gate code = errno; 300*0Sstevel@tonic-gate goto cleanup; 301*0Sstevel@tonic-gate case 0: 302*0Sstevel@tonic-gate code = ETIMEDOUT; 303*0Sstevel@tonic-gate goto cleanup; 304*0Sstevel@tonic-gate default: 305*0Sstevel@tonic-gate /* fall through */ 306*0Sstevel@tonic-gate ; 307*0Sstevel@tonic-gate } 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate tmp_len = sizeof (tmp_addr); 310*0Sstevel@tonic-gate if ((cc = recvfrom(s1, chpw_rep.data, chpw_rep.length, 311*0Sstevel@tonic-gate 0, &tmp_addr, &tmp_len)) < 0) 312*0Sstevel@tonic-gate { 313*0Sstevel@tonic-gate code = errno; 314*0Sstevel@tonic-gate goto cleanup; 315*0Sstevel@tonic-gate } 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate closesocket(s1); 318*0Sstevel@tonic-gate s1 = INVALID_SOCKET; 319*0Sstevel@tonic-gate closesocket(s2); 320*0Sstevel@tonic-gate s2 = INVALID_SOCKET; 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate chpw_rep.length = cc; 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate if (code = krb5_auth_con_setaddrs(context, auth_context, 325*0Sstevel@tonic-gate NULL, &remote_kaddr)) 326*0Sstevel@tonic-gate goto cleanup; 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate if (code = krb5_rd_chpw_rep(context, auth_context, &chpw_rep, 329*0Sstevel@tonic-gate &local_result_code, srvr_msg)) 330*0Sstevel@tonic-gate goto cleanup; 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gate if (srvr_rsp_code) 333*0Sstevel@tonic-gate *srvr_rsp_code = local_result_code; 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate code = 0; 336*0Sstevel@tonic-gate goto cleanup; 337*0Sstevel@tonic-gate } 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate code = errno; 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate cleanup: 342*0Sstevel@tonic-gate if (auth_context != NULL) 343*0Sstevel@tonic-gate krb5_auth_con_free(context, auth_context); 344*0Sstevel@tonic-gate 345*0Sstevel@tonic-gate if (addr_p != NULL) 346*0Sstevel@tonic-gate krb5_xfree(addr_p); 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate if (s1 != INVALID_SOCKET) 349*0Sstevel@tonic-gate closesocket(s1); 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate if (s2 != INVALID_SOCKET) 352*0Sstevel@tonic-gate closesocket(s2); 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate krb5_xfree(chpw_req.data); 355*0Sstevel@tonic-gate krb5_xfree(chpw_rep.data); 356*0Sstevel@tonic-gate krb5_xfree(ap_req.data); 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate return (code); 359*0Sstevel@tonic-gate } 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate /* 363*0Sstevel@tonic-gate * kadm5_chpass_principal_v2 364*0Sstevel@tonic-gate * 365*0Sstevel@tonic-gate * New function used to prepare to make the change password request to a 366*0Sstevel@tonic-gate * non-SEAM admin server. The protocol used in this case is not based on 367*0Sstevel@tonic-gate * RPCSEC_GSS, it simply makes the request to port 464 (udp and tcp). 368*0Sstevel@tonic-gate * This is the same way that MIT KRB5 1.2.1 changes passwords. 369*0Sstevel@tonic-gate */ 370*0Sstevel@tonic-gate kadm5_ret_t 371*0Sstevel@tonic-gate kadm5_chpass_principal_v2(void *server_handle, 372*0Sstevel@tonic-gate krb5_principal princ, 373*0Sstevel@tonic-gate char *newpw, 374*0Sstevel@tonic-gate kadm5_ret_t *srvr_rsp_code, 375*0Sstevel@tonic-gate krb5_data *srvr_msg) 376*0Sstevel@tonic-gate { 377*0Sstevel@tonic-gate kadm5_ret_t code; 378*0Sstevel@tonic-gate kadm5_server_handle_t handle = (kadm5_server_handle_t)server_handle; 379*0Sstevel@tonic-gate krb5_error_code result; 380*0Sstevel@tonic-gate krb5_creds mcreds; 381*0Sstevel@tonic-gate krb5_creds ncreds; 382*0Sstevel@tonic-gate krb5_ccache ccache; 383*0Sstevel@tonic-gate int cpwlen; 384*0Sstevel@tonic-gate char *cpw_service = NULL; 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate /* 387*0Sstevel@tonic-gate * The credentials have already been stored in the cache in the 388*0Sstevel@tonic-gate * initialization step earlier, but we dont have direct access to it 389*0Sstevel@tonic-gate * at this level. Derive the cache and fetch the credentials to use for 390*0Sstevel@tonic-gate * sending the request. 391*0Sstevel@tonic-gate */ 392*0Sstevel@tonic-gate memset(&mcreds, 0, sizeof (krb5_creds)); 393*0Sstevel@tonic-gate if ((code = krb5_cc_resolve(handle->context, handle->cache_name, 394*0Sstevel@tonic-gate &ccache))) 395*0Sstevel@tonic-gate return (code); 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate /* set the client principal in the credential match structure */ 398*0Sstevel@tonic-gate mcreds.client = princ; 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate /* 401*0Sstevel@tonic-gate * set the server principal (kadmin/changepw@REALM) in the credential 402*0Sstevel@tonic-gate * match struct 403*0Sstevel@tonic-gate */ 404*0Sstevel@tonic-gate cpwlen = strlen(KADM5_CHANGEPW_SERVICE) + 405*0Sstevel@tonic-gate strlen(handle->params.realm) + 2; 406*0Sstevel@tonic-gate cpw_service = malloc(cpwlen); 407*0Sstevel@tonic-gate if (cpw_service == NULL) { 408*0Sstevel@tonic-gate return (ENOMEM); 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate snprintf(cpw_service, cpwlen, "%s@%s", 412*0Sstevel@tonic-gate KADM5_CHANGEPW_SERVICE, handle->params.realm); 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gate /* generate the server principal from the name string we generated */ 415*0Sstevel@tonic-gate if ((code = krb5_parse_name(handle->context, cpw_service, 416*0Sstevel@tonic-gate &mcreds.server))) { 417*0Sstevel@tonic-gate free(cpw_service); 418*0Sstevel@tonic-gate return (code); 419*0Sstevel@tonic-gate } 420*0Sstevel@tonic-gate 421*0Sstevel@tonic-gate /* Find the credentials in the cache */ 422*0Sstevel@tonic-gate if ((code = krb5_cc_retrieve_cred(handle->context, ccache, 0, &mcreds, 423*0Sstevel@tonic-gate &ncreds))) { 424*0Sstevel@tonic-gate free(cpw_service); 425*0Sstevel@tonic-gate return (code); 426*0Sstevel@tonic-gate } 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate /* Now we have all we need to make the change request. */ 429*0Sstevel@tonic-gate result = krb5_change_password(handle->context, &handle->params, 430*0Sstevel@tonic-gate &ncreds, newpw, 431*0Sstevel@tonic-gate srvr_rsp_code, 432*0Sstevel@tonic-gate srvr_msg); 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate free(cpw_service); 435*0Sstevel@tonic-gate return (result); 436*0Sstevel@tonic-gate } 437