1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 /*
6 ** set password functions added by Paul W. Nelson, Thursby Software Systems, Inc.
7 */
8 #include <string.h>
9
10 #include "k5-int.h"
11 /* Solaris Kerberos */
12 /* #include "krb5_err.h" */
13 #include "auth_con.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
59 *ptr++ = 0;
60 *ptr++ = 1;
61
62 /* ap_req length, big-endian */
63
64 *ptr++ = (ap_req->length>>8) & 0xff;
65 *ptr++ = ap_req->length & 0xff;
66
67 /* ap-req data */
68
69 memcpy(ptr, ap_req->data, ap_req->length);
70 ptr += ap_req->length;
71
72 /* krb-priv of password */
73
74 memcpy(ptr, cipherpw.data, cipherpw.length);
75
76 cleanup:
77 if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */
78 free(cipherpw.data);
79
80 return(ret);
81 }
82
83 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)84 krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
85 {
86 char *ptr;
87 int plen, vno;
88 krb5_data ap_rep;
89 krb5_ap_rep_enc_part *ap_rep_enc;
90 krb5_error_code ret;
91 krb5_data cipherresult;
92 krb5_data clearresult;
93 /* Solaris Kerberos */
94 krb5_error *krberror = NULL;
95 krb5_replay_data replay;
96 krb5_keyblock *tmp;
97
98 if (packet->length < 4)
99 /* either this, or the server is printing bad messages,
100 or the caller passed in garbage */
101 return(KRB5KRB_AP_ERR_MODIFIED);
102
103 ptr = packet->data;
104
105 /* verify length */
106
107 plen = (*ptr++ & 0xff);
108 plen = (plen<<8) | (*ptr++ & 0xff);
109
110 if (plen != packet->length)
111 {
112 /*
113 * MS KDCs *may* send back a KRB_ERROR. Although
114 * not 100% correct via RFC3244, it's something
115 * we can workaround here.
116 */
117 if (krb5_is_krb_error(packet)) {
118
119 if ((ret = krb5_rd_error(context, packet, &krberror)))
120 return(ret);
121
122 if (krberror->e_data.data == NULL) {
123 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
124 krb5_free_error(context, krberror);
125 return (ret);
126 }
127 }
128 else
129 {
130 return(KRB5KRB_AP_ERR_MODIFIED);
131 }
132 }
133
134 /* Solaris Kerberos */
135 if (krberror != NULL) {
136 krb5_free_error(context, krberror);
137 krberror = NULL;
138 }
139
140 /* verify version number */
141
142 vno = (*ptr++ & 0xff);
143 vno = (vno<<8) | (*ptr++ & 0xff);
144
145 if (vno != 1)
146 return(KRB5KDC_ERR_BAD_PVNO);
147
148 /* read, check ap-rep length */
149
150 ap_rep.length = (*ptr++ & 0xff);
151 ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
152
153 if (ptr + ap_rep.length >= packet->data + packet->length)
154 return(KRB5KRB_AP_ERR_MODIFIED);
155
156 if (ap_rep.length) {
157 /* verify ap_rep */
158 ap_rep.data = ptr;
159 ptr += ap_rep.length;
160
161 /*
162 * Save send_subkey to later smash recv_subkey.
163 */
164 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
165 if (ret)
166 return ret;
167
168 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
169 if (ret) {
170 krb5_free_keyblock(context, tmp);
171 return(ret);
172 }
173
174 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
175
176 /* extract and decrypt the result */
177
178 cipherresult.data = ptr;
179 cipherresult.length = (packet->data + packet->length) - ptr;
180
181 /*
182 * Smash recv_subkey to be send_subkey, per spec.
183 */
184 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
185 krb5_free_keyblock(context, tmp);
186 if (ret)
187 return ret;
188
189 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
190 &replay);
191
192 if (ret)
193 return(ret);
194 } else {
195 cipherresult.data = ptr;
196 cipherresult.length = (packet->data + packet->length) - ptr;
197
198 if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
199 return(ret);
200
201 clearresult = krberror->e_data;
202 }
203
204 if (clearresult.length < 2) {
205 ret = KRB5KRB_AP_ERR_MODIFIED;
206 goto cleanup;
207 }
208
209 ptr = clearresult.data;
210
211 *result_code = (*ptr++ & 0xff);
212 *result_code = (*result_code<<8) | (*ptr++ & 0xff);
213
214 if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
215 (*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
216 ret = KRB5KRB_AP_ERR_MODIFIED;
217 goto cleanup;
218 }
219
220 /* all success replies should be authenticated/encrypted */
221
222 if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
223 ret = KRB5KRB_AP_ERR_MODIFIED;
224 goto cleanup;
225 }
226
227 result_data->length = (clearresult.data + clearresult.length) - ptr;
228
229 if (result_data->length) {
230 result_data->data = (char *) malloc(result_data->length);
231 if (result_data->data == NULL) {
232 ret = ENOMEM;
233 goto cleanup;
234 }
235 memcpy(result_data->data, ptr, result_data->length);
236 } else {
237 result_data->data = NULL;
238 }
239
240 ret = 0;
241
242 cleanup:
243 if (ap_rep.length) {
244 krb5_xfree(clearresult.data);
245 } else {
246 krb5_free_error(context, krberror);
247 }
248
249 return(ret);
250 }
251
252 krb5_error_code KRB5_CALLCONV
krb5_chpw_result_code_string(krb5_context context,int result_code,char ** code_string)253 krb5_chpw_result_code_string(krb5_context context, int result_code, char **code_string)
254 {
255 switch (result_code) {
256 case KRB5_KPASSWD_MALFORMED:
257 *code_string = "Malformed request error";
258 break;
259 case KRB5_KPASSWD_HARDERROR:
260 *code_string = "Server error";
261 break;
262 case KRB5_KPASSWD_AUTHERROR:
263 *code_string = "Authentication error";
264 break;
265 case KRB5_KPASSWD_SOFTERROR:
266 *code_string = "Password change rejected";
267 break;
268 default:
269 *code_string = "Password change failed";
270 break;
271 }
272
273 return(0);
274 }
275
276 krb5_error_code
krb5int_mk_setpw_req(krb5_context context,krb5_auth_context auth_context,krb5_data * ap_req,krb5_principal targprinc,char * passwd,krb5_data * packet)277 krb5int_mk_setpw_req(
278 krb5_context context,
279 krb5_auth_context auth_context,
280 krb5_data *ap_req,
281 krb5_principal targprinc,
282 char *passwd,
283 krb5_data *packet )
284 {
285 krb5_error_code ret;
286 krb5_data cipherpw;
287 krb5_data *encoded_setpw;
288 struct krb5_setpw_req req;
289
290 char *ptr;
291
292 cipherpw.data = NULL;
293 cipherpw.length = 0;
294
295 if ((ret = krb5_auth_con_setflags(context, auth_context,
296 KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
297 return(ret);
298
299 req.target = targprinc;
300 req.password.data = passwd;
301 req.password.length = strlen(passwd);
302 ret = encode_krb5_setpw_req(&req, &encoded_setpw);
303 if (ret) {
304 return ret;
305 }
306
307 if ( (ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) {
308 krb5_free_data( context, encoded_setpw);
309 return(ret);
310 }
311 krb5_free_data( context, encoded_setpw);
312
313
314 packet->length = 6 + ap_req->length + cipherpw.length;
315 packet->data = (char *) malloc(packet->length);
316 if (packet->data == NULL) {
317 ret = ENOMEM;
318 goto cleanup;
319 }
320 ptr = packet->data;
321 /*
322 ** build the packet -
323 */
324 /* put in the length */
325 *ptr++ = (packet->length>>8) & 0xff;
326 *ptr++ = packet->length & 0xff;
327 /* put in the version */
328 *ptr++ = (char)0xff;
329 *ptr++ = (char)0x80;
330 /* the ap_req length is big endian */
331 *ptr++ = (ap_req->length>>8) & 0xff;
332 *ptr++ = ap_req->length & 0xff;
333 /* put in the request data */
334 memcpy(ptr, ap_req->data, ap_req->length);
335 ptr += ap_req->length;
336 /*
337 ** put in the "private" password data -
338 */
339 memcpy(ptr, cipherpw.data, cipherpw.length);
340 ret = 0;
341 cleanup:
342 if (cipherpw.data)
343 krb5_free_data_contents(context, &cipherpw);
344 if ((ret != 0) && packet->data) {
345 free( packet->data);
346 packet->data = NULL;
347 }
348 return ret;
349 }
350
351 krb5_error_code
krb5int_rd_setpw_rep(krb5_context context,krb5_auth_context auth_context,krb5_data * packet,int * result_code,krb5_data * result_data)352 krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet,
353 int *result_code, krb5_data *result_data )
354 {
355 char *ptr;
356 unsigned int message_length, version_number;
357 krb5_data ap_rep;
358 krb5_ap_rep_enc_part *ap_rep_enc;
359 krb5_error_code ret;
360 krb5_data cipherresult;
361 krb5_data clearresult;
362 krb5_keyblock *tmpkey;
363 /*
364 ** validate the packet length -
365 */
366 if (packet->length < 4)
367 return(KRB5KRB_AP_ERR_MODIFIED);
368
369 ptr = packet->data;
370
371 /*
372 ** see if it is an error
373 */
374 if (krb5_is_krb_error(packet)) {
375 krb5_error *krberror;
376 if ((ret = krb5_rd_error(context, packet, &krberror)))
377 return(ret);
378 if (krberror->e_data.data == NULL) {
379 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
380 krb5_free_error(context, krberror);
381 return (ret);
382 }
383 clearresult = krberror->e_data;
384 krberror->e_data.data = NULL; /*So we can free it later*/
385 krberror->e_data.length = 0;
386 krb5_free_error(context, krberror);
387
388 } else { /* Not an error*/
389
390 /*
391 ** validate the message length -
392 ** length is big endian
393 */
394 message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
395 ptr += 2;
396 /*
397 ** make sure the message length and packet length agree -
398 */
399 if (message_length != packet->length)
400 return(KRB5KRB_AP_ERR_MODIFIED);
401 /*
402 ** get the version number -
403 */
404 version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
405 ptr += 2;
406 /*
407 ** make sure we support the version returned -
408 */
409 /*
410 ** set password version is 0xff80, change password version is 1
411 */
412 if (version_number != 1 && version_number != 0xff80)
413 return(KRB5KDC_ERR_BAD_PVNO);
414 /*
415 ** now fill in ap_rep with the reply -
416 */
417 /*
418 ** get the reply length -
419 */
420 ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
421 ptr += 2;
422 /*
423 ** validate ap_rep length agrees with the packet length -
424 */
425 if (ptr + ap_rep.length >= packet->data + packet->length)
426 return(KRB5KRB_AP_ERR_MODIFIED);
427 /*
428 ** if data was returned, set the ap_rep ptr -
429 */
430 if( ap_rep.length ) {
431 ap_rep.data = ptr;
432 ptr += ap_rep.length;
433
434 /*
435 * Save send_subkey to later smash recv_subkey.
436 */
437 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
438 if (ret)
439 return ret;
440
441 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
442 if (ret) {
443 krb5_free_keyblock(context, tmpkey);
444 return(ret);
445 }
446
447 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
448 /*
449 ** now decrypt the result -
450 */
451 cipherresult.data = ptr;
452 cipherresult.length = (packet->data + packet->length) - ptr;
453
454 /*
455 * Smash recv_subkey to be send_subkey, per spec.
456 */
457 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
458 krb5_free_keyblock(context, tmpkey);
459 if (ret)
460 return ret;
461
462 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
463 NULL);
464 if (ret)
465 return(ret);
466 } /*We got an ap_rep*/
467 else
468 return (KRB5KRB_AP_ERR_MODIFIED);
469 } /*Response instead of error*/
470
471 /*
472 ** validate the cleartext length
473 */
474 if (clearresult.length < 2) {
475 ret = KRB5KRB_AP_ERR_MODIFIED;
476 goto cleanup;
477 }
478 /*
479 ** now decode the result -
480 */
481 ptr = clearresult.data;
482
483 *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
484 ptr += 2;
485
486 /*
487 ** result code 5 is access denied
488 */
489 if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5))
490 {
491 ret = KRB5KRB_AP_ERR_MODIFIED;
492 goto cleanup;
493 }
494 /*
495 ** all success replies should be authenticated/encrypted
496 */
497 if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) )
498 {
499 ret = KRB5KRB_AP_ERR_MODIFIED;
500 goto cleanup;
501 }
502
503 if (result_data) {
504 result_data->length = (clearresult.data + clearresult.length) - ptr;
505
506 if (result_data->length)
507 {
508 result_data->data = (char *) malloc(result_data->length);
509 if (result_data->data)
510 memcpy(result_data->data, ptr, result_data->length);
511 }
512 else
513 result_data->data = NULL;
514 }
515 ret = 0;
516
517 cleanup:
518 krb5_free_data_contents(context, &clearresult);
519 return(ret);
520 }
521
522 krb5_error_code
krb5int_setpw_result_code_string(krb5_context context,int result_code,const char ** code_string)523 krb5int_setpw_result_code_string( krb5_context context, int result_code, const char **code_string )
524 {
525 switch (result_code)
526 {
527 case KRB5_KPASSWD_MALFORMED:
528 *code_string = "Malformed request error";
529 break;
530 case KRB5_KPASSWD_HARDERROR:
531 *code_string = "Server error";
532 break;
533 case KRB5_KPASSWD_AUTHERROR:
534 *code_string = "Authentication error";
535 break;
536 case KRB5_KPASSWD_SOFTERROR:
537 *code_string = "Password change rejected";
538 break;
539 case 5: /* access denied */
540 *code_string = "Access denied";
541 break;
542 case 6: /* bad version */
543 *code_string = "Wrong protocol version";
544 break;
545 case 7: /* initial flag is needed */
546 *code_string = "Initial password required";
547 break;
548 case 0:
549 *code_string = "Success";
550 default:
551 *code_string = "Password change failed";
552 break;
553 }
554
555 return(0);
556 }
557
558