xref: /onnv-gate/usr/src/lib/krb5/kadm5/srv/chgpwd.c (revision 1474:00024dd3f95c)
10Sstevel@tonic-gate /*
2*1474Smp153739  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
70Sstevel@tonic-gate 
80Sstevel@tonic-gate /*
90Sstevel@tonic-gate  * lib/krb5/kadm5/srv/chgpwd.c
100Sstevel@tonic-gate  *
110Sstevel@tonic-gate  * Copyright 1998 by the Massachusetts Institute of Technology.
120Sstevel@tonic-gate  * All Rights Reserved.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * Export of this software from the United States of America may
150Sstevel@tonic-gate  *   require a specific license from the United States Government.
160Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
170Sstevel@tonic-gate  *   export to obtain such a license before exporting.
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
200Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
210Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
220Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
230Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
240Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
250Sstevel@tonic-gate  * to distribution of the software without specific, written prior
260Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
270Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
280Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
290Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
300Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
310Sstevel@tonic-gate  * or implied warranty.
320Sstevel@tonic-gate  *
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /*
360Sstevel@tonic-gate  * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #include <libintl.h>
400Sstevel@tonic-gate #include <locale.h>
410Sstevel@tonic-gate #include <kadm5/admin.h>
420Sstevel@tonic-gate #include <syslog.h>
430Sstevel@tonic-gate #include <krb5/adm_proto.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #define	MAXAPREQ 1500
460Sstevel@tonic-gate 
470Sstevel@tonic-gate static krb5_error_code
480Sstevel@tonic-gate process_chpw_request(krb5_context context, void *server_handle,
490Sstevel@tonic-gate 			char *realm, int s, krb5_keytab keytab,
500Sstevel@tonic-gate 			struct sockaddr_in *sin, krb5_data *req,
510Sstevel@tonic-gate 			krb5_data *rep)
520Sstevel@tonic-gate {
530Sstevel@tonic-gate 	krb5_error_code ret;
540Sstevel@tonic-gate 	char *ptr;
550Sstevel@tonic-gate 	int plen, vno;
560Sstevel@tonic-gate 	krb5_address local_kaddr, remote_kaddr;
570Sstevel@tonic-gate 	int allocated_mem = 0;
580Sstevel@tonic-gate 	krb5_data ap_req, ap_rep;
590Sstevel@tonic-gate 	krb5_auth_context auth_context;
600Sstevel@tonic-gate 	krb5_principal changepw;
610Sstevel@tonic-gate 	krb5_ticket *ticket;
620Sstevel@tonic-gate 	krb5_data cipher, clear;
630Sstevel@tonic-gate 	struct sockaddr local_addr, remote_addr;
640Sstevel@tonic-gate 	int addrlen;
650Sstevel@tonic-gate 	krb5_replay_data replay;
660Sstevel@tonic-gate 	krb5_error krberror;
670Sstevel@tonic-gate 	int numresult;
680Sstevel@tonic-gate 	char strresult[1024];
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 	ret = 0;
710Sstevel@tonic-gate 	rep->length = 0;
720Sstevel@tonic-gate 
730Sstevel@tonic-gate 	auth_context = NULL;
740Sstevel@tonic-gate 	changepw = NULL;
750Sstevel@tonic-gate 	ap_rep.length = 0;
760Sstevel@tonic-gate 	ap_rep.data = NULL;
770Sstevel@tonic-gate 	ticket = NULL;
780Sstevel@tonic-gate 	clear.length = 0;
790Sstevel@tonic-gate 	clear.data = NULL;
800Sstevel@tonic-gate 	cipher.length = 0;
810Sstevel@tonic-gate 	cipher.data = NULL;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	if (req->length < 4) {
840Sstevel@tonic-gate 		/*
850Sstevel@tonic-gate 		 * either this, or the server is printing bad messages,
860Sstevel@tonic-gate 		 * or the caller passed in garbage
870Sstevel@tonic-gate 		 */
880Sstevel@tonic-gate 		ret = KRB5KRB_AP_ERR_MODIFIED;
890Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_MALFORMED;
900Sstevel@tonic-gate 		(void) strlcpy(strresult, "Request was truncated",
910Sstevel@tonic-gate 				sizeof (strresult));
920Sstevel@tonic-gate 		goto chpwfail;
930Sstevel@tonic-gate 	}
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	ptr = req->data;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	/*
980Sstevel@tonic-gate 	 * Verify length
990Sstevel@tonic-gate 	 */
1000Sstevel@tonic-gate 	plen = (*ptr++ & 0xff);
1010Sstevel@tonic-gate 	plen = (plen<<8) | (*ptr++ & 0xff);
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	if (plen != req->length)
1040Sstevel@tonic-gate 		return (KRB5KRB_AP_ERR_MODIFIED);
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	/*
1070Sstevel@tonic-gate 	 * Verify version number
1080Sstevel@tonic-gate 	 */
1090Sstevel@tonic-gate 	vno = (*ptr++ & 0xff);
1100Sstevel@tonic-gate 	vno = (vno<<8) | (*ptr++ & 0xff);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	if (vno != 1) {
1130Sstevel@tonic-gate 		ret = KRB5KDC_ERR_BAD_PVNO;
1140Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_MALFORMED;
1150Sstevel@tonic-gate 		(void) snprintf(strresult, sizeof (strresult),
1160Sstevel@tonic-gate 		    "Request contained unknown protocol version number %d",
1170Sstevel@tonic-gate 		    vno);
1180Sstevel@tonic-gate 		goto chpwfail;
1190Sstevel@tonic-gate 	}
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	/*
1220Sstevel@tonic-gate 	 * Read, check ap-req length
1230Sstevel@tonic-gate 	 */
1240Sstevel@tonic-gate 	ap_req.length = (*ptr++ & 0xff);
1250Sstevel@tonic-gate 	ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	if (ptr + ap_req.length >= req->data + req->length) {
1280Sstevel@tonic-gate 		ret = KRB5KRB_AP_ERR_MODIFIED;
1290Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_MALFORMED;
1300Sstevel@tonic-gate 		(void) strlcpy(strresult, "Request was truncated in AP-REQ",
1310Sstevel@tonic-gate 					sizeof (strresult));
1320Sstevel@tonic-gate 		goto chpwfail;
1330Sstevel@tonic-gate 	}
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	/*
1360Sstevel@tonic-gate 	 * Verify ap_req
1370Sstevel@tonic-gate 	 */
1380Sstevel@tonic-gate 	ap_req.data = ptr;
1390Sstevel@tonic-gate 	ptr += ap_req.length;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	if (ret = krb5_auth_con_init(context, &auth_context)) {
142*1474Smp153739 		krb5_klog_syslog(LOG_ERR,
143*1474Smp153739 				gettext("Change password request failed. "
144*1474Smp153739 						"Failed initializing auth context: %s"),
145*1474Smp153739 				error_message(ret));
1460Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
1470Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed initializing auth context",
1480Sstevel@tonic-gate 					sizeof (strresult));
1490Sstevel@tonic-gate 		goto chpwfail;
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	if (ret = krb5_auth_con_setflags(context, auth_context,
1530Sstevel@tonic-gate 					KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
154*1474Smp153739 		krb5_klog_syslog(LOG_ERR,
155*1474Smp153739 				gettext("Change password request failed. "
156*1474Smp153739 						"Failed setting auth "
157*1474Smp153739 					    "context flags: %s"),
158*1474Smp153739 				error_message(ret));
1590Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
1600Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed initializing auth context",
1610Sstevel@tonic-gate 					sizeof (strresult));
1620Sstevel@tonic-gate 		goto chpwfail;
1630Sstevel@tonic-gate 	}
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
1660Sstevel@tonic-gate 				    "kadmin", "changepw", NULL)) {
167*1474Smp153739 		krb5_klog_syslog(LOG_ERR,
168*1474Smp153739 			gettext("Change password request failed "
169*1474Smp153739 					"Failed to build kadmin/changepw "
170*1474Smp153739 					"principal: %s"),
171*1474Smp153739 			error_message(ret));
1720Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
1730Sstevel@tonic-gate 		(void) strlcpy(strresult,
1740Sstevel@tonic-gate 				"Failed building kadmin/changepw principal",
1750Sstevel@tonic-gate 				sizeof (strresult));
1760Sstevel@tonic-gate 		goto chpwfail;
1770Sstevel@tonic-gate 	}
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
1800Sstevel@tonic-gate 			NULL, &ticket);
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	if (ret) {
183*1474Smp153739 		char kt_name[MAX_KEYTAB_NAME_LEN];
184*1474Smp153739 		if (krb5_kt_get_name(context, keytab,
185*1474Smp153739 				kt_name, sizeof (kt_name)))
186*1474Smp153739 			strncpy(kt_name, "default keytab", sizeof (kt_name));
187*1474Smp153739 
188*1474Smp153739 		switch (ret) {
189*1474Smp153739 		case KRB5_KT_NOTFOUND:
190*1474Smp153739 		krb5_klog_syslog(LOG_ERR,
191*1474Smp153739 			gettext("Change password request failed because "
192*1474Smp153739 					"keytab entry \"kadmin/changepw\" "
193*1474Smp153739 					"is missing from \"%s\""),
194*1474Smp153739 			kt_name);
195*1474Smp153739 		break;
196*1474Smp153739 		case ENOENT:
197*1474Smp153739 		krb5_klog_syslog(LOG_ERR,
198*1474Smp153739 			gettext("Change password request failed because "
199*1474Smp153739 					"keytab file \"%s\" does not exist"),
200*1474Smp153739 			kt_name);
201*1474Smp153739 		break;
202*1474Smp153739 		default:
203*1474Smp153739 		krb5_klog_syslog(LOG_ERR,
204*1474Smp153739 			gettext("Change password request failed. "
205*1474Smp153739 					"Failed to parse Kerberos AP_REQ message: %s"),
206*1474Smp153739 			error_message(ret));
207*1474Smp153739 		}
208*1474Smp153739 
2090Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_AUTHERROR;
2100Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed reading application request",
2110Sstevel@tonic-gate 					sizeof (strresult));
2120Sstevel@tonic-gate 		goto chpwfail;
2130Sstevel@tonic-gate 	}
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	/*
2160Sstevel@tonic-gate 	 * Set up address info
2170Sstevel@tonic-gate 	 */
2180Sstevel@tonic-gate 	addrlen = sizeof (local_addr);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	if (getsockname(s, &local_addr, &addrlen) < 0) {
2210Sstevel@tonic-gate 		ret = errno;
2220Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
2230Sstevel@tonic-gate 		(void) strlcpy(strresult,
2240Sstevel@tonic-gate 				"Failed getting server internet address",
2250Sstevel@tonic-gate 				sizeof (strresult));
2260Sstevel@tonic-gate 		goto chpwfail;
2270Sstevel@tonic-gate 	}
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	/*
2300Sstevel@tonic-gate 	 * Some brain-dead OS's don't return useful information from
2310Sstevel@tonic-gate 	 * the getsockname call.  Namely, windows and solaris.
2320Sstevel@tonic-gate 	 */
2330Sstevel@tonic-gate 	if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
2340Sstevel@tonic-gate 		local_kaddr.addrtype = ADDRTYPE_INET;
2350Sstevel@tonic-gate 		local_kaddr.length = sizeof (((struct sockaddr_in *)
2360Sstevel@tonic-gate 						&local_addr)->sin_addr);
2370Sstevel@tonic-gate 		/* CSTYLED */
2380Sstevel@tonic-gate 		local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr);
2390Sstevel@tonic-gate 	} else {
2400Sstevel@tonic-gate 		krb5_address **addrs;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 		krb5_os_localaddr(context, &addrs);
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 		local_kaddr.magic = addrs[0]->magic;
2450Sstevel@tonic-gate 		local_kaddr.addrtype = addrs[0]->addrtype;
2460Sstevel@tonic-gate 		local_kaddr.length = addrs[0]->length;
2470Sstevel@tonic-gate 		if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) {
2480Sstevel@tonic-gate 			ret = errno;
2490Sstevel@tonic-gate 			numresult = KRB5_KPASSWD_HARDERROR;
2500Sstevel@tonic-gate 			(void) strlcpy(strresult,
2510Sstevel@tonic-gate 				"Malloc failed for local_kaddr",
2520Sstevel@tonic-gate 				sizeof (strresult));
2530Sstevel@tonic-gate 			goto chpwfail;
2540Sstevel@tonic-gate 		}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 		(void) memcpy(local_kaddr.contents, addrs[0]->contents,
2570Sstevel@tonic-gate 				addrs[0]->length);
2580Sstevel@tonic-gate 		allocated_mem++;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 		krb5_free_addresses(context, addrs);
2610Sstevel@tonic-gate 	}
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	addrlen = sizeof (remote_addr);
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	if (getpeername(s, &remote_addr, &addrlen) < 0) {
2660Sstevel@tonic-gate 		ret = errno;
2670Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
2680Sstevel@tonic-gate 		(void) strlcpy(strresult,
2690Sstevel@tonic-gate 				"Failed getting client internet address",
2700Sstevel@tonic-gate 				sizeof (strresult));
2710Sstevel@tonic-gate 		goto chpwfail;
2720Sstevel@tonic-gate 	}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	remote_kaddr.addrtype = ADDRTYPE_INET;
2750Sstevel@tonic-gate 	remote_kaddr.length = sizeof (((struct sockaddr_in *)
2760Sstevel@tonic-gate 					&remote_addr)->sin_addr);
2770Sstevel@tonic-gate 	/* CSTYLED */
2780Sstevel@tonic-gate 	remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr);
2790Sstevel@tonic-gate 	remote_kaddr.addrtype = ADDRTYPE_INET;
2800Sstevel@tonic-gate 	remote_kaddr.length = sizeof (sin->sin_addr);
2810Sstevel@tonic-gate 	remote_kaddr.contents = (krb5_octet *) &sin->sin_addr;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	/*
2840Sstevel@tonic-gate 	 * mk_priv requires that the local address be set.
2850Sstevel@tonic-gate 	 * getsockname is used for this.  rd_priv requires that the
2860Sstevel@tonic-gate 	 * remote address be set.  recvfrom is used for this.  If
2870Sstevel@tonic-gate 	 * rd_priv is given a local address, and the message has the
2880Sstevel@tonic-gate 	 * recipient addr in it, this will be checked.  However, there
2890Sstevel@tonic-gate 	 * is simply no way to know ahead of time what address the
2900Sstevel@tonic-gate 	 * message will be delivered *to*.  Therefore, it is important
2910Sstevel@tonic-gate 	 * that either no recipient address is in the messages when
2920Sstevel@tonic-gate 	 * mk_priv is called, or that no local address is passed to
2930Sstevel@tonic-gate 	 * rd_priv.  Both is a better idea, and I have done that.  In
2940Sstevel@tonic-gate 	 * summary, when mk_priv is called, *only* a local address is
2950Sstevel@tonic-gate 	 * specified.  when rd_priv is called, *only* a remote address
2960Sstevel@tonic-gate 	 * is specified.  Are we having fun yet?
2970Sstevel@tonic-gate 	 */
2980Sstevel@tonic-gate 	if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
2990Sstevel@tonic-gate 					&remote_kaddr)) {
3000Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
3010Sstevel@tonic-gate 		(void) strlcpy(strresult,
3020Sstevel@tonic-gate 				"Failed storing client internet address",
3030Sstevel@tonic-gate 				sizeof (strresult));
3040Sstevel@tonic-gate 		goto chpwfail;
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	/*
3080Sstevel@tonic-gate 	 * Verify that this is an AS_REQ ticket
3090Sstevel@tonic-gate 	 */
3100Sstevel@tonic-gate 	if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
3110Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_AUTHERROR;
3120Sstevel@tonic-gate 		(void) strlcpy(strresult,
3130Sstevel@tonic-gate 				"Ticket must be derived from a password",
3140Sstevel@tonic-gate 				sizeof (strresult));
3150Sstevel@tonic-gate 		goto chpwfail;
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	/*
3190Sstevel@tonic-gate 	 * Construct the ap-rep
3200Sstevel@tonic-gate 	 */
3210Sstevel@tonic-gate 	if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) {
3220Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_AUTHERROR;
3230Sstevel@tonic-gate 		(void) strlcpy(strresult,
3240Sstevel@tonic-gate 				"Failed replying to application request",
3250Sstevel@tonic-gate 				sizeof (strresult));
3260Sstevel@tonic-gate 		goto chpwfail;
3270Sstevel@tonic-gate 	}
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	/*
3300Sstevel@tonic-gate 	 * Decrypt the new password
3310Sstevel@tonic-gate 	 */
3320Sstevel@tonic-gate 	cipher.length = (req->data + req->length) - ptr;
3330Sstevel@tonic-gate 	cipher.data = ptr;
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	if (ret = krb5_rd_priv(context, auth_context, &cipher,
3360Sstevel@tonic-gate 				&clear, &replay)) {
3370Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
3380Sstevel@tonic-gate 		(void) strlcpy(strresult, "Failed decrypting request",
3390Sstevel@tonic-gate 					sizeof (strresult));
3400Sstevel@tonic-gate 		goto chpwfail;
3410Sstevel@tonic-gate 	}
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	/*
3440Sstevel@tonic-gate 	 * Change the password
3450Sstevel@tonic-gate 	 */
3460Sstevel@tonic-gate 	if ((ptr = (char *)malloc(clear.length + 1)) == NULL) {
3470Sstevel@tonic-gate 		ret = errno;
3480Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
3490Sstevel@tonic-gate 		(void) strlcpy(strresult, "Malloc failed for ptr",
3500Sstevel@tonic-gate 			sizeof (strresult));
3510Sstevel@tonic-gate 		goto chpwfail;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 	(void) memcpy(ptr, clear.data, clear.length);
3540Sstevel@tonic-gate 	ptr[clear.length] = '\0';
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle,
3570Sstevel@tonic-gate 						ticket->enc_part2->client,
3580Sstevel@tonic-gate 						ptr, NULL, strresult,
3590Sstevel@tonic-gate 						sizeof (strresult));
3600Sstevel@tonic-gate 	/*
3610Sstevel@tonic-gate 	 * Zap the password
3620Sstevel@tonic-gate 	 */
3630Sstevel@tonic-gate 	(void) memset(clear.data, 0, clear.length);
3640Sstevel@tonic-gate 	(void) memset(ptr, 0, clear.length);
3650Sstevel@tonic-gate 	if (clear.data != NULL) {
3660Sstevel@tonic-gate 		krb5_xfree(clear.data);
3670Sstevel@tonic-gate 		clear.data = NULL;
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 	free(ptr);
3700Sstevel@tonic-gate 	clear.length = 0;
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	if (ret) {
3730Sstevel@tonic-gate 		if ((ret != KADM5_PASS_Q_TOOSHORT) &&
3740Sstevel@tonic-gate 		    (ret != KADM5_PASS_REUSE) &&
3750Sstevel@tonic-gate 		    (ret != KADM5_PASS_Q_CLASS) &&
3760Sstevel@tonic-gate 		    (ret != KADM5_PASS_Q_DICT) &&
3770Sstevel@tonic-gate 		    (ret != KADM5_PASS_TOOSOON))
3780Sstevel@tonic-gate 			numresult = KRB5_KPASSWD_HARDERROR;
3790Sstevel@tonic-gate 		else
3800Sstevel@tonic-gate 			numresult = KRB5_KPASSWD_SOFTERROR;
3810Sstevel@tonic-gate 		/*
3820Sstevel@tonic-gate 		 * strresult set by kadb5_chpass_principal_util()
3830Sstevel@tonic-gate 		 */
3840Sstevel@tonic-gate 		goto chpwfail;
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	/*
3880Sstevel@tonic-gate 	 * Success!
3890Sstevel@tonic-gate 	 */
3900Sstevel@tonic-gate 	numresult = KRB5_KPASSWD_SUCCESS;
3910Sstevel@tonic-gate 	(void) strlcpy(strresult, "", sizeof (strresult));
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate chpwfail:
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 	clear.length = 2 + strlen(strresult);
3960Sstevel@tonic-gate 	if (clear.data != NULL) {
3970Sstevel@tonic-gate 		krb5_xfree(clear.data);
3980Sstevel@tonic-gate 		clear.data = NULL;
3990Sstevel@tonic-gate 	}
4000Sstevel@tonic-gate 	if ((clear.data = (char *)malloc(clear.length)) == NULL) {
4010Sstevel@tonic-gate 		ret = errno;
4020Sstevel@tonic-gate 		numresult = KRB5_KPASSWD_HARDERROR;
4030Sstevel@tonic-gate 		(void) strlcpy(strresult, "Malloc failed for clear.data",
4040Sstevel@tonic-gate 			sizeof (strresult));
4050Sstevel@tonic-gate 	}
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	cipher.length = 0;
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	if (ap_rep.length) {
4100Sstevel@tonic-gate 		if (ret = krb5_auth_con_setaddrs(context, auth_context,
4110Sstevel@tonic-gate 					&local_kaddr, NULL)) {
4120Sstevel@tonic-gate 		    numresult = KRB5_KPASSWD_HARDERROR;
4130Sstevel@tonic-gate 		    (void) strlcpy(strresult,
4140Sstevel@tonic-gate 			"Failed storing client and server internet addresses",
4150Sstevel@tonic-gate 			sizeof (strresult));
4160Sstevel@tonic-gate 		} else {
4170Sstevel@tonic-gate 			if (ret = krb5_mk_priv(context, auth_context, &clear,
4180Sstevel@tonic-gate 						&cipher, &replay)) {
4190Sstevel@tonic-gate 				numresult = KRB5_KPASSWD_HARDERROR;
4200Sstevel@tonic-gate 				(void) strlcpy(strresult,
4210Sstevel@tonic-gate 					"Failed encrypting reply",
4220Sstevel@tonic-gate 					sizeof (strresult));
4230Sstevel@tonic-gate 			}
4240Sstevel@tonic-gate 		}
4250Sstevel@tonic-gate 	}
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	ptr = clear.data;
4280Sstevel@tonic-gate 	*ptr++ = (numresult>>8) & 0xff;
4290Sstevel@tonic-gate 	*ptr++ = numresult & 0xff;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	(void) memcpy(ptr, strresult, strlen(strresult));
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	/*
4340Sstevel@tonic-gate 	 * If no KRB-PRIV was constructed, then we need a KRB-ERROR.
4350Sstevel@tonic-gate 	 * If this fails, just bail.  There's nothing else we can do.
4360Sstevel@tonic-gate 	 */
4370Sstevel@tonic-gate 	if (cipher.length == 0) {
4380Sstevel@tonic-gate 		/*
4390Sstevel@tonic-gate 		 * Clear out ap_rep now, so that it won't be inserted
4400Sstevel@tonic-gate 		 * in the reply
4410Sstevel@tonic-gate 		 */
4420Sstevel@tonic-gate 		if (ap_rep.length) {
4430Sstevel@tonic-gate 			if (ap_rep.data != NULL)
4440Sstevel@tonic-gate 				krb5_xfree(ap_rep.data);
4450Sstevel@tonic-gate 			ap_rep.data = NULL;
4460Sstevel@tonic-gate 			ap_rep.length = 0;
4470Sstevel@tonic-gate 		}
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 		krberror.ctime = 0;
4500Sstevel@tonic-gate 		krberror.cusec = 0;
4510Sstevel@tonic-gate 		krberror.susec = 0;
4520Sstevel@tonic-gate 		if (ret = krb5_timeofday(context, &krberror.stime))
4530Sstevel@tonic-gate 			goto bailout;
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		/*
4560Sstevel@tonic-gate 		 * This is really icky.  but it's what all the other callers
4570Sstevel@tonic-gate 		 * to mk_error do.
4580Sstevel@tonic-gate 		 */
4590Sstevel@tonic-gate 		krberror.error = ret;
4600Sstevel@tonic-gate 		krberror.error -= ERROR_TABLE_BASE_krb5;
4610Sstevel@tonic-gate 		if (krberror.error < 0 || krberror.error > 128)
4620Sstevel@tonic-gate 			krberror.error = KRB_ERR_GENERIC;
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 		krberror.client = NULL;
4650Sstevel@tonic-gate 		if (ret = krb5_build_principal(context, &krberror.server,
4660Sstevel@tonic-gate 					    strlen(realm), realm,
4670Sstevel@tonic-gate 					    "kadmin", "changepw", NULL)) {
4680Sstevel@tonic-gate 			goto bailout;
4690Sstevel@tonic-gate 		}
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 		krberror.text.length = 0;
4720Sstevel@tonic-gate 		krberror.e_data = clear;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 		ret = krb5_mk_error(context, &krberror, &cipher);
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 		krb5_free_principal(context, krberror.server);
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 		if (ret)
4790Sstevel@tonic-gate 			goto bailout;
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	/*
4830Sstevel@tonic-gate 	 * Construct the reply
4840Sstevel@tonic-gate 	 */
4850Sstevel@tonic-gate 	rep->length = 6 + ap_rep.length + cipher.length;
4860Sstevel@tonic-gate 	if ((rep->data = (char *)malloc(rep->length)) == NULL)  {
4870Sstevel@tonic-gate 		ret = errno;
4880Sstevel@tonic-gate 		goto bailout;
4890Sstevel@tonic-gate 	}
4900Sstevel@tonic-gate 	ptr = rep->data;
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	/*
4930Sstevel@tonic-gate 	 * Length
4940Sstevel@tonic-gate 	 */
4950Sstevel@tonic-gate 	*ptr++ = (rep->length>>8) & 0xff;
4960Sstevel@tonic-gate 	*ptr++ = rep->length & 0xff;
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	/*
4990Sstevel@tonic-gate 	 * Version == 0x0001 big-endian
5000Sstevel@tonic-gate 	 */
5010Sstevel@tonic-gate 	*ptr++ = 0;
5020Sstevel@tonic-gate 	*ptr++ = 1;
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	/*
5050Sstevel@tonic-gate 	 * ap_rep length, big-endian
5060Sstevel@tonic-gate 	 */
5070Sstevel@tonic-gate 	*ptr++ = (ap_rep.length>>8) & 0xff;
5080Sstevel@tonic-gate 	*ptr++ = ap_rep.length & 0xff;
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	/*
5110Sstevel@tonic-gate 	 * ap-rep data
5120Sstevel@tonic-gate 	 */
5130Sstevel@tonic-gate 	if (ap_rep.length) {
5140Sstevel@tonic-gate 		(void) memcpy(ptr, ap_rep.data, ap_rep.length);
5150Sstevel@tonic-gate 		ptr += ap_rep.length;
5160Sstevel@tonic-gate 	}
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	/*
5190Sstevel@tonic-gate 	 * krb-priv or krb-error
5200Sstevel@tonic-gate 	 */
5210Sstevel@tonic-gate 	(void) memcpy(ptr, cipher.data, cipher.length);
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate bailout:
5240Sstevel@tonic-gate 	if (auth_context)
5250Sstevel@tonic-gate 		krb5_auth_con_free(context, auth_context);
5260Sstevel@tonic-gate 	if (changepw)
5270Sstevel@tonic-gate 		krb5_free_principal(context, changepw);
5280Sstevel@tonic-gate 	if (ap_rep.data != NULL)
5290Sstevel@tonic-gate 		krb5_xfree(ap_rep.data);
5300Sstevel@tonic-gate 	if (ticket)
5310Sstevel@tonic-gate 		krb5_free_ticket(context, ticket);
5320Sstevel@tonic-gate 	if (clear.data != NULL)
5330Sstevel@tonic-gate 		krb5_xfree(clear.data);
5340Sstevel@tonic-gate 	if (cipher.data != NULL)
5350Sstevel@tonic-gate 		krb5_xfree(cipher.data);
5360Sstevel@tonic-gate 	if (allocated_mem)
5370Sstevel@tonic-gate 		krb5_xfree(local_kaddr.contents);
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	return (ret);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate /*
5440Sstevel@tonic-gate  * This routine is used to handle password-change requests received
5450Sstevel@tonic-gate  * on kpasswd-port 464 from MIT/M$ clients.
5460Sstevel@tonic-gate  */
5470Sstevel@tonic-gate void
5480Sstevel@tonic-gate handle_chpw(krb5_context context, int s1,
5490Sstevel@tonic-gate 		void *serverhandle, kadm5_config_params *params)
5500Sstevel@tonic-gate {
5510Sstevel@tonic-gate 	krb5_error_code ret;
5520Sstevel@tonic-gate 	char req[MAXAPREQ];
5530Sstevel@tonic-gate 	int len;
5540Sstevel@tonic-gate 	struct sockaddr_in from;
5550Sstevel@tonic-gate 	int fromlen;
5560Sstevel@tonic-gate 	krb5_keytab kt;
5570Sstevel@tonic-gate 	krb5_data reqdata, repdata;
5580Sstevel@tonic-gate 	int s2 = -1;
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	reqdata.length = 0;
5610Sstevel@tonic-gate 	reqdata.data = NULL;
5620Sstevel@tonic-gate 	repdata.length = 0;
5630Sstevel@tonic-gate 	repdata.data = NULL;
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	fromlen = sizeof (from);
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from,
5680Sstevel@tonic-gate 			    &fromlen)) < 0) {
5690Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive "
5700Sstevel@tonic-gate 				"request: %s"), error_message(errno));
5710Sstevel@tonic-gate 		return;
5720Sstevel@tonic-gate 	}
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	if ((ret = krb5_kt_resolve(context, params->admin_keytab, &kt))) {
5750Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open "
5760Sstevel@tonic-gate 				"admin keytab %s"), error_message(ret));
5770Sstevel@tonic-gate 		return;
5780Sstevel@tonic-gate 	}
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	reqdata.length = len;
5810Sstevel@tonic-gate 	reqdata.data = req;
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	/*
5840Sstevel@tonic-gate 	 * This is really obscure.  s1 is used for all communications.  it
5850Sstevel@tonic-gate 	 * is left unconnected in case the server is multihomed and routes
5860Sstevel@tonic-gate 	 * are asymmetric.  s2 is connected to resolve routes and get
5870Sstevel@tonic-gate 	 * addresses.  this is the *only* way to get proper addresses for
5880Sstevel@tonic-gate 	 * multihomed hosts if routing is asymmetric.
5890Sstevel@tonic-gate 	 *
5900Sstevel@tonic-gate 	 * A related problem in the server, but not the client, is that
5910Sstevel@tonic-gate 	 * many os's have no way to disconnect a connected udp socket, so
5920Sstevel@tonic-gate 	 * the s2 socket needs to be closed and recreated for each
5930Sstevel@tonic-gate 	 * request.  The s1 socket must not be closed, or else queued
5940Sstevel@tonic-gate 	 * requests will be lost.
5950Sstevel@tonic-gate 	 *
5960Sstevel@tonic-gate 	 * A "naive" client implementation (one socket, no connect,
5970Sstevel@tonic-gate 	 * hostname resolution to get the local ip addr) will work and
5980Sstevel@tonic-gate 	 * interoperate if the client is single-homed.
5990Sstevel@tonic-gate 	 */
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
6020Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create "
6030Sstevel@tonic-gate 				"connecting socket: %s"), error_message(errno));
6040Sstevel@tonic-gate 		goto cleanup;
6050Sstevel@tonic-gate 	}
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) {
6080Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect "
6090Sstevel@tonic-gate 				"to client: %s"), error_message(errno));
6100Sstevel@tonic-gate 		if (s2 > 0)
6110Sstevel@tonic-gate 			(void) close(s2);
6120Sstevel@tonic-gate 		goto cleanup;
6130Sstevel@tonic-gate 	}
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	if ((ret = process_chpw_request(context, serverhandle,
6160Sstevel@tonic-gate 					params->realm, s2, kt, &from,
6170Sstevel@tonic-gate 					&reqdata, &repdata))) {
6180Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing "
6190Sstevel@tonic-gate 				"request: %s"), error_message(ret));
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	if (s2 > 0)
6230Sstevel@tonic-gate 		(void) close(s2);
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	if (repdata.length == 0 || repdata.data == NULL) {
6260Sstevel@tonic-gate 		/*
6270Sstevel@tonic-gate 		 * Just return.  This means something really bad happened
6280Sstevel@tonic-gate 		 */
6290Sstevel@tonic-gate 		goto cleanup;
6300Sstevel@tonic-gate 	}
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	len = sendto(s1, repdata.data, repdata.length, 0,
6330Sstevel@tonic-gate 		    (struct sockaddr *)&from, sizeof (from));
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	if (len < repdata.length) {
6360Sstevel@tonic-gate 		krb5_xfree(repdata.data);
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 		krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:"
6390Sstevel@tonic-gate 				" %s"), error_message(errno));
6400Sstevel@tonic-gate 		goto cleanup;
6410Sstevel@tonic-gate 	}
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	if (repdata.data != NULL)
6440Sstevel@tonic-gate 		krb5_xfree(repdata.data);
6450Sstevel@tonic-gate cleanup:
6460Sstevel@tonic-gate 	krb5_kt_close(context, kt);
6470Sstevel@tonic-gate }
648