2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 ** set password functions added by Paul W. Nelson, Thursby Software Systems, Inc.
11 /* Solaris Kerberos */
12 /* #include "krb5_err.h" */
19 krb5_auth_context auth_context
,
24 krb5_error_code ret
= 0;
27 krb5_replay_data replay
;
32 if ((ret
= krb5_auth_con_setflags(context
, auth_context
,
33 KRB5_AUTH_CONTEXT_DO_SEQUENCE
)))
36 clearpw
.length
= strlen(passwd
);
37 clearpw
.data
= passwd
;
39 if ((ret
= krb5_mk_priv(context
, auth_context
,
40 &clearpw
, &cipherpw
, &replay
)))
43 packet
->length
= 6 + ap_req
->length
+ cipherpw
.length
;
44 packet
->data
= (char *) malloc(packet
->length
);
45 if (packet
->data
== NULL
)
54 *ptr
++ = (packet
->length
>> 8) & 0xff;
55 *ptr
++ = packet
->length
& 0xff;
57 /* version == 0x0001 big-endian */
62 /* ap_req length, big-endian */
64 *ptr
++ = (ap_req
->length
>>8) & 0xff;
65 *ptr
++ = ap_req
->length
& 0xff;
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
);
83 krb5int_rd_chpw_rep(krb5_context context
, krb5_auth_context auth_context
, krb5_data
*packet
, int *result_code
, krb5_data
*result_data
)
88 krb5_ap_rep_enc_part
*ap_rep_enc
;
90 krb5_data cipherresult
;
91 krb5_data clearresult
;
92 /* Solaris Kerberos */
93 krb5_error
*krberror
= NULL
;
94 krb5_replay_data replay
;
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
);
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
)))
121 if (krberror
->e_data
.data
== NULL
) {
122 ret
= ERROR_TABLE_BASE_krb5
+ (krb5_error_code
) krberror
->error
;
123 krb5_free_error(context
, krberror
);
129 return(KRB5KRB_AP_ERR_MODIFIED
);
133 /* Solaris Kerberos */
134 if (krberror
!= NULL
) {
135 krb5_free_error(context
, krberror
);
139 /* verify version number */
141 vno
= (*ptr
++ & 0xff);
142 vno
= (vno
<<8) | (*ptr
++ & 0xff);
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
);
158 ptr
+= ap_rep
.length
;
161 * Save send_subkey to later smash recv_subkey.
163 ret
= krb5_auth_con_getsendsubkey(context
, auth_context
, &tmp
);
167 ret
= krb5_rd_rep(context
, auth_context
, &ap_rep
, &ap_rep_enc
);
169 krb5_free_keyblock(context
, tmp
);
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
);
188 ret
= krb5_rd_priv(context
, auth_context
, &cipherresult
, &clearresult
,
194 cipherresult
.data
= ptr
;
195 cipherresult
.length
= (packet
->data
+ packet
->length
) - ptr
;
197 if ((ret
= krb5_rd_error(context
, &cipherresult
, &krberror
)))
200 clearresult
= krberror
->e_data
;
203 if (clearresult
.length
< 2) {
204 ret
= KRB5KRB_AP_ERR_MODIFIED
;
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
;
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
;
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
) {
234 memcpy(result_data
->data
, ptr
, result_data
->length
);
236 result_data
->data
= NULL
;
243 krb5_xfree(clearresult
.data
);
245 krb5_free_error(context
, krberror
);
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";
258 case KRB5_KPASSWD_HARDERROR
:
259 *code_string
= "Server error";
261 case KRB5_KPASSWD_AUTHERROR
:
262 *code_string
= "Authentication error";
264 case KRB5_KPASSWD_SOFTERROR
:
265 *code_string
= "Password change rejected";
268 *code_string
= "Password change failed";
276 krb5int_mk_setpw_req(
277 krb5_context context
,
278 krb5_auth_context auth_context
,
280 krb5_principal targprinc
,
286 krb5_data
*encoded_setpw
;
287 struct krb5_setpw_req req
;
291 cipherpw
.data
= NULL
;
294 if ((ret
= krb5_auth_con_setflags(context
, auth_context
,
295 KRB5_AUTH_CONTEXT_DO_SEQUENCE
)))
298 req
.target
= targprinc
;
299 req
.password
.data
= passwd
;
300 req
.password
.length
= strlen(passwd
);
301 ret
= encode_krb5_setpw_req(&req
, &encoded_setpw
);
306 if ( (ret
= krb5_mk_priv(context
, auth_context
, encoded_setpw
, &cipherpw
, NULL
)) != 0) {
307 krb5_free_data( context
, encoded_setpw
);
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
) {
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 */
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
);
342 krb5_free_data_contents(context
, &cipherpw
);
343 if ((ret
!= 0) && packet
->data
) {
351 krb5int_rd_setpw_rep( krb5_context context
, krb5_auth_context auth_context
, krb5_data
*packet
,
352 int *result_code
, krb5_data
*result_data
)
355 unsigned int message_length
, version_number
;
357 krb5_ap_rep_enc_part
*ap_rep_enc
;
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
);
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
)))
377 if (krberror
->e_data
.data
== NULL
) {
378 ret
= ERROR_TABLE_BASE_krb5
+ (krb5_error_code
) krberror
->error
;
379 krb5_free_error(context
, krberror
);
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));
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));
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));
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
) {
431 ptr
+= ap_rep
.length
;
434 * Save send_subkey to later smash recv_subkey.
436 ret
= krb5_auth_con_getsendsubkey(context
, auth_context
, &tmpkey
);
440 ret
= krb5_rd_rep(context
, auth_context
, &ap_rep
, &ap_rep_enc
);
442 krb5_free_keyblock(context
, tmpkey
);
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
);
461 ret
= krb5_rd_priv(context
, auth_context
, &cipherresult
, &clearresult
,
465 } /*We got an ap_rep*/
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
;
478 ** now decode the result -
480 ptr
= clearresult
.data
;
482 *result_code
= (((ptr
[0] << 8)&0xff) | (ptr
[1]&0xff));
486 ** result code 5 is access denied
488 if ((*result_code
< KRB5_KPASSWD_SUCCESS
) || (*result_code
> 5))
490 ret
= KRB5KRB_AP_ERR_MODIFIED
;
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
;
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
);
512 result_data
->data
= NULL
;
517 krb5_free_data_contents(context
, &clearresult
);
522 krb5int_setpw_result_code_string( krb5_context context
, int result_code
, const char **code_string
)
526 case KRB5_KPASSWD_MALFORMED
:
527 *code_string
= "Malformed request error";
529 case KRB5_KPASSWD_HARDERROR
:
530 *code_string
= "Server error";
532 case KRB5_KPASSWD_AUTHERROR
:
533 *code_string
= "Authentication error";
535 case KRB5_KPASSWD_SOFTERROR
:
536 *code_string
= "Password change rejected";
538 case 5: /* access denied */
539 *code_string
= "Access denied";
541 case 6: /* bad version */
542 *code_string
= "Wrong protocol version";
544 case 7: /* initial flag is needed */
545 *code_string
= "Initial password required";
548 *code_string
= "Success";
550 *code_string
= "Password change failed";