xref: /illumos-gate/usr/src/lib/krb5/kadm5/clnt/chpw.c (revision 002c70ff32f5df6f93c15f88d351ce26443e6ee7)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 #include <string.h>
9 
10 #include <k5-int.h>
11 #include <kadm5/admin.h>
12 #include <client_internal.h>
13 #include <auth_con.h>
14 #include <locale.h>
15 
16 /*
17  * krb5_mk_chpw_req
18  *
19  * Generate a CHANGEPW request packet to send to a
20  * password server.
21  * The format of the packet used here is defined in the
22  * Marc Horowitz Password change protocol document (1998)
23  * (expired).
24  * It is also defined in the latest kerberos passwd set/change
25  * protocol IETF draft document by UMich, Cisco, and MS.
26  */
27 krb5_error_code KRB5_CALLCONV
28 krb5_mk_chpw_req(context, auth_context, ap_req, passwd, packet)
29 krb5_context context;
30 krb5_auth_context auth_context;
31 krb5_data *ap_req;
32 char *passwd;
33 krb5_data *packet;
34 {
35 	krb5_error_code ret = 0;
36 	krb5_data clearpw;
37 	krb5_data cipherpw;
38 	krb5_replay_data replay;
39 	char *ptr;
40 
41 	cipherpw.data = NULL;
42 
43 	if (ret = krb5_auth_con_setflags(context, auth_context,
44 					KRB5_AUTH_CONTEXT_DO_SEQUENCE))
45 		goto cleanup;
46 
47 	clearpw.length = strlen(passwd);
48 	clearpw.data = passwd;
49 
50 	if (ret = krb5_mk_priv(context, auth_context,
51 			    &clearpw, &cipherpw, &replay))
52 		goto cleanup;
53 
54 	packet->length = 6 + ap_req->length + cipherpw.length;
55 	packet->data = (char *)malloc(packet->length);
56 	if (packet->data == NULL)
57 	{
58 		ret = ENOMEM;
59 		goto cleanup;
60 	}
61 	ptr = packet->data;
62 
63 	/* length */
64 	*ptr++ = (packet->length>>8) & 0xff;
65 	*ptr++ = packet->length & 0xff;
66 
67 	/*
68 	 * version == 0x0001 big-endian
69 	 * NOTE: when MS and MIT start supporting the latest
70 	 *	version of the passwd change protocol (v2),
71 	 *	this value will change to 2.
72 	 */
73 	*ptr++ = 0;
74 	*ptr++ = 1;
75 
76 	/* ap_req length, big-endian */
77 	*ptr++ = (ap_req->length>>8) & 0xff;
78 	*ptr++ = ap_req->length & 0xff;
79 
80 	/* ap-req data */
81 	memcpy(ptr, ap_req->data, ap_req->length);
82 	ptr += ap_req->length;
83 
84 	/* krb-priv of password */
85 	memcpy(ptr, cipherpw.data, cipherpw.length);
86 
87 cleanup:
88 	if (cipherpw.data != NULL)  /* allocated by krb5_mk_priv */
89 		free(cipherpw.data);
90 
91 	return (ret);
92 }
93 
94 /*
95  * krb5_rd_chpw_rep
96  *
97  * Decode and parse the reply from the CHANGEPW request.
98  */
99 krb5_error_code KRB5_CALLCONV
100 krb5_rd_chpw_rep(context, auth_context, packet, result_code, result_data)
101 krb5_context context;
102 krb5_auth_context auth_context;
103 krb5_data *packet;
104 int *result_code;
105 krb5_data *result_data;
106 {
107 	char *ptr;
108 	int plen, vno;
109 	krb5_data ap_rep;
110 	krb5_ap_rep_enc_part *ap_rep_enc;
111 	krb5_error_code ret;
112 	krb5_data cipherresult;
113 	krb5_data clearresult;
114 	krb5_error *krberror;
115 	krb5_replay_data replay;
116 	krb5_keyblock *tmp;
117 	int local_result_code;
118 
119 	if (packet->length < 4)
120 		/*
121 		 * either this, or the server is printing bad messages,
122 		 * or the caller passed in garbage
123 		 */
124 		return (KRB5KRB_AP_ERR_MODIFIED);
125 
126 	ptr = packet->data;
127 
128 	/* verify length */
129 	plen = (*ptr++ & 0xff);
130 	plen = (plen<<8) | (*ptr++ & 0xff);
131 
132 	if (plen != packet->length)
133 		return (KRB5KRB_AP_ERR_MODIFIED);
134 
135 	/* verify version number */
136 	vno = (*ptr++ & 0xff);
137 	vno = (vno<<8) | (*ptr++ & 0xff);
138 
139 	/*
140 	 * when the servers update to v2 of the protocol,
141 	 * "2" will be a valid version number here
142 	 */
143 	if (vno != 1 && vno != 2)
144 		return (KRB5KDC_ERR_BAD_PVNO);
145 
146 	/* read, check ap-rep length */
147 	ap_rep.length = (*ptr++ & 0xff);
148 	ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
149 
150 	if (ptr + ap_rep.length >= packet->data + packet->length)
151 		return (KRB5KRB_AP_ERR_MODIFIED);
152 
153 	if (ap_rep.length) {
154 		/* verify ap_rep */
155 		ap_rep.data = ptr;
156 		ptr += ap_rep.length;
157 
158 		/*
159 		 * Save send_subkey to later smash recv_subkey.
160 		 */
161 		ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
162 		if (ret)
163 			return (ret);
164 
165 		if (ret = krb5_rd_rep(context, auth_context, &ap_rep,
166 				    &ap_rep_enc)) {
167 			krb5_free_keyblock(context, tmp);
168 			return (ret);
169 		}
170 
171 		krb5_free_ap_rep_enc_part(context, ap_rep_enc);
172 
173 		/* extract and decrypt the result */
174 		cipherresult.data = ptr;
175 		cipherresult.length = (packet->data + packet->length) - ptr;
176 
177 		/*
178 		 * Smash recv_subkey to be send_subkey, per spec.
179 		 */
180 		ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
181 		krb5_free_keyblock(context, tmp);
182 		if (ret)
183 			return (ret);
184 
185 		ret = krb5_rd_priv(context, auth_context, &cipherresult,
186 				&clearresult, &replay);
187 
188 		if (ret)
189 			return (ret);
190 	} else {
191 		cipherresult.data = ptr;
192 		cipherresult.length = (packet->data + packet->length) - ptr;
193 
194 		if (ret = krb5_rd_error(context, &cipherresult, &krberror))
195 			return (ret);
196 
197 		clearresult = krberror->e_data;
198 	}
199 
200 	if (clearresult.length < 2) {
201 		ret = KRB5KRB_AP_ERR_MODIFIED;
202 		goto cleanup;
203 	}
204 
205 	ptr = clearresult.data;
206 
207 	local_result_code = (*ptr++ & 0xff);
208 	local_result_code = (local_result_code<<8) | (*ptr++ & 0xff);
209 
210 	if (result_code)
211 		*result_code = local_result_code;
212 
213 	/*
214 	 * Make sure the result code is in range for this
215 	 * protocol.
216 	 */
217 	if ((local_result_code < KRB5_KPASSWD_SUCCESS) ||
218 	    (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
219 		ret = KRB5KRB_AP_ERR_MODIFIED;
220 		goto cleanup;
221 	}
222 
223 
224 	/* all success replies should be authenticated/encrypted */
225 	if ((ap_rep.length == 0) &&
226 	    (local_result_code == KRB5_KPASSWD_SUCCESS)) {
227 		ret = KRB5KRB_AP_ERR_MODIFIED;
228 		goto cleanup;
229 	}
230 
231 	result_data->length = (clearresult.data + clearresult.length) - ptr;
232 
233 	if (result_data->length) {
234 		result_data->data = (char *)malloc(result_data->length);
235 		if (result_data->data == NULL) {
236 			ret = ENOMEM;
237 			goto cleanup;
238 		}
239 		memcpy(result_data->data, ptr, result_data->length);
240 	} else {
241 		result_data->data = NULL;
242 	}
243 
244 	ret = 0;
245 
246 cleanup:
247 	if (ap_rep.length) {
248 		krb5_xfree(clearresult.data);
249 	} else {
250 		krb5_free_error(context, krberror);
251 	}
252 
253 	return (ret);
254 }
255