1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 ** set password functions added by Paul W. Nelson, Thursby Software Systems, Inc.
12 krb5int_mk_chpw_req(krb5_context context,
13 krb5_auth_context auth_context,
18 krb5_error_code ret = 0;
21 krb5_replay_data replay;
26 if ((ret = krb5_auth_con_setflags(context, auth_context,
27 KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
30 clearpw.length = strlen(passwd);
31 clearpw.data = passwd;
33 if ((ret = krb5_mk_priv(context, auth_context,
34 &clearpw, &cipherpw, &replay)))
37 packet->length = 6 + ap_req->length + cipherpw.length;
38 packet->data = (char *) malloc(packet->length);
39 if (packet->data == NULL) {
47 store_16_be(packet->length, ptr);
50 /* version == 0x0001 big-endian */
55 /* ap_req length, big-endian */
57 store_16_be(ap_req->length, ptr);
62 memcpy(ptr, ap_req->data, ap_req->length);
63 ptr += ap_req->length;
65 /* krb-priv of password */
67 memcpy(ptr, cipherpw.data, cipherpw.length);
70 if (cipherpw.data != NULL) /* allocated by krb5_mk_priv */
77 krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context,
78 krb5_data *packet, int *result_code, krb5_data *result_data)
81 unsigned int plen, vno;
83 krb5_ap_rep_enc_part *ap_rep_enc;
85 krb5_data cipherresult;
86 krb5_data clearresult;
87 krb5_error *krberror = NULL;
88 krb5_replay_data replay;
91 if (packet->length < 4)
92 /* either this, or the server is printing bad messages,
93 or the caller passed in garbage */
94 return(KRB5KRB_AP_ERR_MODIFIED);
100 plen = (*ptr++ & 0xff);
101 plen = (plen<<8) | (*ptr++ & 0xff);
103 if (plen != packet->length) {
105 * MS KDCs *may* send back a KRB_ERROR. Although
106 * not 100% correct via RFC3244, it's something
107 * we can workaround here.
109 if (krb5_is_krb_error(packet)) {
111 if ((ret = krb5_rd_error(context, packet, &krberror)))
114 if (krberror->e_data.data == NULL)
115 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
117 ret = KRB5KRB_AP_ERR_MODIFIED;
118 krb5_free_error(context, krberror);
121 return(KRB5KRB_AP_ERR_MODIFIED);
126 /* verify version number */
128 vno = (*ptr++ & 0xff);
129 vno = (vno<<8) | (*ptr++ & 0xff);
132 return(KRB5KDC_ERR_BAD_PVNO);
134 /* read, check ap-rep length */
136 ap_rep.length = (*ptr++ & 0xff);
137 ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
139 if (ptr + ap_rep.length >= packet->data + packet->length)
140 return(KRB5KRB_AP_ERR_MODIFIED);
145 ptr += ap_rep.length;
148 * Save send_subkey to later smash recv_subkey.
150 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
154 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
156 krb5_free_keyblock(context, tmp);
160 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
162 /* extract and decrypt the result */
164 cipherresult.data = ptr;
165 cipherresult.length = (packet->data + packet->length) - ptr;
168 * Smash recv_subkey to be send_subkey, per spec.
170 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
171 krb5_free_keyblock(context, tmp);
175 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
181 cipherresult.data = ptr;
182 cipherresult.length = (packet->data + packet->length) - ptr;
184 if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
187 clearresult = krberror->e_data;
190 if (clearresult.length < 2) {
191 ret = KRB5KRB_AP_ERR_MODIFIED;
195 ptr = clearresult.data;
197 *result_code = (*ptr++ & 0xff);
198 *result_code = (*result_code<<8) | (*ptr++ & 0xff);
200 if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
201 (*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
202 ret = KRB5KRB_AP_ERR_MODIFIED;
206 /* all success replies should be authenticated/encrypted */
208 if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
209 ret = KRB5KRB_AP_ERR_MODIFIED;
213 result_data->length = (clearresult.data + clearresult.length) - ptr;
215 if (result_data->length) {
216 result_data->data = (char *) malloc(result_data->length);
217 if (result_data->data == NULL) {
221 memcpy(result_data->data, ptr, result_data->length);
223 result_data->data = NULL;
230 free(clearresult.data);
232 krb5_free_error(context, krberror);
238 krb5_error_code KRB5_CALLCONV
239 krb5_chpw_result_code_string(krb5_context context, int result_code,
242 switch (result_code) {
243 case KRB5_KPASSWD_MALFORMED:
244 *code_string = "Malformed request error";
246 case KRB5_KPASSWD_HARDERROR:
247 *code_string = "Server error";
249 case KRB5_KPASSWD_AUTHERROR:
250 *code_string = "Authentication error";
252 case KRB5_KPASSWD_SOFTERROR:
253 *code_string = "Password change rejected";
256 *code_string = "Password change failed";
264 krb5int_mk_setpw_req(krb5_context context,
265 krb5_auth_context auth_context,
267 krb5_principal targprinc,
273 krb5_data *encoded_setpw;
274 struct krb5_setpw_req req;
278 cipherpw.data = NULL;
281 if ((ret = krb5_auth_con_setflags(context, auth_context,
282 KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
285 req.target = targprinc;
286 req.password.data = passwd;
287 req.password.length = strlen(passwd);
288 ret = encode_krb5_setpw_req(&req, &encoded_setpw);
293 if ((ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) {
294 krb5_free_data(context, encoded_setpw);
297 krb5_free_data(context, encoded_setpw);
300 packet->length = 6 + ap_req->length + cipherpw.length;
301 packet->data = (char *) malloc(packet->length);
302 if (packet->data == NULL) {
308 ** build the packet -
310 /* put in the length */
311 store_16_be(packet->length, ptr);
313 /* put in the version */
316 /* the ap_req length is big endian */
317 store_16_be(ap_req->length, ptr);
319 /* put in the request data */
320 memcpy(ptr, ap_req->data, ap_req->length);
321 ptr += ap_req->length;
323 ** put in the "private" password data -
325 memcpy(ptr, cipherpw.data, cipherpw.length);
329 krb5_free_data_contents(context, &cipherpw);
330 if ((ret != 0) && packet->data) {
338 krb5int_rd_setpw_rep(krb5_context context, krb5_auth_context auth_context,
340 int *result_code, krb5_data *result_data)
343 unsigned int message_length, version_number;
345 krb5_ap_rep_enc_part *ap_rep_enc;
347 krb5_data cipherresult;
348 krb5_data clearresult;
349 krb5_keyblock *tmpkey;
351 ** validate the packet length -
353 if (packet->length < 4)
354 return(KRB5KRB_AP_ERR_MODIFIED);
359 ** see if it is an error
361 if (krb5_is_krb_error(packet)) {
362 krb5_error *krberror;
363 if ((ret = krb5_rd_error(context, packet, &krberror)))
365 if (krberror->e_data.data == NULL) {
366 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
367 krb5_free_error(context, krberror);
370 clearresult = krberror->e_data;
371 krberror->e_data.data = NULL; /*So we can free it later*/
372 krberror->e_data.length = 0;
373 krb5_free_error(context, krberror);
376 } else { /* Not an error*/
379 ** validate the message length -
380 ** length is big endian
382 message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
385 ** make sure the message length and packet length agree -
387 if (message_length != packet->length)
388 return(KRB5KRB_AP_ERR_MODIFIED);
390 ** get the version number -
392 version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
395 ** make sure we support the version returned -
398 ** set password version is 0xff80, change password version is 1
400 if (version_number != 1 && version_number != 0xff80)
401 return(KRB5KDC_ERR_BAD_PVNO);
403 ** now fill in ap_rep with the reply -
406 ** get the reply length -
408 ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
411 ** validate ap_rep length agrees with the packet length -
413 if (ptr + ap_rep.length >= packet->data + packet->length)
414 return(KRB5KRB_AP_ERR_MODIFIED);
416 ** if data was returned, set the ap_rep ptr -
420 ptr += ap_rep.length;
423 * Save send_subkey to later smash recv_subkey.
425 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
429 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
431 krb5_free_keyblock(context, tmpkey);
435 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
437 ** now decrypt the result -
439 cipherresult.data = ptr;
440 cipherresult.length = (packet->data + packet->length) - ptr;
443 * Smash recv_subkey to be send_subkey, per spec.
445 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
446 krb5_free_keyblock(context, tmpkey);
450 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
454 } /*We got an ap_rep*/
456 return (KRB5KRB_AP_ERR_MODIFIED);
457 } /*Response instead of error*/
460 ** validate the cleartext length
462 if (clearresult.length < 2) {
463 ret = KRB5KRB_AP_ERR_MODIFIED;
467 ** now decode the result -
469 ptr = clearresult.data;
471 *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
475 ** result code 5 is access denied
477 if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5)) {
478 ret = KRB5KRB_AP_ERR_MODIFIED;
482 ** all success replies should be authenticated/encrypted
484 if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
485 ret = KRB5KRB_AP_ERR_MODIFIED;
490 result_data->length = (clearresult.data + clearresult.length) - ptr;
492 if (result_data->length) {
493 result_data->data = (char *) malloc(result_data->length);
494 if (result_data->data)
495 memcpy(result_data->data, ptr, result_data->length);
497 result_data->data = NULL;
502 krb5_free_data_contents(context, &clearresult);
507 krb5int_setpw_result_code_string(krb5_context context, int result_code,
508 const char **code_string)
510 switch (result_code) {
511 case KRB5_KPASSWD_MALFORMED:
512 *code_string = "Malformed request error";
514 case KRB5_KPASSWD_HARDERROR:
515 *code_string = "Server error";
517 case KRB5_KPASSWD_AUTHERROR:
518 *code_string = "Authentication error";
520 case KRB5_KPASSWD_SOFTERROR:
521 *code_string = "Password change rejected";
523 case 5: /* access denied */
524 *code_string = "Access denied";
526 case 6: /* bad version */
527 *code_string = "Wrong protocol version";
529 case 7: /* initial flag is needed */
530 *code_string = "Initial password required";
533 *code_string = "Success";
536 *code_string = "Password change failed";