xref: /onnv-gate/usr/src/lib/krb5/kadm5/srv/chgpwd.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
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/kadm5/srv/chgpwd.c
10*0Sstevel@tonic-gate  *
11*0Sstevel@tonic-gate  * Copyright 1998 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 /*
36*0Sstevel@tonic-gate  * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients.
37*0Sstevel@tonic-gate  */
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate #include <libintl.h>
40*0Sstevel@tonic-gate #include <locale.h>
41*0Sstevel@tonic-gate #include <kadm5/admin.h>
42*0Sstevel@tonic-gate #include <syslog.h>
43*0Sstevel@tonic-gate #include <krb5/adm_proto.h>
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate #define	MAXAPREQ 1500
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate static krb5_error_code
48*0Sstevel@tonic-gate process_chpw_request(krb5_context context, void *server_handle,
49*0Sstevel@tonic-gate 			char *realm, int s, krb5_keytab keytab,
50*0Sstevel@tonic-gate 			struct sockaddr_in *sin, krb5_data *req,
51*0Sstevel@tonic-gate 			krb5_data *rep)
52*0Sstevel@tonic-gate {
53*0Sstevel@tonic-gate 	krb5_error_code ret;
54*0Sstevel@tonic-gate 	char *ptr;
55*0Sstevel@tonic-gate 	int plen, vno;
56*0Sstevel@tonic-gate 	krb5_address local_kaddr, remote_kaddr;
57*0Sstevel@tonic-gate 	int allocated_mem = 0;
58*0Sstevel@tonic-gate 	krb5_data ap_req, ap_rep;
59*0Sstevel@tonic-gate 	krb5_auth_context auth_context;
60*0Sstevel@tonic-gate 	krb5_principal changepw;
61*0Sstevel@tonic-gate 	krb5_ticket *ticket;
62*0Sstevel@tonic-gate 	krb5_data cipher, clear;
63*0Sstevel@tonic-gate 	struct sockaddr local_addr, remote_addr;
64*0Sstevel@tonic-gate 	int addrlen;
65*0Sstevel@tonic-gate 	krb5_replay_data replay;
66*0Sstevel@tonic-gate 	krb5_error krberror;
67*0Sstevel@tonic-gate 	int numresult;
68*0Sstevel@tonic-gate 	char strresult[1024];
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate 	ret = 0;
71*0Sstevel@tonic-gate 	rep->length = 0;
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate 	auth_context = NULL;
74*0Sstevel@tonic-gate 	changepw = NULL;
75*0Sstevel@tonic-gate 	ap_rep.length = 0;
76*0Sstevel@tonic-gate 	ap_rep.data = NULL;
77*0Sstevel@tonic-gate 	ticket = NULL;
78*0Sstevel@tonic-gate 	clear.length = 0;
79*0Sstevel@tonic-gate 	clear.data = NULL;
80*0Sstevel@tonic-gate 	cipher.length = 0;
81*0Sstevel@tonic-gate 	cipher.data = NULL;
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate 	if (req->length < 4) {
84*0Sstevel@tonic-gate 		/*
85*0Sstevel@tonic-gate 		 * either this, or the server is printing bad messages,
86*0Sstevel@tonic-gate 		 * or the caller passed in garbage
87*0Sstevel@tonic-gate 		 */
88*0Sstevel@tonic-gate 		ret = KRB5KRB_AP_ERR_MODIFIED;
89*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_MALFORMED;
90*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Request was truncated",
91*0Sstevel@tonic-gate 				sizeof (strresult));
92*0Sstevel@tonic-gate 		goto chpwfail;
93*0Sstevel@tonic-gate 	}
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate 	ptr = req->data;
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate 	/*
98*0Sstevel@tonic-gate 	 * Verify length
99*0Sstevel@tonic-gate 	 */
100*0Sstevel@tonic-gate 	plen = (*ptr++ & 0xff);
101*0Sstevel@tonic-gate 	plen = (plen<<8) | (*ptr++ & 0xff);
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate 	if (plen != req->length)
104*0Sstevel@tonic-gate 		return (KRB5KRB_AP_ERR_MODIFIED);
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate 	/*
107*0Sstevel@tonic-gate 	 * Verify version number
108*0Sstevel@tonic-gate 	 */
109*0Sstevel@tonic-gate 	vno = (*ptr++ & 0xff);
110*0Sstevel@tonic-gate 	vno = (vno<<8) | (*ptr++ & 0xff);
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 	if (vno != 1) {
113*0Sstevel@tonic-gate 		ret = KRB5KDC_ERR_BAD_PVNO;
114*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_MALFORMED;
115*0Sstevel@tonic-gate 		(void) snprintf(strresult, sizeof (strresult),
116*0Sstevel@tonic-gate 		    "Request contained unknown protocol version number %d",
117*0Sstevel@tonic-gate 		    vno);
118*0Sstevel@tonic-gate 		goto chpwfail;
119*0Sstevel@tonic-gate 	}
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	/*
122*0Sstevel@tonic-gate 	 * Read, check ap-req length
123*0Sstevel@tonic-gate 	 */
124*0Sstevel@tonic-gate 	ap_req.length = (*ptr++ & 0xff);
125*0Sstevel@tonic-gate 	ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate 	if (ptr + ap_req.length >= req->data + req->length) {
128*0Sstevel@tonic-gate 		ret = KRB5KRB_AP_ERR_MODIFIED;
129*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_MALFORMED;
130*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Request was truncated in AP-REQ",
131*0Sstevel@tonic-gate 					sizeof (strresult));
132*0Sstevel@tonic-gate 		goto chpwfail;
133*0Sstevel@tonic-gate 	}
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate 	/*
136*0Sstevel@tonic-gate 	 * Verify ap_req
137*0Sstevel@tonic-gate 	 */
138*0Sstevel@tonic-gate 	ap_req.data = ptr;
139*0Sstevel@tonic-gate 	ptr += ap_req.length;
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 	if (ret = krb5_auth_con_init(context, &auth_context)) {
142*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
143*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed initializing auth context",
144*0Sstevel@tonic-gate 					sizeof (strresult));
145*0Sstevel@tonic-gate 		goto chpwfail;
146*0Sstevel@tonic-gate 	}
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	if (ret = krb5_auth_con_setflags(context, auth_context,
149*0Sstevel@tonic-gate 					KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
150*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
151*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed initializing auth context",
152*0Sstevel@tonic-gate 					sizeof (strresult));
153*0Sstevel@tonic-gate 		goto chpwfail;
154*0Sstevel@tonic-gate 	}
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
157*0Sstevel@tonic-gate 				    "kadmin", "changepw", NULL)) {
158*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
159*0Sstevel@tonic-gate 		(void) strlcpy(strresult,
160*0Sstevel@tonic-gate 				"Failed building kadmin/changepw principal",
161*0Sstevel@tonic-gate 				sizeof (strresult));
162*0Sstevel@tonic-gate 		goto chpwfail;
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
166*0Sstevel@tonic-gate 			NULL, &ticket);
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	if (ret) {
169*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_AUTHERROR;
170*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed reading application request",
171*0Sstevel@tonic-gate 					sizeof (strresult));
172*0Sstevel@tonic-gate 		goto chpwfail;
173*0Sstevel@tonic-gate 	}
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	/*
176*0Sstevel@tonic-gate 	 * Set up address info
177*0Sstevel@tonic-gate 	 */
178*0Sstevel@tonic-gate 	addrlen = sizeof (local_addr);
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate 	if (getsockname(s, &local_addr, &addrlen) < 0) {
181*0Sstevel@tonic-gate 		ret = errno;
182*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
183*0Sstevel@tonic-gate 		(void) strlcpy(strresult,
184*0Sstevel@tonic-gate 				"Failed getting server internet address",
185*0Sstevel@tonic-gate 				sizeof (strresult));
186*0Sstevel@tonic-gate 		goto chpwfail;
187*0Sstevel@tonic-gate 	}
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	/*
190*0Sstevel@tonic-gate 	 * Some brain-dead OS's don't return useful information from
191*0Sstevel@tonic-gate 	 * the getsockname call.  Namely, windows and solaris.
192*0Sstevel@tonic-gate 	 */
193*0Sstevel@tonic-gate 	if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
194*0Sstevel@tonic-gate 		local_kaddr.addrtype = ADDRTYPE_INET;
195*0Sstevel@tonic-gate 		local_kaddr.length = sizeof (((struct sockaddr_in *)
196*0Sstevel@tonic-gate 						&local_addr)->sin_addr);
197*0Sstevel@tonic-gate 		/* CSTYLED */
198*0Sstevel@tonic-gate 		local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr);
199*0Sstevel@tonic-gate 	} else {
200*0Sstevel@tonic-gate 		krb5_address **addrs;
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 		krb5_os_localaddr(context, &addrs);
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 		local_kaddr.magic = addrs[0]->magic;
205*0Sstevel@tonic-gate 		local_kaddr.addrtype = addrs[0]->addrtype;
206*0Sstevel@tonic-gate 		local_kaddr.length = addrs[0]->length;
207*0Sstevel@tonic-gate 		if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) {
208*0Sstevel@tonic-gate 			ret = errno;
209*0Sstevel@tonic-gate 			numresult = KRB5_KPASSWD_HARDERROR;
210*0Sstevel@tonic-gate 			(void) strlcpy(strresult,
211*0Sstevel@tonic-gate 				"Malloc failed for local_kaddr",
212*0Sstevel@tonic-gate 				sizeof (strresult));
213*0Sstevel@tonic-gate 			goto chpwfail;
214*0Sstevel@tonic-gate 		}
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 		(void) memcpy(local_kaddr.contents, addrs[0]->contents,
217*0Sstevel@tonic-gate 				addrs[0]->length);
218*0Sstevel@tonic-gate 		allocated_mem++;
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate 		krb5_free_addresses(context, addrs);
221*0Sstevel@tonic-gate 	}
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	addrlen = sizeof (remote_addr);
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	if (getpeername(s, &remote_addr, &addrlen) < 0) {
226*0Sstevel@tonic-gate 		ret = errno;
227*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
228*0Sstevel@tonic-gate 		(void) strlcpy(strresult,
229*0Sstevel@tonic-gate 				"Failed getting client internet address",
230*0Sstevel@tonic-gate 				sizeof (strresult));
231*0Sstevel@tonic-gate 		goto chpwfail;
232*0Sstevel@tonic-gate 	}
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	remote_kaddr.addrtype = ADDRTYPE_INET;
235*0Sstevel@tonic-gate 	remote_kaddr.length = sizeof (((struct sockaddr_in *)
236*0Sstevel@tonic-gate 					&remote_addr)->sin_addr);
237*0Sstevel@tonic-gate 	/* CSTYLED */
238*0Sstevel@tonic-gate 	remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr);
239*0Sstevel@tonic-gate 	remote_kaddr.addrtype = ADDRTYPE_INET;
240*0Sstevel@tonic-gate 	remote_kaddr.length = sizeof (sin->sin_addr);
241*0Sstevel@tonic-gate 	remote_kaddr.contents = (krb5_octet *) &sin->sin_addr;
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 	/*
244*0Sstevel@tonic-gate 	 * mk_priv requires that the local address be set.
245*0Sstevel@tonic-gate 	 * getsockname is used for this.  rd_priv requires that the
246*0Sstevel@tonic-gate 	 * remote address be set.  recvfrom is used for this.  If
247*0Sstevel@tonic-gate 	 * rd_priv is given a local address, and the message has the
248*0Sstevel@tonic-gate 	 * recipient addr in it, this will be checked.  However, there
249*0Sstevel@tonic-gate 	 * is simply no way to know ahead of time what address the
250*0Sstevel@tonic-gate 	 * message will be delivered *to*.  Therefore, it is important
251*0Sstevel@tonic-gate 	 * that either no recipient address is in the messages when
252*0Sstevel@tonic-gate 	 * mk_priv is called, or that no local address is passed to
253*0Sstevel@tonic-gate 	 * rd_priv.  Both is a better idea, and I have done that.  In
254*0Sstevel@tonic-gate 	 * summary, when mk_priv is called, *only* a local address is
255*0Sstevel@tonic-gate 	 * specified.  when rd_priv is called, *only* a remote address
256*0Sstevel@tonic-gate 	 * is specified.  Are we having fun yet?
257*0Sstevel@tonic-gate 	 */
258*0Sstevel@tonic-gate 	if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
259*0Sstevel@tonic-gate 					&remote_kaddr)) {
260*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
261*0Sstevel@tonic-gate 		(void) strlcpy(strresult,
262*0Sstevel@tonic-gate 				"Failed storing client internet address",
263*0Sstevel@tonic-gate 				sizeof (strresult));
264*0Sstevel@tonic-gate 		goto chpwfail;
265*0Sstevel@tonic-gate 	}
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	/*
268*0Sstevel@tonic-gate 	 * Verify that this is an AS_REQ ticket
269*0Sstevel@tonic-gate 	 */
270*0Sstevel@tonic-gate 	if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
271*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_AUTHERROR;
272*0Sstevel@tonic-gate 		(void) strlcpy(strresult,
273*0Sstevel@tonic-gate 				"Ticket must be derived from a password",
274*0Sstevel@tonic-gate 				sizeof (strresult));
275*0Sstevel@tonic-gate 		goto chpwfail;
276*0Sstevel@tonic-gate 	}
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	/*
279*0Sstevel@tonic-gate 	 * Construct the ap-rep
280*0Sstevel@tonic-gate 	 */
281*0Sstevel@tonic-gate 	if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) {
282*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_AUTHERROR;
283*0Sstevel@tonic-gate 		(void) strlcpy(strresult,
284*0Sstevel@tonic-gate 				"Failed replying to application request",
285*0Sstevel@tonic-gate 				sizeof (strresult));
286*0Sstevel@tonic-gate 		goto chpwfail;
287*0Sstevel@tonic-gate 	}
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	/*
290*0Sstevel@tonic-gate 	 * Decrypt the new password
291*0Sstevel@tonic-gate 	 */
292*0Sstevel@tonic-gate 	cipher.length = (req->data + req->length) - ptr;
293*0Sstevel@tonic-gate 	cipher.data = ptr;
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate 	if (ret = krb5_rd_priv(context, auth_context, &cipher,
296*0Sstevel@tonic-gate 				&clear, &replay)) {
297*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
298*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed decrypting request",
299*0Sstevel@tonic-gate 					sizeof (strresult));
300*0Sstevel@tonic-gate 		goto chpwfail;
301*0Sstevel@tonic-gate 	}
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	/*
304*0Sstevel@tonic-gate 	 * Change the password
305*0Sstevel@tonic-gate 	 */
306*0Sstevel@tonic-gate 	if ((ptr = (char *)malloc(clear.length + 1)) == NULL) {
307*0Sstevel@tonic-gate 		ret = errno;
308*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
309*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Malloc failed for ptr",
310*0Sstevel@tonic-gate 			sizeof (strresult));
311*0Sstevel@tonic-gate 		goto chpwfail;
312*0Sstevel@tonic-gate 	}
313*0Sstevel@tonic-gate 	(void) memcpy(ptr, clear.data, clear.length);
314*0Sstevel@tonic-gate 	ptr[clear.length] = '\0';
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate 	ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle,
317*0Sstevel@tonic-gate 						ticket->enc_part2->client,
318*0Sstevel@tonic-gate 						ptr, NULL, strresult,
319*0Sstevel@tonic-gate 						sizeof (strresult));
320*0Sstevel@tonic-gate 	/*
321*0Sstevel@tonic-gate 	 * Zap the password
322*0Sstevel@tonic-gate 	 */
323*0Sstevel@tonic-gate 	(void) memset(clear.data, 0, clear.length);
324*0Sstevel@tonic-gate 	(void) memset(ptr, 0, clear.length);
325*0Sstevel@tonic-gate 	if (clear.data != NULL) {
326*0Sstevel@tonic-gate 		krb5_xfree(clear.data);
327*0Sstevel@tonic-gate 		clear.data = NULL;
328*0Sstevel@tonic-gate 	}
329*0Sstevel@tonic-gate 	free(ptr);
330*0Sstevel@tonic-gate 	clear.length = 0;
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	if (ret) {
333*0Sstevel@tonic-gate 		if ((ret != KADM5_PASS_Q_TOOSHORT) &&
334*0Sstevel@tonic-gate 		    (ret != KADM5_PASS_REUSE) &&
335*0Sstevel@tonic-gate 		    (ret != KADM5_PASS_Q_CLASS) &&
336*0Sstevel@tonic-gate 		    (ret != KADM5_PASS_Q_DICT) &&
337*0Sstevel@tonic-gate 		    (ret != KADM5_PASS_TOOSOON))
338*0Sstevel@tonic-gate 			numresult = KRB5_KPASSWD_HARDERROR;
339*0Sstevel@tonic-gate 		else
340*0Sstevel@tonic-gate 			numresult = KRB5_KPASSWD_SOFTERROR;
341*0Sstevel@tonic-gate 		/*
342*0Sstevel@tonic-gate 		 * strresult set by kadb5_chpass_principal_util()
343*0Sstevel@tonic-gate 		 */
344*0Sstevel@tonic-gate 		goto chpwfail;
345*0Sstevel@tonic-gate 	}
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate 	/*
348*0Sstevel@tonic-gate 	 * Success!
349*0Sstevel@tonic-gate 	 */
350*0Sstevel@tonic-gate 	numresult = KRB5_KPASSWD_SUCCESS;
351*0Sstevel@tonic-gate 	(void) strlcpy(strresult, "", sizeof (strresult));
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate chpwfail:
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	clear.length = 2 + strlen(strresult);
356*0Sstevel@tonic-gate 	if (clear.data != NULL) {
357*0Sstevel@tonic-gate 		krb5_xfree(clear.data);
358*0Sstevel@tonic-gate 		clear.data = NULL;
359*0Sstevel@tonic-gate 	}
360*0Sstevel@tonic-gate 	if ((clear.data = (char *)malloc(clear.length)) == NULL) {
361*0Sstevel@tonic-gate 		ret = errno;
362*0Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
363*0Sstevel@tonic-gate 		(void) strlcpy(strresult, "Malloc failed for clear.data",
364*0Sstevel@tonic-gate 			sizeof (strresult));
365*0Sstevel@tonic-gate 	}
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	cipher.length = 0;
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	if (ap_rep.length) {
370*0Sstevel@tonic-gate 		if (ret = krb5_auth_con_setaddrs(context, auth_context,
371*0Sstevel@tonic-gate 					&local_kaddr, NULL)) {
372*0Sstevel@tonic-gate 		    numresult = KRB5_KPASSWD_HARDERROR;
373*0Sstevel@tonic-gate 		    (void) strlcpy(strresult,
374*0Sstevel@tonic-gate 			"Failed storing client and server internet addresses",
375*0Sstevel@tonic-gate 			sizeof (strresult));
376*0Sstevel@tonic-gate 		} else {
377*0Sstevel@tonic-gate 			if (ret = krb5_mk_priv(context, auth_context, &clear,
378*0Sstevel@tonic-gate 						&cipher, &replay)) {
379*0Sstevel@tonic-gate 				numresult = KRB5_KPASSWD_HARDERROR;
380*0Sstevel@tonic-gate 				(void) strlcpy(strresult,
381*0Sstevel@tonic-gate 					"Failed encrypting reply",
382*0Sstevel@tonic-gate 					sizeof (strresult));
383*0Sstevel@tonic-gate 			}
384*0Sstevel@tonic-gate 		}
385*0Sstevel@tonic-gate 	}
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate 	ptr = clear.data;
388*0Sstevel@tonic-gate 	*ptr++ = (numresult>>8) & 0xff;
389*0Sstevel@tonic-gate 	*ptr++ = numresult & 0xff;
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	(void) memcpy(ptr, strresult, strlen(strresult));
392*0Sstevel@tonic-gate 
393*0Sstevel@tonic-gate 	/*
394*0Sstevel@tonic-gate 	 * If no KRB-PRIV was constructed, then we need a KRB-ERROR.
395*0Sstevel@tonic-gate 	 * If this fails, just bail.  There's nothing else we can do.
396*0Sstevel@tonic-gate 	 */
397*0Sstevel@tonic-gate 	if (cipher.length == 0) {
398*0Sstevel@tonic-gate 		/*
399*0Sstevel@tonic-gate 		 * Clear out ap_rep now, so that it won't be inserted
400*0Sstevel@tonic-gate 		 * in the reply
401*0Sstevel@tonic-gate 		 */
402*0Sstevel@tonic-gate 		if (ap_rep.length) {
403*0Sstevel@tonic-gate 			if (ap_rep.data != NULL)
404*0Sstevel@tonic-gate 				krb5_xfree(ap_rep.data);
405*0Sstevel@tonic-gate 			ap_rep.data = NULL;
406*0Sstevel@tonic-gate 			ap_rep.length = 0;
407*0Sstevel@tonic-gate 		}
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 		krberror.ctime = 0;
410*0Sstevel@tonic-gate 		krberror.cusec = 0;
411*0Sstevel@tonic-gate 		krberror.susec = 0;
412*0Sstevel@tonic-gate 		if (ret = krb5_timeofday(context, &krberror.stime))
413*0Sstevel@tonic-gate 			goto bailout;
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate 		/*
416*0Sstevel@tonic-gate 		 * This is really icky.  but it's what all the other callers
417*0Sstevel@tonic-gate 		 * to mk_error do.
418*0Sstevel@tonic-gate 		 */
419*0Sstevel@tonic-gate 		krberror.error = ret;
420*0Sstevel@tonic-gate 		krberror.error -= ERROR_TABLE_BASE_krb5;
421*0Sstevel@tonic-gate 		if (krberror.error < 0 || krberror.error > 128)
422*0Sstevel@tonic-gate 			krberror.error = KRB_ERR_GENERIC;
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate 		krberror.client = NULL;
425*0Sstevel@tonic-gate 		if (ret = krb5_build_principal(context, &krberror.server,
426*0Sstevel@tonic-gate 					    strlen(realm), realm,
427*0Sstevel@tonic-gate 					    "kadmin", "changepw", NULL)) {
428*0Sstevel@tonic-gate 			goto bailout;
429*0Sstevel@tonic-gate 		}
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate 		krberror.text.length = 0;
432*0Sstevel@tonic-gate 		krberror.e_data = clear;
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 		ret = krb5_mk_error(context, &krberror, &cipher);
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 		krb5_free_principal(context, krberror.server);
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 		if (ret)
439*0Sstevel@tonic-gate 			goto bailout;
440*0Sstevel@tonic-gate 	}
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 	/*
443*0Sstevel@tonic-gate 	 * Construct the reply
444*0Sstevel@tonic-gate 	 */
445*0Sstevel@tonic-gate 	rep->length = 6 + ap_rep.length + cipher.length;
446*0Sstevel@tonic-gate 	if ((rep->data = (char *)malloc(rep->length)) == NULL)  {
447*0Sstevel@tonic-gate 		ret = errno;
448*0Sstevel@tonic-gate 		goto bailout;
449*0Sstevel@tonic-gate 	}
450*0Sstevel@tonic-gate 	ptr = rep->data;
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate 	/*
453*0Sstevel@tonic-gate 	 * Length
454*0Sstevel@tonic-gate 	 */
455*0Sstevel@tonic-gate 	*ptr++ = (rep->length>>8) & 0xff;
456*0Sstevel@tonic-gate 	*ptr++ = rep->length & 0xff;
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	/*
459*0Sstevel@tonic-gate 	 * Version == 0x0001 big-endian
460*0Sstevel@tonic-gate 	 */
461*0Sstevel@tonic-gate 	*ptr++ = 0;
462*0Sstevel@tonic-gate 	*ptr++ = 1;
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate 	/*
465*0Sstevel@tonic-gate 	 * ap_rep length, big-endian
466*0Sstevel@tonic-gate 	 */
467*0Sstevel@tonic-gate 	*ptr++ = (ap_rep.length>>8) & 0xff;
468*0Sstevel@tonic-gate 	*ptr++ = ap_rep.length & 0xff;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	/*
471*0Sstevel@tonic-gate 	 * ap-rep data
472*0Sstevel@tonic-gate 	 */
473*0Sstevel@tonic-gate 	if (ap_rep.length) {
474*0Sstevel@tonic-gate 		(void) memcpy(ptr, ap_rep.data, ap_rep.length);
475*0Sstevel@tonic-gate 		ptr += ap_rep.length;
476*0Sstevel@tonic-gate 	}
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	/*
479*0Sstevel@tonic-gate 	 * krb-priv or krb-error
480*0Sstevel@tonic-gate 	 */
481*0Sstevel@tonic-gate 	(void) memcpy(ptr, cipher.data, cipher.length);
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate bailout:
484*0Sstevel@tonic-gate 	if (auth_context)
485*0Sstevel@tonic-gate 		krb5_auth_con_free(context, auth_context);
486*0Sstevel@tonic-gate 	if (changepw)
487*0Sstevel@tonic-gate 		krb5_free_principal(context, changepw);
488*0Sstevel@tonic-gate 	if (ap_rep.data != NULL)
489*0Sstevel@tonic-gate 		krb5_xfree(ap_rep.data);
490*0Sstevel@tonic-gate 	if (ticket)
491*0Sstevel@tonic-gate 		krb5_free_ticket(context, ticket);
492*0Sstevel@tonic-gate 	if (clear.data != NULL)
493*0Sstevel@tonic-gate 		krb5_xfree(clear.data);
494*0Sstevel@tonic-gate 	if (cipher.data != NULL)
495*0Sstevel@tonic-gate 		krb5_xfree(cipher.data);
496*0Sstevel@tonic-gate 	if (allocated_mem)
497*0Sstevel@tonic-gate 		krb5_xfree(local_kaddr.contents);
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 	return (ret);
500*0Sstevel@tonic-gate }
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate /*
504*0Sstevel@tonic-gate  * This routine is used to handle password-change requests received
505*0Sstevel@tonic-gate  * on kpasswd-port 464 from MIT/M$ clients.
506*0Sstevel@tonic-gate  */
507*0Sstevel@tonic-gate void
508*0Sstevel@tonic-gate handle_chpw(krb5_context context, int s1,
509*0Sstevel@tonic-gate 		void *serverhandle, kadm5_config_params *params)
510*0Sstevel@tonic-gate {
511*0Sstevel@tonic-gate 	krb5_error_code ret;
512*0Sstevel@tonic-gate 	char req[MAXAPREQ];
513*0Sstevel@tonic-gate 	int len;
514*0Sstevel@tonic-gate 	struct sockaddr_in from;
515*0Sstevel@tonic-gate 	int fromlen;
516*0Sstevel@tonic-gate 	krb5_keytab kt;
517*0Sstevel@tonic-gate 	krb5_data reqdata, repdata;
518*0Sstevel@tonic-gate 	int s2 = -1;
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	reqdata.length = 0;
521*0Sstevel@tonic-gate 	reqdata.data = NULL;
522*0Sstevel@tonic-gate 	repdata.length = 0;
523*0Sstevel@tonic-gate 	repdata.data = NULL;
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 	fromlen = sizeof (from);
526*0Sstevel@tonic-gate 
527*0Sstevel@tonic-gate 	if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from,
528*0Sstevel@tonic-gate 			    &fromlen)) < 0) {
529*0Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive "
530*0Sstevel@tonic-gate 				"request: %s"), error_message(errno));
531*0Sstevel@tonic-gate 		return;
532*0Sstevel@tonic-gate 	}
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 	if ((ret = krb5_kt_resolve(context, params->admin_keytab, &kt))) {
535*0Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open "
536*0Sstevel@tonic-gate 				"admin keytab %s"), error_message(ret));
537*0Sstevel@tonic-gate 		return;
538*0Sstevel@tonic-gate 	}
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 	reqdata.length = len;
541*0Sstevel@tonic-gate 	reqdata.data = req;
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 	/*
544*0Sstevel@tonic-gate 	 * This is really obscure.  s1 is used for all communications.  it
545*0Sstevel@tonic-gate 	 * is left unconnected in case the server is multihomed and routes
546*0Sstevel@tonic-gate 	 * are asymmetric.  s2 is connected to resolve routes and get
547*0Sstevel@tonic-gate 	 * addresses.  this is the *only* way to get proper addresses for
548*0Sstevel@tonic-gate 	 * multihomed hosts if routing is asymmetric.
549*0Sstevel@tonic-gate 	 *
550*0Sstevel@tonic-gate 	 * A related problem in the server, but not the client, is that
551*0Sstevel@tonic-gate 	 * many os's have no way to disconnect a connected udp socket, so
552*0Sstevel@tonic-gate 	 * the s2 socket needs to be closed and recreated for each
553*0Sstevel@tonic-gate 	 * request.  The s1 socket must not be closed, or else queued
554*0Sstevel@tonic-gate 	 * requests will be lost.
555*0Sstevel@tonic-gate 	 *
556*0Sstevel@tonic-gate 	 * A "naive" client implementation (one socket, no connect,
557*0Sstevel@tonic-gate 	 * hostname resolution to get the local ip addr) will work and
558*0Sstevel@tonic-gate 	 * interoperate if the client is single-homed.
559*0Sstevel@tonic-gate 	 */
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
562*0Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create "
563*0Sstevel@tonic-gate 				"connecting socket: %s"), error_message(errno));
564*0Sstevel@tonic-gate 		goto cleanup;
565*0Sstevel@tonic-gate 	}
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) {
568*0Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect "
569*0Sstevel@tonic-gate 				"to client: %s"), error_message(errno));
570*0Sstevel@tonic-gate 		if (s2 > 0)
571*0Sstevel@tonic-gate 			(void) close(s2);
572*0Sstevel@tonic-gate 		goto cleanup;
573*0Sstevel@tonic-gate 	}
574*0Sstevel@tonic-gate 
575*0Sstevel@tonic-gate 	if ((ret = process_chpw_request(context, serverhandle,
576*0Sstevel@tonic-gate 					params->realm, s2, kt, &from,
577*0Sstevel@tonic-gate 					&reqdata, &repdata))) {
578*0Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing "
579*0Sstevel@tonic-gate 				"request: %s"), error_message(ret));
580*0Sstevel@tonic-gate 	}
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	if (s2 > 0)
583*0Sstevel@tonic-gate 		(void) close(s2);
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 	if (repdata.length == 0 || repdata.data == NULL) {
586*0Sstevel@tonic-gate 		/*
587*0Sstevel@tonic-gate 		 * Just return.  This means something really bad happened
588*0Sstevel@tonic-gate 		 */
589*0Sstevel@tonic-gate 		goto cleanup;
590*0Sstevel@tonic-gate 	}
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate 	len = sendto(s1, repdata.data, repdata.length, 0,
593*0Sstevel@tonic-gate 		    (struct sockaddr *)&from, sizeof (from));
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 	if (len < repdata.length) {
596*0Sstevel@tonic-gate 		krb5_xfree(repdata.data);
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:"
599*0Sstevel@tonic-gate 				" %s"), error_message(errno));
600*0Sstevel@tonic-gate 		goto cleanup;
601*0Sstevel@tonic-gate 	}
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 	if (repdata.data != NULL)
604*0Sstevel@tonic-gate 		krb5_xfree(repdata.data);
605*0Sstevel@tonic-gate cleanup:
606*0Sstevel@tonic-gate 	krb5_kt_close(context, kt);
607*0Sstevel@tonic-gate }
608