1ba7b222eSGlenn Barry /*
2ba7b222eSGlenn Barry  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3ba7b222eSGlenn Barry  * Use is subject to license terms.
4ba7b222eSGlenn Barry  */
510db1377Sgtb /*
610db1377Sgtb ** set password functions added by Paul W. Nelson, Thursby Software Systems, Inc.
710db1377Sgtb */
810db1377Sgtb #include <string.h>
910db1377Sgtb 
1010db1377Sgtb #include "k5-int.h"
11159d09a2SMark Phalan /* Solaris Kerberos */
12159d09a2SMark Phalan /* #include "krb5_err.h" */
1310db1377Sgtb #include "auth_con.h"
1410db1377Sgtb 
1510db1377Sgtb 
16*55fea89dSDan Cross krb5_error_code
krb5int_mk_chpw_req(krb5_context context,krb5_auth_context auth_context,krb5_data * ap_req,char * passwd,krb5_data * packet)17159d09a2SMark Phalan krb5int_mk_chpw_req(
18*55fea89dSDan Cross 	krb5_context context,
19*55fea89dSDan Cross 	krb5_auth_context auth_context,
20159d09a2SMark Phalan 	krb5_data *ap_req,
21*55fea89dSDan Cross 	char *passwd,
22159d09a2SMark Phalan 	krb5_data *packet)
2310db1377Sgtb {
2410db1377Sgtb     krb5_error_code ret = 0;
2510db1377Sgtb     krb5_data clearpw;
2610db1377Sgtb     krb5_data cipherpw;
2710db1377Sgtb     krb5_replay_data replay;
2810db1377Sgtb     char *ptr;
2910db1377Sgtb 
3010db1377Sgtb     cipherpw.data = NULL;
3110db1377Sgtb 
3210db1377Sgtb     if ((ret = krb5_auth_con_setflags(context, auth_context,
3310db1377Sgtb 				      KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
3410db1377Sgtb 	  goto cleanup;
3510db1377Sgtb 
3610db1377Sgtb     clearpw.length = strlen(passwd);
3710db1377Sgtb     clearpw.data = passwd;
3810db1377Sgtb 
3910db1377Sgtb     if ((ret = krb5_mk_priv(context, auth_context,
4010db1377Sgtb 			    &clearpw, &cipherpw, &replay)))
4110db1377Sgtb       goto cleanup;
4210db1377Sgtb 
4310db1377Sgtb     packet->length = 6 + ap_req->length + cipherpw.length;
4410db1377Sgtb     packet->data = (char *) malloc(packet->length);
4510db1377Sgtb     if (packet->data == NULL)
46159d09a2SMark Phalan 	{
4710db1377Sgtb 	    ret = ENOMEM;
4810db1377Sgtb 	    goto cleanup;
49159d09a2SMark Phalan 	}
5010db1377Sgtb     ptr = packet->data;
5110db1377Sgtb 
5210db1377Sgtb     /* length */
5310db1377Sgtb 
54159d09a2SMark Phalan     *ptr++ = (packet->length>> 8) & 0xff;
5510db1377Sgtb     *ptr++ = packet->length & 0xff;
5610db1377Sgtb 
5710db1377Sgtb     /* version == 0x0001 big-endian */
5810db1377Sgtb 
5910db1377Sgtb     *ptr++ = 0;
6010db1377Sgtb     *ptr++ = 1;
6110db1377Sgtb 
6210db1377Sgtb     /* ap_req length, big-endian */
6310db1377Sgtb 
6410db1377Sgtb     *ptr++ = (ap_req->length>>8) & 0xff;
6510db1377Sgtb     *ptr++ = ap_req->length & 0xff;
6610db1377Sgtb 
6710db1377Sgtb     /* ap-req data */
6810db1377Sgtb 
6910db1377Sgtb     memcpy(ptr, ap_req->data, ap_req->length);
7010db1377Sgtb     ptr += ap_req->length;
7110db1377Sgtb 
7210db1377Sgtb     /* krb-priv of password */
7310db1377Sgtb 
7410db1377Sgtb     memcpy(ptr, cipherpw.data, cipherpw.length);
7510db1377Sgtb 
7610db1377Sgtb cleanup:
7710db1377Sgtb     if(cipherpw.data != NULL)  /* allocated by krb5_mk_priv */
7810db1377Sgtb       free(cipherpw.data);
79*55fea89dSDan Cross 
8010db1377Sgtb     return(ret);
8110db1377Sgtb }
8210db1377Sgtb 
83*55fea89dSDan Cross 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)8410db1377Sgtb krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
8510db1377Sgtb {
8610db1377Sgtb     char *ptr;
8710db1377Sgtb     int plen, vno;
8810db1377Sgtb     krb5_data ap_rep;
8910db1377Sgtb     krb5_ap_rep_enc_part *ap_rep_enc;
9010db1377Sgtb     krb5_error_code ret;
9110db1377Sgtb     krb5_data cipherresult;
9210db1377Sgtb     krb5_data clearresult;
93159d09a2SMark Phalan     /* Solaris Kerberos */
94159d09a2SMark Phalan     krb5_error *krberror = NULL;
9510db1377Sgtb     krb5_replay_data replay;
9610db1377Sgtb     krb5_keyblock *tmp;
9710db1377Sgtb 
9810db1377Sgtb     if (packet->length < 4)
9910db1377Sgtb 	/* either this, or the server is printing bad messages,
10010db1377Sgtb 	   or the caller passed in garbage */
10110db1377Sgtb 	return(KRB5KRB_AP_ERR_MODIFIED);
10210db1377Sgtb 
10310db1377Sgtb     ptr = packet->data;
10410db1377Sgtb 
10510db1377Sgtb     /* verify length */
10610db1377Sgtb 
10710db1377Sgtb     plen = (*ptr++ & 0xff);
10810db1377Sgtb     plen = (plen<<8) | (*ptr++ & 0xff);
10910db1377Sgtb 
110*55fea89dSDan Cross     if (plen != packet->length)
111159d09a2SMark Phalan 	{
112159d09a2SMark Phalan 		/*
113159d09a2SMark Phalan 		 * MS KDCs *may* send back a KRB_ERROR.  Although
114159d09a2SMark Phalan 		 * not 100% correct via RFC3244, it's something
115159d09a2SMark Phalan 		 * we can workaround here.
116159d09a2SMark Phalan 		 */
117159d09a2SMark Phalan 		if (krb5_is_krb_error(packet)) {
118159d09a2SMark Phalan 
119159d09a2SMark Phalan 			if ((ret = krb5_rd_error(context, packet, &krberror)))
120159d09a2SMark Phalan 			return(ret);
121159d09a2SMark Phalan 
122159d09a2SMark Phalan 			if (krberror->e_data.data  == NULL) {
123159d09a2SMark Phalan 				ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
124159d09a2SMark Phalan 				krb5_free_error(context, krberror);
125159d09a2SMark Phalan 				return (ret);
126159d09a2SMark Phalan 			}
127159d09a2SMark Phalan 		}
128159d09a2SMark Phalan 		else
129159d09a2SMark Phalan 		{
130159d09a2SMark Phalan 			return(KRB5KRB_AP_ERR_MODIFIED);
131159d09a2SMark Phalan 		}
132159d09a2SMark Phalan 	}
133159d09a2SMark Phalan 
134159d09a2SMark Phalan     /* Solaris Kerberos */
135159d09a2SMark Phalan     if (krberror != NULL) {
136159d09a2SMark Phalan 	krb5_free_error(context, krberror);
137159d09a2SMark Phalan 	krberror = NULL;
138159d09a2SMark Phalan     }
13910db1377Sgtb 
14010db1377Sgtb     /* verify version number */
14110db1377Sgtb 
14210db1377Sgtb     vno = (*ptr++ & 0xff);
14310db1377Sgtb     vno = (vno<<8) | (*ptr++ & 0xff);
14410db1377Sgtb 
14510db1377Sgtb     if (vno != 1)
14610db1377Sgtb 	return(KRB5KDC_ERR_BAD_PVNO);
14710db1377Sgtb 
14810db1377Sgtb     /* read, check ap-rep length */
14910db1377Sgtb 
15010db1377Sgtb     ap_rep.length = (*ptr++ & 0xff);
15110db1377Sgtb     ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
15210db1377Sgtb 
15310db1377Sgtb     if (ptr + ap_rep.length >= packet->data + packet->length)
15410db1377Sgtb 	return(KRB5KRB_AP_ERR_MODIFIED);
15510db1377Sgtb 
15610db1377Sgtb     if (ap_rep.length) {
15710db1377Sgtb 	/* verify ap_rep */
15810db1377Sgtb 	ap_rep.data = ptr;
15910db1377Sgtb 	ptr += ap_rep.length;
16010db1377Sgtb 
16110db1377Sgtb 	/*
16210db1377Sgtb 	 * Save send_subkey to later smash recv_subkey.
16310db1377Sgtb 	 */
16410db1377Sgtb 	ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
16510db1377Sgtb 	if (ret)
16610db1377Sgtb 	    return ret;
16710db1377Sgtb 
16810db1377Sgtb 	ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
16910db1377Sgtb 	if (ret) {
17010db1377Sgtb 	    krb5_free_keyblock(context, tmp);
17110db1377Sgtb 	    return(ret);
17210db1377Sgtb 	}
17310db1377Sgtb 
17410db1377Sgtb 	krb5_free_ap_rep_enc_part(context, ap_rep_enc);
17510db1377Sgtb 
17610db1377Sgtb 	/* extract and decrypt the result */
17710db1377Sgtb 
17810db1377Sgtb 	cipherresult.data = ptr;
17910db1377Sgtb 	cipherresult.length = (packet->data + packet->length) - ptr;
18010db1377Sgtb 
18110db1377Sgtb 	/*
18210db1377Sgtb 	 * Smash recv_subkey to be send_subkey, per spec.
18310db1377Sgtb 	 */
18410db1377Sgtb 	ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
18510db1377Sgtb 	krb5_free_keyblock(context, tmp);
18610db1377Sgtb 	if (ret)
18710db1377Sgtb 	    return ret;
18810db1377Sgtb 
18910db1377Sgtb 	ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
19010db1377Sgtb 			   &replay);
19110db1377Sgtb 
19210db1377Sgtb 	if (ret)
19310db1377Sgtb 	    return(ret);
19410db1377Sgtb     } else {
19510db1377Sgtb 	cipherresult.data = ptr;
19610db1377Sgtb 	cipherresult.length = (packet->data + packet->length) - ptr;
19710db1377Sgtb 
19810db1377Sgtb 	if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
19910db1377Sgtb 	    return(ret);
20010db1377Sgtb 
20110db1377Sgtb 	clearresult = krberror->e_data;
20210db1377Sgtb     }
20310db1377Sgtb 
20410db1377Sgtb     if (clearresult.length < 2) {
20510db1377Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
20610db1377Sgtb 	goto cleanup;
20710db1377Sgtb     }
20810db1377Sgtb 
20910db1377Sgtb     ptr = clearresult.data;
21010db1377Sgtb 
21110db1377Sgtb     *result_code = (*ptr++ & 0xff);
21210db1377Sgtb     *result_code = (*result_code<<8) | (*ptr++ & 0xff);
21310db1377Sgtb 
21410db1377Sgtb     if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
21510db1377Sgtb 	(*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
21610db1377Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
21710db1377Sgtb 	goto cleanup;
21810db1377Sgtb     }
21910db1377Sgtb 
22010db1377Sgtb     /* all success replies should be authenticated/encrypted */
22110db1377Sgtb 
22210db1377Sgtb     if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
22310db1377Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
22410db1377Sgtb 	goto cleanup;
22510db1377Sgtb     }
22610db1377Sgtb 
22710db1377Sgtb     result_data->length = (clearresult.data + clearresult.length) - ptr;
22810db1377Sgtb 
22910db1377Sgtb     if (result_data->length) {
23010db1377Sgtb 	result_data->data = (char *) malloc(result_data->length);
23110db1377Sgtb 	if (result_data->data == NULL) {
23210db1377Sgtb 	    ret = ENOMEM;
23310db1377Sgtb 	    goto cleanup;
23410db1377Sgtb 	}
23510db1377Sgtb 	memcpy(result_data->data, ptr, result_data->length);
23610db1377Sgtb     } else {
23710db1377Sgtb 	result_data->data = NULL;
23810db1377Sgtb     }
23910db1377Sgtb 
24010db1377Sgtb     ret = 0;
24110db1377Sgtb 
24210db1377Sgtb cleanup:
24310db1377Sgtb     if (ap_rep.length) {
24410db1377Sgtb 	krb5_xfree(clearresult.data);
24510db1377Sgtb     } else {
24610db1377Sgtb 	krb5_free_error(context, krberror);
24710db1377Sgtb     }
24810db1377Sgtb 
24910db1377Sgtb     return(ret);
25010db1377Sgtb }
25110db1377Sgtb 
25210db1377Sgtb krb5_error_code KRB5_CALLCONV
krb5_chpw_result_code_string(krb5_context context,int result_code,char ** code_string)25310db1377Sgtb krb5_chpw_result_code_string(krb5_context context, int result_code, char **code_string)
25410db1377Sgtb {
25510db1377Sgtb    switch (result_code) {
25610db1377Sgtb    case KRB5_KPASSWD_MALFORMED:
25710db1377Sgtb       *code_string = "Malformed request error";
25810db1377Sgtb       break;
25910db1377Sgtb    case KRB5_KPASSWD_HARDERROR:
26010db1377Sgtb       *code_string = "Server error";
26110db1377Sgtb       break;
26210db1377Sgtb    case KRB5_KPASSWD_AUTHERROR:
26310db1377Sgtb       *code_string = "Authentication error";
26410db1377Sgtb       break;
26510db1377Sgtb    case KRB5_KPASSWD_SOFTERROR:
26610db1377Sgtb       *code_string = "Password change rejected";
26710db1377Sgtb       break;
26810db1377Sgtb    default:
26910db1377Sgtb       *code_string = "Password change failed";
27010db1377Sgtb       break;
27110db1377Sgtb    }
27210db1377Sgtb 
27310db1377Sgtb    return(0);
27410db1377Sgtb }
27510db1377Sgtb 
276*55fea89dSDan Cross 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)27710db1377Sgtb krb5int_mk_setpw_req(
27810db1377Sgtb      krb5_context context,
27910db1377Sgtb      krb5_auth_context auth_context,
28010db1377Sgtb      krb5_data *ap_req,
28110db1377Sgtb      krb5_principal targprinc,
28210db1377Sgtb      char *passwd,
28310db1377Sgtb      krb5_data *packet )
28410db1377Sgtb {
28510db1377Sgtb     krb5_error_code ret;
28610db1377Sgtb     krb5_data	cipherpw;
28710db1377Sgtb     krb5_data	*encoded_setpw;
288ba7b222eSGlenn Barry     struct krb5_setpw_req req;
28910db1377Sgtb 
29010db1377Sgtb     char *ptr;
29110db1377Sgtb 
292ba7b222eSGlenn Barry     cipherpw.data = NULL;
293ba7b222eSGlenn Barry     cipherpw.length = 0;
294*55fea89dSDan Cross 
29510db1377Sgtb     if ((ret = krb5_auth_con_setflags(context, auth_context,
29610db1377Sgtb 				      KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
29710db1377Sgtb 		return(ret);
29810db1377Sgtb 
299ba7b222eSGlenn Barry     req.target = targprinc;
300ba7b222eSGlenn Barry     req.password.data = passwd;
301ba7b222eSGlenn Barry     req.password.length = strlen(passwd);
302ba7b222eSGlenn Barry     ret = encode_krb5_setpw_req(&req, &encoded_setpw);
30310db1377Sgtb     if (ret) {
30410db1377Sgtb 	return ret;
30510db1377Sgtb     }
30610db1377Sgtb 
30710db1377Sgtb     if ( (ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) {
30810db1377Sgtb 	krb5_free_data( context, encoded_setpw);
30910db1377Sgtb 	return(ret);
31010db1377Sgtb     }
31110db1377Sgtb     krb5_free_data( context, encoded_setpw);
312*55fea89dSDan Cross 
31310db1377Sgtb 
31410db1377Sgtb     packet->length = 6 + ap_req->length + cipherpw.length;
31510db1377Sgtb     packet->data = (char *) malloc(packet->length);
31610db1377Sgtb     if (packet->data  == NULL) {
31710db1377Sgtb 	ret = ENOMEM;
31810db1377Sgtb 	goto cleanup;
31910db1377Sgtb     }
32010db1377Sgtb     ptr = packet->data;
32110db1377Sgtb /*
32210db1377Sgtb ** build the packet -
32310db1377Sgtb */
32410db1377Sgtb /* put in the length */
32510db1377Sgtb     *ptr++ = (packet->length>>8) & 0xff;
32610db1377Sgtb     *ptr++ = packet->length & 0xff;
32710db1377Sgtb /* put in the version */
32810db1377Sgtb     *ptr++ = (char)0xff;
32910db1377Sgtb     *ptr++ = (char)0x80;
33010db1377Sgtb /* the ap_req length is big endian */
33110db1377Sgtb     *ptr++ = (ap_req->length>>8) & 0xff;
33210db1377Sgtb     *ptr++ = ap_req->length & 0xff;
33310db1377Sgtb /* put in the request data */
33410db1377Sgtb     memcpy(ptr, ap_req->data, ap_req->length);
33510db1377Sgtb     ptr += ap_req->length;
33610db1377Sgtb /*
33710db1377Sgtb ** put in the "private" password data -
33810db1377Sgtb */
33910db1377Sgtb     memcpy(ptr, cipherpw.data, cipherpw.length);
34010db1377Sgtb     ret = 0;
34110db1377Sgtb  cleanup:
34210db1377Sgtb     if (cipherpw.data)
34310db1377Sgtb 	krb5_free_data_contents(context, &cipherpw);
34410db1377Sgtb     if ((ret != 0) && packet->data) {
34510db1377Sgtb 	free( packet->data);
34610db1377Sgtb 	packet->data = NULL;
34710db1377Sgtb     }
34810db1377Sgtb     return ret;
34910db1377Sgtb }
35010db1377Sgtb 
351*55fea89dSDan Cross 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)35210db1377Sgtb krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet,
35310db1377Sgtb      int *result_code, krb5_data *result_data )
35410db1377Sgtb {
35510db1377Sgtb     char *ptr;
35610db1377Sgtb     unsigned int message_length, version_number;
35710db1377Sgtb     krb5_data ap_rep;
35810db1377Sgtb     krb5_ap_rep_enc_part *ap_rep_enc;
35910db1377Sgtb     krb5_error_code ret;
36010db1377Sgtb     krb5_data cipherresult;
36110db1377Sgtb     krb5_data clearresult;
36210db1377Sgtb     krb5_keyblock *tmpkey;
36310db1377Sgtb /*
36410db1377Sgtb ** validate the packet length -
36510db1377Sgtb */
36610db1377Sgtb     if (packet->length < 4)
36710db1377Sgtb 	return(KRB5KRB_AP_ERR_MODIFIED);
36810db1377Sgtb 
36910db1377Sgtb     ptr = packet->data;
37010db1377Sgtb 
37110db1377Sgtb /*
37210db1377Sgtb ** see if it is an error
37310db1377Sgtb */
37410db1377Sgtb     if (krb5_is_krb_error(packet)) {
37510db1377Sgtb 	krb5_error *krberror;
37610db1377Sgtb 	if ((ret = krb5_rd_error(context, packet, &krberror)))
37710db1377Sgtb 	    return(ret);
37810db1377Sgtb 	if (krberror->e_data.data  == NULL) {
37910db1377Sgtb 	    ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
38010db1377Sgtb 	    krb5_free_error(context, krberror);
38110db1377Sgtb 	    return (ret);
38210db1377Sgtb 	}
38310db1377Sgtb 	clearresult = krberror->e_data;
38410db1377Sgtb 	krberror->e_data.data  = NULL; /*So we can free it later*/
38510db1377Sgtb 	krberror->e_data.length = 0;
38610db1377Sgtb 	krb5_free_error(context, krberror);
387*55fea89dSDan Cross 
38810db1377Sgtb     } else { /* Not an error*/
38910db1377Sgtb 
39010db1377Sgtb /*
39110db1377Sgtb ** validate the message length -
392*55fea89dSDan Cross ** length is big endian
39310db1377Sgtb */
39410db1377Sgtb 	message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
39510db1377Sgtb 	ptr += 2;
39610db1377Sgtb /*
39710db1377Sgtb ** make sure the message length and packet length agree -
39810db1377Sgtb */
39910db1377Sgtb 	if (message_length != packet->length)
40010db1377Sgtb 	    return(KRB5KRB_AP_ERR_MODIFIED);
40110db1377Sgtb /*
40210db1377Sgtb ** get the version number -
40310db1377Sgtb */
40410db1377Sgtb 	version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
40510db1377Sgtb 	ptr += 2;
40610db1377Sgtb /*
40710db1377Sgtb ** make sure we support the version returned -
40810db1377Sgtb */
40910db1377Sgtb /*
41010db1377Sgtb ** set password version is 0xff80, change password version is 1
41110db1377Sgtb */
412159d09a2SMark Phalan 	if (version_number != 1 && version_number != 0xff80)
41310db1377Sgtb 	    return(KRB5KDC_ERR_BAD_PVNO);
41410db1377Sgtb /*
41510db1377Sgtb ** now fill in ap_rep with the reply -
41610db1377Sgtb */
41710db1377Sgtb /*
41810db1377Sgtb ** get the reply length -
41910db1377Sgtb */
42010db1377Sgtb 	ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
42110db1377Sgtb 	ptr += 2;
42210db1377Sgtb /*
42310db1377Sgtb ** validate ap_rep length agrees with the packet length -
42410db1377Sgtb */
42510db1377Sgtb 	if (ptr + ap_rep.length >= packet->data + packet->length)
42610db1377Sgtb 	    return(KRB5KRB_AP_ERR_MODIFIED);
42710db1377Sgtb /*
42810db1377Sgtb ** if data was returned, set the ap_rep ptr -
42910db1377Sgtb */
43010db1377Sgtb 	if( ap_rep.length ) {
43110db1377Sgtb 	    ap_rep.data = ptr;
43210db1377Sgtb 	    ptr += ap_rep.length;
43310db1377Sgtb 
43410db1377Sgtb 	    /*
43510db1377Sgtb 	     * Save send_subkey to later smash recv_subkey.
43610db1377Sgtb 	     */
43710db1377Sgtb 	    ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
43810db1377Sgtb 	    if (ret)
43910db1377Sgtb 		return ret;
44010db1377Sgtb 
44110db1377Sgtb 	    ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
44210db1377Sgtb 	    if (ret) {
44310db1377Sgtb 		krb5_free_keyblock(context, tmpkey);
44410db1377Sgtb 		return(ret);
44510db1377Sgtb 	    }
44610db1377Sgtb 
44710db1377Sgtb 	    krb5_free_ap_rep_enc_part(context, ap_rep_enc);
44810db1377Sgtb /*
44910db1377Sgtb ** now decrypt the result -
45010db1377Sgtb */
45110db1377Sgtb 	    cipherresult.data = ptr;
45210db1377Sgtb 	    cipherresult.length = (packet->data + packet->length) - ptr;
45310db1377Sgtb 
45410db1377Sgtb 	    /*
45510db1377Sgtb 	     * Smash recv_subkey to be send_subkey, per spec.
45610db1377Sgtb 	     */
45710db1377Sgtb 	    ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
45810db1377Sgtb 	    krb5_free_keyblock(context, tmpkey);
45910db1377Sgtb 	    if (ret)
46010db1377Sgtb 		return ret;
46110db1377Sgtb 
46210db1377Sgtb 	    ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
46310db1377Sgtb 			       NULL);
46410db1377Sgtb 	    if (ret)
46510db1377Sgtb 		return(ret);
46610db1377Sgtb 	} /*We got an ap_rep*/
46710db1377Sgtb 	else
46810db1377Sgtb 	    return (KRB5KRB_AP_ERR_MODIFIED);
46910db1377Sgtb     } /*Response instead of error*/
47010db1377Sgtb 
47110db1377Sgtb /*
472*55fea89dSDan Cross ** validate the cleartext length
47310db1377Sgtb */
47410db1377Sgtb     if (clearresult.length < 2) {
47510db1377Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
47610db1377Sgtb 	goto cleanup;
47710db1377Sgtb     }
47810db1377Sgtb /*
47910db1377Sgtb ** now decode the result -
48010db1377Sgtb */
48110db1377Sgtb     ptr = clearresult.data;
48210db1377Sgtb 
48310db1377Sgtb     *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
48410db1377Sgtb     ptr += 2;
48510db1377Sgtb 
48610db1377Sgtb /*
48710db1377Sgtb ** result code 5 is access denied
48810db1377Sgtb */
48910db1377Sgtb     if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5))
49010db1377Sgtb     {
49110db1377Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
49210db1377Sgtb 	goto cleanup;
49310db1377Sgtb     }
49410db1377Sgtb /*
49510db1377Sgtb ** all success replies should be authenticated/encrypted
49610db1377Sgtb */
49710db1377Sgtb     if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) )
49810db1377Sgtb     {
49910db1377Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
50010db1377Sgtb 	goto cleanup;
50110db1377Sgtb     }
50210db1377Sgtb 
50310db1377Sgtb     if (result_data) {
50410db1377Sgtb 	result_data->length = (clearresult.data + clearresult.length) - ptr;
50510db1377Sgtb 
50610db1377Sgtb 	if (result_data->length)
50710db1377Sgtb 	{
50810db1377Sgtb 	    result_data->data = (char *) malloc(result_data->length);
50910db1377Sgtb 	    if (result_data->data)
51010db1377Sgtb 		memcpy(result_data->data, ptr, result_data->length);
51110db1377Sgtb 	}
51210db1377Sgtb 	else
51310db1377Sgtb 	    result_data->data = NULL;
51410db1377Sgtb     }
51510db1377Sgtb     ret = 0;
51610db1377Sgtb 
51710db1377Sgtb  cleanup:
51810db1377Sgtb     krb5_free_data_contents(context, &clearresult);
51910db1377Sgtb     return(ret);
52010db1377Sgtb }
52110db1377Sgtb 
522*55fea89dSDan Cross krb5_error_code
krb5int_setpw_result_code_string(krb5_context context,int result_code,const char ** code_string)52310db1377Sgtb krb5int_setpw_result_code_string( krb5_context context, int result_code, const char **code_string )
52410db1377Sgtb {
52510db1377Sgtb    switch (result_code)
52610db1377Sgtb    {
52710db1377Sgtb    case KRB5_KPASSWD_MALFORMED:
52810db1377Sgtb       *code_string = "Malformed request error";
52910db1377Sgtb       break;
53010db1377Sgtb    case KRB5_KPASSWD_HARDERROR:
53110db1377Sgtb       *code_string = "Server error";
53210db1377Sgtb       break;
53310db1377Sgtb    case KRB5_KPASSWD_AUTHERROR:
53410db1377Sgtb       *code_string = "Authentication error";
53510db1377Sgtb       break;
53610db1377Sgtb    case KRB5_KPASSWD_SOFTERROR:
53710db1377Sgtb       *code_string = "Password change rejected";
53810db1377Sgtb       break;
53910db1377Sgtb    case 5: /* access denied */
54010db1377Sgtb       *code_string = "Access denied";
54110db1377Sgtb       break;
54210db1377Sgtb    case 6:	/* bad version */
54310db1377Sgtb       *code_string = "Wrong protocol version";
54410db1377Sgtb       break;
54510db1377Sgtb    case 7: /* initial flag is needed */
54610db1377Sgtb       *code_string = "Initial password required";
54710db1377Sgtb       break;
54810db1377Sgtb    case 0:
54906307114SToomas Soome       *code_string = "Success";
55006307114SToomas Soome       break;
55110db1377Sgtb    default:
55210db1377Sgtb       *code_string = "Password change failed";
55310db1377Sgtb       break;
55410db1377Sgtb    }
55510db1377Sgtb 
55610db1377Sgtb    return(0);
55710db1377Sgtb }
55810db1377Sgtb 
559