dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / krb5 / krb / chpw.c
blob4df55da5746b305722bba7ed3a8ece9ed1b9c381
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>
10 #include "k5-int.h"
11 /* Solaris Kerberos */
12 /* #include "krb5_err.h" */
13 #include "auth_con.h"
16 krb5_error_code
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)
24 krb5_error_code ret = 0;
25 krb5_data clearpw;
26 krb5_data cipherpw;
27 krb5_replay_data replay;
28 char *ptr;
30 cipherpw.data = NULL;
32 if ((ret = krb5_auth_con_setflags(context, auth_context,
33 KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
34 goto cleanup;
36 clearpw.length = strlen(passwd);
37 clearpw.data = passwd;
39 if ((ret = krb5_mk_priv(context, auth_context,
40 &clearpw, &cipherpw, &replay)))
41 goto cleanup;
43 packet->length = 6 + ap_req->length + cipherpw.length;
44 packet->data = (char *) malloc(packet->length);
45 if (packet->data == NULL)
47 ret = ENOMEM;
48 goto cleanup;
50 ptr = packet->data;
52 /* length */
54 *ptr++ = (packet->length>> 8) & 0xff;
55 *ptr++ = packet->length & 0xff;
57 /* version == 0x0001 big-endian */
59 *ptr++ = 0;
60 *ptr++ = 1;
62 /* ap_req length, big-endian */
64 *ptr++ = (ap_req->length>>8) & 0xff;
65 *ptr++ = ap_req->length & 0xff;
67 /* ap-req data */
69 memcpy(ptr, ap_req->data, ap_req->length);
70 ptr += ap_req->length;
72 /* krb-priv of password */
74 memcpy(ptr, cipherpw.data, cipherpw.length);
76 cleanup:
77 free(cipherpw.data);
79 return(ret);
82 krb5_error_code
83 krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
85 char *ptr;
86 int plen, vno;
87 krb5_data ap_rep;
88 krb5_ap_rep_enc_part *ap_rep_enc;
89 krb5_error_code ret;
90 krb5_data cipherresult;
91 krb5_data clearresult;
92 /* Solaris Kerberos */
93 krb5_error *krberror = NULL;
94 krb5_replay_data replay;
95 krb5_keyblock *tmp;
97 if (packet->length < 4)
98 /* either this, or the server is printing bad messages,
99 or the caller passed in garbage */
100 return(KRB5KRB_AP_ERR_MODIFIED);
102 ptr = packet->data;
104 /* verify length */
106 plen = (*ptr++ & 0xff);
107 plen = (plen<<8) | (*ptr++ & 0xff);
109 if (plen != packet->length)
112 * MS KDCs *may* send back a KRB_ERROR. Although
113 * not 100% correct via RFC3244, it's something
114 * we can workaround here.
116 if (krb5_is_krb_error(packet)) {
118 if ((ret = krb5_rd_error(context, packet, &krberror)))
119 return(ret);
121 if (krberror->e_data.data == NULL) {
122 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
123 krb5_free_error(context, krberror);
124 return (ret);
127 else
129 return(KRB5KRB_AP_ERR_MODIFIED);
133 /* Solaris Kerberos */
134 if (krberror != NULL) {
135 krb5_free_error(context, krberror);
136 krberror = NULL;
139 /* verify version number */
141 vno = (*ptr++ & 0xff);
142 vno = (vno<<8) | (*ptr++ & 0xff);
144 if (vno != 1)
145 return(KRB5KDC_ERR_BAD_PVNO);
147 /* read, check ap-rep length */
149 ap_rep.length = (*ptr++ & 0xff);
150 ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
152 if (ptr + ap_rep.length >= packet->data + packet->length)
153 return(KRB5KRB_AP_ERR_MODIFIED);
155 if (ap_rep.length) {
156 /* verify ap_rep */
157 ap_rep.data = ptr;
158 ptr += ap_rep.length;
161 * Save send_subkey to later smash recv_subkey.
163 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
164 if (ret)
165 return ret;
167 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
168 if (ret) {
169 krb5_free_keyblock(context, tmp);
170 return(ret);
173 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
175 /* extract and decrypt the result */
177 cipherresult.data = ptr;
178 cipherresult.length = (packet->data + packet->length) - ptr;
181 * Smash recv_subkey to be send_subkey, per spec.
183 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
184 krb5_free_keyblock(context, tmp);
185 if (ret)
186 return ret;
188 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
189 &replay);
191 if (ret)
192 return(ret);
193 } else {
194 cipherresult.data = ptr;
195 cipherresult.length = (packet->data + packet->length) - ptr;
197 if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
198 return(ret);
200 clearresult = krberror->e_data;
203 if (clearresult.length < 2) {
204 ret = KRB5KRB_AP_ERR_MODIFIED;
205 goto cleanup;
208 ptr = clearresult.data;
210 *result_code = (*ptr++ & 0xff);
211 *result_code = (*result_code<<8) | (*ptr++ & 0xff);
213 if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
214 (*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
215 ret = KRB5KRB_AP_ERR_MODIFIED;
216 goto cleanup;
219 /* all success replies should be authenticated/encrypted */
221 if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
222 ret = KRB5KRB_AP_ERR_MODIFIED;
223 goto cleanup;
226 result_data->length = (clearresult.data + clearresult.length) - ptr;
228 if (result_data->length) {
229 result_data->data = (char *) malloc(result_data->length);
230 if (result_data->data == NULL) {
231 ret = ENOMEM;
232 goto cleanup;
234 memcpy(result_data->data, ptr, result_data->length);
235 } else {
236 result_data->data = NULL;
239 ret = 0;
241 cleanup:
242 if (ap_rep.length) {
243 krb5_xfree(clearresult.data);
244 } else {
245 krb5_free_error(context, krberror);
248 return(ret);
251 krb5_error_code KRB5_CALLCONV
252 krb5_chpw_result_code_string(krb5_context context, int result_code, char **code_string)
254 switch (result_code) {
255 case KRB5_KPASSWD_MALFORMED:
256 *code_string = "Malformed request error";
257 break;
258 case KRB5_KPASSWD_HARDERROR:
259 *code_string = "Server error";
260 break;
261 case KRB5_KPASSWD_AUTHERROR:
262 *code_string = "Authentication error";
263 break;
264 case KRB5_KPASSWD_SOFTERROR:
265 *code_string = "Password change rejected";
266 break;
267 default:
268 *code_string = "Password change failed";
269 break;
272 return(0);
275 krb5_error_code
276 krb5int_mk_setpw_req(
277 krb5_context context,
278 krb5_auth_context auth_context,
279 krb5_data *ap_req,
280 krb5_principal targprinc,
281 char *passwd,
282 krb5_data *packet )
284 krb5_error_code ret;
285 krb5_data cipherpw;
286 krb5_data *encoded_setpw;
287 struct krb5_setpw_req req;
289 char *ptr;
291 cipherpw.data = NULL;
292 cipherpw.length = 0;
294 if ((ret = krb5_auth_con_setflags(context, auth_context,
295 KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
296 return(ret);
298 req.target = targprinc;
299 req.password.data = passwd;
300 req.password.length = strlen(passwd);
301 ret = encode_krb5_setpw_req(&req, &encoded_setpw);
302 if (ret) {
303 return ret;
306 if ( (ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) {
307 krb5_free_data( context, encoded_setpw);
308 return(ret);
310 krb5_free_data( context, encoded_setpw);
313 packet->length = 6 + ap_req->length + cipherpw.length;
314 packet->data = (char *) malloc(packet->length);
315 if (packet->data == NULL) {
316 ret = ENOMEM;
317 goto cleanup;
319 ptr = packet->data;
321 ** build the packet -
323 /* put in the length */
324 *ptr++ = (packet->length>>8) & 0xff;
325 *ptr++ = packet->length & 0xff;
326 /* put in the version */
327 *ptr++ = (char)0xff;
328 *ptr++ = (char)0x80;
329 /* the ap_req length is big endian */
330 *ptr++ = (ap_req->length>>8) & 0xff;
331 *ptr++ = ap_req->length & 0xff;
332 /* put in the request data */
333 memcpy(ptr, ap_req->data, ap_req->length);
334 ptr += ap_req->length;
336 ** put in the "private" password data -
338 memcpy(ptr, cipherpw.data, cipherpw.length);
339 ret = 0;
340 cleanup:
341 if (cipherpw.data)
342 krb5_free_data_contents(context, &cipherpw);
343 if ((ret != 0) && packet->data) {
344 free( packet->data);
345 packet->data = NULL;
347 return ret;
350 krb5_error_code
351 krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet,
352 int *result_code, krb5_data *result_data )
354 char *ptr;
355 unsigned int message_length, version_number;
356 krb5_data ap_rep;
357 krb5_ap_rep_enc_part *ap_rep_enc;
358 krb5_error_code ret;
359 krb5_data cipherresult;
360 krb5_data clearresult;
361 krb5_keyblock *tmpkey;
363 ** validate the packet length -
365 if (packet->length < 4)
366 return(KRB5KRB_AP_ERR_MODIFIED);
368 ptr = packet->data;
371 ** see if it is an error
373 if (krb5_is_krb_error(packet)) {
374 krb5_error *krberror;
375 if ((ret = krb5_rd_error(context, packet, &krberror)))
376 return(ret);
377 if (krberror->e_data.data == NULL) {
378 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
379 krb5_free_error(context, krberror);
380 return (ret);
382 clearresult = krberror->e_data;
383 krberror->e_data.data = NULL; /*So we can free it later*/
384 krberror->e_data.length = 0;
385 krb5_free_error(context, krberror);
387 } else { /* Not an error*/
390 ** validate the message length -
391 ** length is big endian
393 message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
394 ptr += 2;
396 ** make sure the message length and packet length agree -
398 if (message_length != packet->length)
399 return(KRB5KRB_AP_ERR_MODIFIED);
401 ** get the version number -
403 version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
404 ptr += 2;
406 ** make sure we support the version returned -
409 ** set password version is 0xff80, change password version is 1
411 if (version_number != 1 && version_number != 0xff80)
412 return(KRB5KDC_ERR_BAD_PVNO);
414 ** now fill in ap_rep with the reply -
417 ** get the reply length -
419 ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
420 ptr += 2;
422 ** validate ap_rep length agrees with the packet length -
424 if (ptr + ap_rep.length >= packet->data + packet->length)
425 return(KRB5KRB_AP_ERR_MODIFIED);
427 ** if data was returned, set the ap_rep ptr -
429 if( ap_rep.length ) {
430 ap_rep.data = ptr;
431 ptr += ap_rep.length;
434 * Save send_subkey to later smash recv_subkey.
436 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
437 if (ret)
438 return ret;
440 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
441 if (ret) {
442 krb5_free_keyblock(context, tmpkey);
443 return(ret);
446 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
448 ** now decrypt the result -
450 cipherresult.data = ptr;
451 cipherresult.length = (packet->data + packet->length) - ptr;
454 * Smash recv_subkey to be send_subkey, per spec.
456 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
457 krb5_free_keyblock(context, tmpkey);
458 if (ret)
459 return ret;
461 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
462 NULL);
463 if (ret)
464 return(ret);
465 } /*We got an ap_rep*/
466 else
467 return (KRB5KRB_AP_ERR_MODIFIED);
468 } /*Response instead of error*/
471 ** validate the cleartext length
473 if (clearresult.length < 2) {
474 ret = KRB5KRB_AP_ERR_MODIFIED;
475 goto cleanup;
478 ** now decode the result -
480 ptr = clearresult.data;
482 *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
483 ptr += 2;
486 ** result code 5 is access denied
488 if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5))
490 ret = KRB5KRB_AP_ERR_MODIFIED;
491 goto cleanup;
494 ** all success replies should be authenticated/encrypted
496 if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) )
498 ret = KRB5KRB_AP_ERR_MODIFIED;
499 goto cleanup;
502 if (result_data) {
503 result_data->length = (clearresult.data + clearresult.length) - ptr;
505 if (result_data->length)
507 result_data->data = (char *) malloc(result_data->length);
508 if (result_data->data)
509 memcpy(result_data->data, ptr, result_data->length);
511 else
512 result_data->data = NULL;
514 ret = 0;
516 cleanup:
517 krb5_free_data_contents(context, &clearresult);
518 return(ret);
521 krb5_error_code
522 krb5int_setpw_result_code_string( krb5_context context, int result_code, const char **code_string )
524 switch (result_code)
526 case KRB5_KPASSWD_MALFORMED:
527 *code_string = "Malformed request error";
528 break;
529 case KRB5_KPASSWD_HARDERROR:
530 *code_string = "Server error";
531 break;
532 case KRB5_KPASSWD_AUTHERROR:
533 *code_string = "Authentication error";
534 break;
535 case KRB5_KPASSWD_SOFTERROR:
536 *code_string = "Password change rejected";
537 break;
538 case 5: /* access denied */
539 *code_string = "Access denied";
540 break;
541 case 6: /* bad version */
542 *code_string = "Wrong protocol version";
543 break;
544 case 7: /* initial flag is needed */
545 *code_string = "Initial password required";
546 break;
547 case 0:
548 *code_string = "Success";
549 default:
550 *code_string = "Password change failed";
551 break;
554 return(0);