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
);
77 if(cipherpw
.data
!= NULL
) /* allocated by krb5_mk_priv */
84 krb5int_rd_chpw_rep(krb5_context context
, krb5_auth_context auth_context
, krb5_data
*packet
, int *result_code
, krb5_data
*result_data
)
89 krb5_ap_rep_enc_part
*ap_rep_enc
;
91 krb5_data cipherresult
;
92 krb5_data clearresult
;
93 /* Solaris Kerberos */
94 krb5_error
*krberror
= NULL
;
95 krb5_replay_data replay
;
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
);
107 plen
= (*ptr
++ & 0xff);
108 plen
= (plen
<<8) | (*ptr
++ & 0xff);
110 if (plen
!= packet
->length
)
113 * MS KDCs *may* send back a KRB_ERROR. Although
114 * not 100% correct via RFC3244, it's something
115 * we can workaround here.
117 if (krb5_is_krb_error(packet
)) {
119 if ((ret
= krb5_rd_error(context
, packet
, &krberror
)))
122 if (krberror
->e_data
.data
== NULL
) {
123 ret
= ERROR_TABLE_BASE_krb5
+ (krb5_error_code
) krberror
->error
;
124 krb5_free_error(context
, krberror
);
130 return(KRB5KRB_AP_ERR_MODIFIED
);
134 /* Solaris Kerberos */
135 if (krberror
!= NULL
) {
136 krb5_free_error(context
, krberror
);
140 /* verify version number */
142 vno
= (*ptr
++ & 0xff);
143 vno
= (vno
<<8) | (*ptr
++ & 0xff);
146 return(KRB5KDC_ERR_BAD_PVNO
);
148 /* read, check ap-rep length */
150 ap_rep
.length
= (*ptr
++ & 0xff);
151 ap_rep
.length
= (ap_rep
.length
<<8) | (*ptr
++ & 0xff);
153 if (ptr
+ ap_rep
.length
>= packet
->data
+ packet
->length
)
154 return(KRB5KRB_AP_ERR_MODIFIED
);
159 ptr
+= ap_rep
.length
;
162 * Save send_subkey to later smash recv_subkey.
164 ret
= krb5_auth_con_getsendsubkey(context
, auth_context
, &tmp
);
168 ret
= krb5_rd_rep(context
, auth_context
, &ap_rep
, &ap_rep_enc
);
170 krb5_free_keyblock(context
, tmp
);
174 krb5_free_ap_rep_enc_part(context
, ap_rep_enc
);
176 /* extract and decrypt the result */
178 cipherresult
.data
= ptr
;
179 cipherresult
.length
= (packet
->data
+ packet
->length
) - ptr
;
182 * Smash recv_subkey to be send_subkey, per spec.
184 ret
= krb5_auth_con_setrecvsubkey(context
, auth_context
, tmp
);
185 krb5_free_keyblock(context
, tmp
);
189 ret
= krb5_rd_priv(context
, auth_context
, &cipherresult
, &clearresult
,
195 cipherresult
.data
= ptr
;
196 cipherresult
.length
= (packet
->data
+ packet
->length
) - ptr
;
198 if ((ret
= krb5_rd_error(context
, &cipherresult
, &krberror
)))
201 clearresult
= krberror
->e_data
;
204 if (clearresult
.length
< 2) {
205 ret
= KRB5KRB_AP_ERR_MODIFIED
;
209 ptr
= clearresult
.data
;
211 *result_code
= (*ptr
++ & 0xff);
212 *result_code
= (*result_code
<<8) | (*ptr
++ & 0xff);
214 if ((*result_code
< KRB5_KPASSWD_SUCCESS
) ||
215 (*result_code
> KRB5_KPASSWD_INITIAL_FLAG_NEEDED
)) {
216 ret
= KRB5KRB_AP_ERR_MODIFIED
;
220 /* all success replies should be authenticated/encrypted */
222 if ((ap_rep
.length
== 0) && (*result_code
== KRB5_KPASSWD_SUCCESS
)) {
223 ret
= KRB5KRB_AP_ERR_MODIFIED
;
227 result_data
->length
= (clearresult
.data
+ clearresult
.length
) - ptr
;
229 if (result_data
->length
) {
230 result_data
->data
= (char *) malloc(result_data
->length
);
231 if (result_data
->data
== NULL
) {
235 memcpy(result_data
->data
, ptr
, result_data
->length
);
237 result_data
->data
= NULL
;
244 krb5_xfree(clearresult
.data
);
246 krb5_free_error(context
, krberror
);
252 krb5_error_code KRB5_CALLCONV
253 krb5_chpw_result_code_string(krb5_context context
, int result_code
, char **code_string
)
255 switch (result_code
) {
256 case KRB5_KPASSWD_MALFORMED
:
257 *code_string
= "Malformed request error";
259 case KRB5_KPASSWD_HARDERROR
:
260 *code_string
= "Server error";
262 case KRB5_KPASSWD_AUTHERROR
:
263 *code_string
= "Authentication error";
265 case KRB5_KPASSWD_SOFTERROR
:
266 *code_string
= "Password change rejected";
269 *code_string
= "Password change failed";
277 krb5int_mk_setpw_req(
278 krb5_context context
,
279 krb5_auth_context auth_context
,
281 krb5_principal targprinc
,
287 krb5_data
*encoded_setpw
;
288 struct krb5_setpw_req req
;
292 cipherpw
.data
= NULL
;
295 if ((ret
= krb5_auth_con_setflags(context
, auth_context
,
296 KRB5_AUTH_CONTEXT_DO_SEQUENCE
)))
299 req
.target
= targprinc
;
300 req
.password
.data
= passwd
;
301 req
.password
.length
= strlen(passwd
);
302 ret
= encode_krb5_setpw_req(&req
, &encoded_setpw
);
307 if ( (ret
= krb5_mk_priv(context
, auth_context
, encoded_setpw
, &cipherpw
, NULL
)) != 0) {
308 krb5_free_data( context
, encoded_setpw
);
311 krb5_free_data( context
, encoded_setpw
);
314 packet
->length
= 6 + ap_req
->length
+ cipherpw
.length
;
315 packet
->data
= (char *) malloc(packet
->length
);
316 if (packet
->data
== NULL
) {
322 ** build the packet -
324 /* put in the length */
325 *ptr
++ = (packet
->length
>>8) & 0xff;
326 *ptr
++ = packet
->length
& 0xff;
327 /* put in the version */
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
;
337 ** put in the "private" password data -
339 memcpy(ptr
, cipherpw
.data
, cipherpw
.length
);
343 krb5_free_data_contents(context
, &cipherpw
);
344 if ((ret
!= 0) && packet
->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
)
356 unsigned int message_length
, version_number
;
358 krb5_ap_rep_enc_part
*ap_rep_enc
;
360 krb5_data cipherresult
;
361 krb5_data clearresult
;
362 krb5_keyblock
*tmpkey
;
364 ** validate the packet length -
366 if (packet
->length
< 4)
367 return(KRB5KRB_AP_ERR_MODIFIED
);
372 ** see if it is an error
374 if (krb5_is_krb_error(packet
)) {
375 krb5_error
*krberror
;
376 if ((ret
= krb5_rd_error(context
, packet
, &krberror
)))
378 if (krberror
->e_data
.data
== NULL
) {
379 ret
= ERROR_TABLE_BASE_krb5
+ (krb5_error_code
) krberror
->error
;
380 krb5_free_error(context
, krberror
);
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
);
388 } else { /* Not an error*/
391 ** validate the message length -
392 ** length is big endian
394 message_length
= (((ptr
[0] << 8)&0xff) | (ptr
[1]&0xff));
397 ** make sure the message length and packet length agree -
399 if (message_length
!= packet
->length
)
400 return(KRB5KRB_AP_ERR_MODIFIED
);
402 ** get the version number -
404 version_number
= (((ptr
[0] << 8)&0xff) | (ptr
[1]&0xff));
407 ** make sure we support the version returned -
410 ** set password version is 0xff80, change password version is 1
412 if (version_number
!= 1 && version_number
!= 0xff80)
413 return(KRB5KDC_ERR_BAD_PVNO
);
415 ** now fill in ap_rep with the reply -
418 ** get the reply length -
420 ap_rep
.length
= (((ptr
[0] << 8)&0xff) | (ptr
[1]&0xff));
423 ** validate ap_rep length agrees with the packet length -
425 if (ptr
+ ap_rep
.length
>= packet
->data
+ packet
->length
)
426 return(KRB5KRB_AP_ERR_MODIFIED
);
428 ** if data was returned, set the ap_rep ptr -
430 if( ap_rep
.length
) {
432 ptr
+= ap_rep
.length
;
435 * Save send_subkey to later smash recv_subkey.
437 ret
= krb5_auth_con_getsendsubkey(context
, auth_context
, &tmpkey
);
441 ret
= krb5_rd_rep(context
, auth_context
, &ap_rep
, &ap_rep_enc
);
443 krb5_free_keyblock(context
, tmpkey
);
447 krb5_free_ap_rep_enc_part(context
, ap_rep_enc
);
449 ** now decrypt the result -
451 cipherresult
.data
= ptr
;
452 cipherresult
.length
= (packet
->data
+ packet
->length
) - ptr
;
455 * Smash recv_subkey to be send_subkey, per spec.
457 ret
= krb5_auth_con_setrecvsubkey(context
, auth_context
, tmpkey
);
458 krb5_free_keyblock(context
, tmpkey
);
462 ret
= krb5_rd_priv(context
, auth_context
, &cipherresult
, &clearresult
,
466 } /*We got an ap_rep*/
468 return (KRB5KRB_AP_ERR_MODIFIED
);
469 } /*Response instead of error*/
472 ** validate the cleartext length
474 if (clearresult
.length
< 2) {
475 ret
= KRB5KRB_AP_ERR_MODIFIED
;
479 ** now decode the result -
481 ptr
= clearresult
.data
;
483 *result_code
= (((ptr
[0] << 8)&0xff) | (ptr
[1]&0xff));
487 ** result code 5 is access denied
489 if ((*result_code
< KRB5_KPASSWD_SUCCESS
) || (*result_code
> 5))
491 ret
= KRB5KRB_AP_ERR_MODIFIED
;
495 ** all success replies should be authenticated/encrypted
497 if( (ap_rep
.length
== 0) && (*result_code
== KRB5_KPASSWD_SUCCESS
) )
499 ret
= KRB5KRB_AP_ERR_MODIFIED
;
504 result_data
->length
= (clearresult
.data
+ clearresult
.length
) - ptr
;
506 if (result_data
->length
)
508 result_data
->data
= (char *) malloc(result_data
->length
);
509 if (result_data
->data
)
510 memcpy(result_data
->data
, ptr
, result_data
->length
);
513 result_data
->data
= NULL
;
518 krb5_free_data_contents(context
, &clearresult
);
523 krb5int_setpw_result_code_string( krb5_context context
, int result_code
, const char **code_string
)
527 case KRB5_KPASSWD_MALFORMED
:
528 *code_string
= "Malformed request error";
530 case KRB5_KPASSWD_HARDERROR
:
531 *code_string
= "Server error";
533 case KRB5_KPASSWD_AUTHERROR
:
534 *code_string
= "Authentication error";
536 case KRB5_KPASSWD_SOFTERROR
:
537 *code_string
= "Password change rejected";
539 case 5: /* access denied */
540 *code_string
= "Access denied";
542 case 6: /* bad version */
543 *code_string
= "Wrong protocol version";
545 case 7: /* initial flag is needed */
546 *code_string
= "Initial password required";
549 *code_string
= "Success";
551 *code_string
= "Password change failed";