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