xref: /onnv-gate/usr/src/lib/krb5/kadm5/clnt/changepw.c (revision 0:68f95e015346)
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