xref: /onnv-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/chpw.c (revision 10598:6f30db2c2cd0)
1*10598SGlenn.Barry@Sun.COM /*
2*10598SGlenn.Barry@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3*10598SGlenn.Barry@Sun.COM  * Use is subject to license terms.
4*10598SGlenn.Barry@Sun.COM  */
51770Sgtb /*
61770Sgtb ** set password functions added by Paul W. Nelson, Thursby Software Systems, Inc.
71770Sgtb */
81770Sgtb #include <string.h>
91770Sgtb 
101770Sgtb #include "k5-int.h"
117934SMark.Phalan@Sun.COM /* Solaris Kerberos */
127934SMark.Phalan@Sun.COM /* #include "krb5_err.h" */
131770Sgtb #include "auth_con.h"
141770Sgtb 
151770Sgtb 
161770Sgtb krb5_error_code
krb5int_mk_chpw_req(krb5_context context,krb5_auth_context auth_context,krb5_data * ap_req,char * passwd,krb5_data * packet)177934SMark.Phalan@Sun.COM krb5int_mk_chpw_req(
187934SMark.Phalan@Sun.COM 	krb5_context context,
197934SMark.Phalan@Sun.COM 	krb5_auth_context auth_context,
207934SMark.Phalan@Sun.COM 	krb5_data *ap_req,
217934SMark.Phalan@Sun.COM 	char *passwd,
227934SMark.Phalan@Sun.COM 	krb5_data *packet)
231770Sgtb {
241770Sgtb     krb5_error_code ret = 0;
251770Sgtb     krb5_data clearpw;
261770Sgtb     krb5_data cipherpw;
271770Sgtb     krb5_replay_data replay;
281770Sgtb     char *ptr;
291770Sgtb 
301770Sgtb     cipherpw.data = NULL;
311770Sgtb 
321770Sgtb     if ((ret = krb5_auth_con_setflags(context, auth_context,
331770Sgtb 				      KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
341770Sgtb 	  goto cleanup;
351770Sgtb 
361770Sgtb     clearpw.length = strlen(passwd);
371770Sgtb     clearpw.data = passwd;
381770Sgtb 
391770Sgtb     if ((ret = krb5_mk_priv(context, auth_context,
401770Sgtb 			    &clearpw, &cipherpw, &replay)))
411770Sgtb       goto cleanup;
421770Sgtb 
431770Sgtb     packet->length = 6 + ap_req->length + cipherpw.length;
441770Sgtb     packet->data = (char *) malloc(packet->length);
451770Sgtb     if (packet->data == NULL)
467934SMark.Phalan@Sun.COM 	{
471770Sgtb 	    ret = ENOMEM;
481770Sgtb 	    goto cleanup;
497934SMark.Phalan@Sun.COM 	}
501770Sgtb     ptr = packet->data;
511770Sgtb 
521770Sgtb     /* length */
531770Sgtb 
547934SMark.Phalan@Sun.COM     *ptr++ = (packet->length>> 8) & 0xff;
551770Sgtb     *ptr++ = packet->length & 0xff;
561770Sgtb 
571770Sgtb     /* version == 0x0001 big-endian */
581770Sgtb 
591770Sgtb     *ptr++ = 0;
601770Sgtb     *ptr++ = 1;
611770Sgtb 
621770Sgtb     /* ap_req length, big-endian */
631770Sgtb 
641770Sgtb     *ptr++ = (ap_req->length>>8) & 0xff;
651770Sgtb     *ptr++ = ap_req->length & 0xff;
661770Sgtb 
671770Sgtb     /* ap-req data */
681770Sgtb 
691770Sgtb     memcpy(ptr, ap_req->data, ap_req->length);
701770Sgtb     ptr += ap_req->length;
711770Sgtb 
721770Sgtb     /* krb-priv of password */
731770Sgtb 
741770Sgtb     memcpy(ptr, cipherpw.data, cipherpw.length);
751770Sgtb 
761770Sgtb cleanup:
771770Sgtb     if(cipherpw.data != NULL)  /* allocated by krb5_mk_priv */
781770Sgtb       free(cipherpw.data);
791770Sgtb 
801770Sgtb     return(ret);
811770Sgtb }
821770Sgtb 
831770Sgtb 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)841770Sgtb krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
851770Sgtb {
861770Sgtb     char *ptr;
871770Sgtb     int plen, vno;
881770Sgtb     krb5_data ap_rep;
891770Sgtb     krb5_ap_rep_enc_part *ap_rep_enc;
901770Sgtb     krb5_error_code ret;
911770Sgtb     krb5_data cipherresult;
921770Sgtb     krb5_data clearresult;
937934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
947934SMark.Phalan@Sun.COM     krb5_error *krberror = NULL;
951770Sgtb     krb5_replay_data replay;
961770Sgtb     krb5_keyblock *tmp;
971770Sgtb 
981770Sgtb     if (packet->length < 4)
991770Sgtb 	/* either this, or the server is printing bad messages,
1001770Sgtb 	   or the caller passed in garbage */
1011770Sgtb 	return(KRB5KRB_AP_ERR_MODIFIED);
1021770Sgtb 
1031770Sgtb     ptr = packet->data;
1041770Sgtb 
1051770Sgtb     /* verify length */
1061770Sgtb 
1071770Sgtb     plen = (*ptr++ & 0xff);
1081770Sgtb     plen = (plen<<8) | (*ptr++ & 0xff);
1091770Sgtb 
1107934SMark.Phalan@Sun.COM     if (plen != packet->length)
1117934SMark.Phalan@Sun.COM 	{
1127934SMark.Phalan@Sun.COM 		/*
1137934SMark.Phalan@Sun.COM 		 * MS KDCs *may* send back a KRB_ERROR.  Although
1147934SMark.Phalan@Sun.COM 		 * not 100% correct via RFC3244, it's something
1157934SMark.Phalan@Sun.COM 		 * we can workaround here.
1167934SMark.Phalan@Sun.COM 		 */
1177934SMark.Phalan@Sun.COM 		if (krb5_is_krb_error(packet)) {
1187934SMark.Phalan@Sun.COM 
1197934SMark.Phalan@Sun.COM 			if ((ret = krb5_rd_error(context, packet, &krberror)))
1207934SMark.Phalan@Sun.COM 			return(ret);
1217934SMark.Phalan@Sun.COM 
1227934SMark.Phalan@Sun.COM 			if (krberror->e_data.data  == NULL) {
1237934SMark.Phalan@Sun.COM 				ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
1247934SMark.Phalan@Sun.COM 				krb5_free_error(context, krberror);
1257934SMark.Phalan@Sun.COM 				return (ret);
1267934SMark.Phalan@Sun.COM 			}
1277934SMark.Phalan@Sun.COM 		}
1287934SMark.Phalan@Sun.COM 		else
1297934SMark.Phalan@Sun.COM 		{
1307934SMark.Phalan@Sun.COM 			return(KRB5KRB_AP_ERR_MODIFIED);
1317934SMark.Phalan@Sun.COM 		}
1327934SMark.Phalan@Sun.COM 	}
1337934SMark.Phalan@Sun.COM 
1347934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
1357934SMark.Phalan@Sun.COM     if (krberror != NULL) {
1367934SMark.Phalan@Sun.COM 	krb5_free_error(context, krberror);
1377934SMark.Phalan@Sun.COM 	krberror = NULL;
1387934SMark.Phalan@Sun.COM     }
1391770Sgtb 
1401770Sgtb     /* verify version number */
1411770Sgtb 
1421770Sgtb     vno = (*ptr++ & 0xff);
1431770Sgtb     vno = (vno<<8) | (*ptr++ & 0xff);
1441770Sgtb 
1451770Sgtb     if (vno != 1)
1461770Sgtb 	return(KRB5KDC_ERR_BAD_PVNO);
1471770Sgtb 
1481770Sgtb     /* read, check ap-rep length */
1491770Sgtb 
1501770Sgtb     ap_rep.length = (*ptr++ & 0xff);
1511770Sgtb     ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
1521770Sgtb 
1531770Sgtb     if (ptr + ap_rep.length >= packet->data + packet->length)
1541770Sgtb 	return(KRB5KRB_AP_ERR_MODIFIED);
1551770Sgtb 
1561770Sgtb     if (ap_rep.length) {
1571770Sgtb 	/* verify ap_rep */
1581770Sgtb 	ap_rep.data = ptr;
1591770Sgtb 	ptr += ap_rep.length;
1601770Sgtb 
1611770Sgtb 	/*
1621770Sgtb 	 * Save send_subkey to later smash recv_subkey.
1631770Sgtb 	 */
1641770Sgtb 	ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
1651770Sgtb 	if (ret)
1661770Sgtb 	    return ret;
1671770Sgtb 
1681770Sgtb 	ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
1691770Sgtb 	if (ret) {
1701770Sgtb 	    krb5_free_keyblock(context, tmp);
1711770Sgtb 	    return(ret);
1721770Sgtb 	}
1731770Sgtb 
1741770Sgtb 	krb5_free_ap_rep_enc_part(context, ap_rep_enc);
1751770Sgtb 
1761770Sgtb 	/* extract and decrypt the result */
1771770Sgtb 
1781770Sgtb 	cipherresult.data = ptr;
1791770Sgtb 	cipherresult.length = (packet->data + packet->length) - ptr;
1801770Sgtb 
1811770Sgtb 	/*
1821770Sgtb 	 * Smash recv_subkey to be send_subkey, per spec.
1831770Sgtb 	 */
1841770Sgtb 	ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
1851770Sgtb 	krb5_free_keyblock(context, tmp);
1861770Sgtb 	if (ret)
1871770Sgtb 	    return ret;
1881770Sgtb 
1891770Sgtb 	ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
1901770Sgtb 			   &replay);
1911770Sgtb 
1921770Sgtb 	if (ret)
1931770Sgtb 	    return(ret);
1941770Sgtb     } else {
1951770Sgtb 	cipherresult.data = ptr;
1961770Sgtb 	cipherresult.length = (packet->data + packet->length) - ptr;
1971770Sgtb 
1981770Sgtb 	if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
1991770Sgtb 	    return(ret);
2001770Sgtb 
2011770Sgtb 	clearresult = krberror->e_data;
2021770Sgtb     }
2031770Sgtb 
2041770Sgtb     if (clearresult.length < 2) {
2051770Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
2061770Sgtb 	goto cleanup;
2071770Sgtb     }
2081770Sgtb 
2091770Sgtb     ptr = clearresult.data;
2101770Sgtb 
2111770Sgtb     *result_code = (*ptr++ & 0xff);
2121770Sgtb     *result_code = (*result_code<<8) | (*ptr++ & 0xff);
2131770Sgtb 
2141770Sgtb     if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
2151770Sgtb 	(*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
2161770Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
2171770Sgtb 	goto cleanup;
2181770Sgtb     }
2191770Sgtb 
2201770Sgtb     /* all success replies should be authenticated/encrypted */
2211770Sgtb 
2221770Sgtb     if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
2231770Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
2241770Sgtb 	goto cleanup;
2251770Sgtb     }
2261770Sgtb 
2271770Sgtb     result_data->length = (clearresult.data + clearresult.length) - ptr;
2281770Sgtb 
2291770Sgtb     if (result_data->length) {
2301770Sgtb 	result_data->data = (char *) malloc(result_data->length);
2311770Sgtb 	if (result_data->data == NULL) {
2321770Sgtb 	    ret = ENOMEM;
2331770Sgtb 	    goto cleanup;
2341770Sgtb 	}
2351770Sgtb 	memcpy(result_data->data, ptr, result_data->length);
2361770Sgtb     } else {
2371770Sgtb 	result_data->data = NULL;
2381770Sgtb     }
2391770Sgtb 
2401770Sgtb     ret = 0;
2411770Sgtb 
2421770Sgtb cleanup:
2431770Sgtb     if (ap_rep.length) {
2441770Sgtb 	krb5_xfree(clearresult.data);
2451770Sgtb     } else {
2461770Sgtb 	krb5_free_error(context, krberror);
2471770Sgtb     }
2481770Sgtb 
2491770Sgtb     return(ret);
2501770Sgtb }
2511770Sgtb 
2521770Sgtb krb5_error_code KRB5_CALLCONV
krb5_chpw_result_code_string(krb5_context context,int result_code,char ** code_string)2531770Sgtb krb5_chpw_result_code_string(krb5_context context, int result_code, char **code_string)
2541770Sgtb {
2551770Sgtb    switch (result_code) {
2561770Sgtb    case KRB5_KPASSWD_MALFORMED:
2571770Sgtb       *code_string = "Malformed request error";
2581770Sgtb       break;
2591770Sgtb    case KRB5_KPASSWD_HARDERROR:
2601770Sgtb       *code_string = "Server error";
2611770Sgtb       break;
2621770Sgtb    case KRB5_KPASSWD_AUTHERROR:
2631770Sgtb       *code_string = "Authentication error";
2641770Sgtb       break;
2651770Sgtb    case KRB5_KPASSWD_SOFTERROR:
2661770Sgtb       *code_string = "Password change rejected";
2671770Sgtb       break;
2681770Sgtb    default:
2691770Sgtb       *code_string = "Password change failed";
2701770Sgtb       break;
2711770Sgtb    }
2721770Sgtb 
2731770Sgtb    return(0);
2741770Sgtb }
2751770Sgtb 
2761770Sgtb 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)2771770Sgtb krb5int_mk_setpw_req(
2781770Sgtb      krb5_context context,
2791770Sgtb      krb5_auth_context auth_context,
2801770Sgtb      krb5_data *ap_req,
2811770Sgtb      krb5_principal targprinc,
2821770Sgtb      char *passwd,
2831770Sgtb      krb5_data *packet )
2841770Sgtb {
2851770Sgtb     krb5_error_code ret;
2861770Sgtb     krb5_data	cipherpw;
2871770Sgtb     krb5_data	*encoded_setpw;
288*10598SGlenn.Barry@Sun.COM     struct krb5_setpw_req req;
2891770Sgtb 
2901770Sgtb     char *ptr;
2911770Sgtb 
292*10598SGlenn.Barry@Sun.COM     cipherpw.data = NULL;
293*10598SGlenn.Barry@Sun.COM     cipherpw.length = 0;
2941770Sgtb 
2951770Sgtb     if ((ret = krb5_auth_con_setflags(context, auth_context,
2961770Sgtb 				      KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
2971770Sgtb 		return(ret);
2981770Sgtb 
299*10598SGlenn.Barry@Sun.COM     req.target = targprinc;
300*10598SGlenn.Barry@Sun.COM     req.password.data = passwd;
301*10598SGlenn.Barry@Sun.COM     req.password.length = strlen(passwd);
302*10598SGlenn.Barry@Sun.COM     ret = encode_krb5_setpw_req(&req, &encoded_setpw);
3031770Sgtb     if (ret) {
3041770Sgtb 	return ret;
3051770Sgtb     }
3061770Sgtb 
3071770Sgtb     if ( (ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) {
3081770Sgtb 	krb5_free_data( context, encoded_setpw);
3091770Sgtb 	return(ret);
3101770Sgtb     }
3111770Sgtb     krb5_free_data( context, encoded_setpw);
3121770Sgtb 
3131770Sgtb 
3141770Sgtb     packet->length = 6 + ap_req->length + cipherpw.length;
3151770Sgtb     packet->data = (char *) malloc(packet->length);
3161770Sgtb     if (packet->data  == NULL) {
3171770Sgtb 	ret = ENOMEM;
3181770Sgtb 	goto cleanup;
3191770Sgtb     }
3201770Sgtb     ptr = packet->data;
3211770Sgtb /*
3221770Sgtb ** build the packet -
3231770Sgtb */
3241770Sgtb /* put in the length */
3251770Sgtb     *ptr++ = (packet->length>>8) & 0xff;
3261770Sgtb     *ptr++ = packet->length & 0xff;
3271770Sgtb /* put in the version */
3281770Sgtb     *ptr++ = (char)0xff;
3291770Sgtb     *ptr++ = (char)0x80;
3301770Sgtb /* the ap_req length is big endian */
3311770Sgtb     *ptr++ = (ap_req->length>>8) & 0xff;
3321770Sgtb     *ptr++ = ap_req->length & 0xff;
3331770Sgtb /* put in the request data */
3341770Sgtb     memcpy(ptr, ap_req->data, ap_req->length);
3351770Sgtb     ptr += ap_req->length;
3361770Sgtb /*
3371770Sgtb ** put in the "private" password data -
3381770Sgtb */
3391770Sgtb     memcpy(ptr, cipherpw.data, cipherpw.length);
3401770Sgtb     ret = 0;
3411770Sgtb  cleanup:
3421770Sgtb     if (cipherpw.data)
3431770Sgtb 	krb5_free_data_contents(context, &cipherpw);
3441770Sgtb     if ((ret != 0) && packet->data) {
3451770Sgtb 	free( packet->data);
3461770Sgtb 	packet->data = NULL;
3471770Sgtb     }
3481770Sgtb     return ret;
3491770Sgtb }
3501770Sgtb 
3511770Sgtb 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)3521770Sgtb krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet,
3531770Sgtb      int *result_code, krb5_data *result_data )
3541770Sgtb {
3551770Sgtb     char *ptr;
3561770Sgtb     unsigned int message_length, version_number;
3571770Sgtb     krb5_data ap_rep;
3581770Sgtb     krb5_ap_rep_enc_part *ap_rep_enc;
3591770Sgtb     krb5_error_code ret;
3601770Sgtb     krb5_data cipherresult;
3611770Sgtb     krb5_data clearresult;
3621770Sgtb     krb5_keyblock *tmpkey;
3631770Sgtb /*
3641770Sgtb ** validate the packet length -
3651770Sgtb */
3661770Sgtb     if (packet->length < 4)
3671770Sgtb 	return(KRB5KRB_AP_ERR_MODIFIED);
3681770Sgtb 
3691770Sgtb     ptr = packet->data;
3701770Sgtb 
3711770Sgtb /*
3721770Sgtb ** see if it is an error
3731770Sgtb */
3741770Sgtb     if (krb5_is_krb_error(packet)) {
3751770Sgtb 	krb5_error *krberror;
3761770Sgtb 	if ((ret = krb5_rd_error(context, packet, &krberror)))
3771770Sgtb 	    return(ret);
3781770Sgtb 	if (krberror->e_data.data  == NULL) {
3791770Sgtb 	    ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
3801770Sgtb 	    krb5_free_error(context, krberror);
3811770Sgtb 	    return (ret);
3821770Sgtb 	}
3831770Sgtb 	clearresult = krberror->e_data;
3841770Sgtb 	krberror->e_data.data  = NULL; /*So we can free it later*/
3851770Sgtb 	krberror->e_data.length = 0;
3861770Sgtb 	krb5_free_error(context, krberror);
3871770Sgtb 
3881770Sgtb     } else { /* Not an error*/
3891770Sgtb 
3901770Sgtb /*
3911770Sgtb ** validate the message length -
3921770Sgtb ** length is big endian
3931770Sgtb */
3941770Sgtb 	message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
3951770Sgtb 	ptr += 2;
3961770Sgtb /*
3971770Sgtb ** make sure the message length and packet length agree -
3981770Sgtb */
3991770Sgtb 	if (message_length != packet->length)
4001770Sgtb 	    return(KRB5KRB_AP_ERR_MODIFIED);
4011770Sgtb /*
4021770Sgtb ** get the version number -
4031770Sgtb */
4041770Sgtb 	version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
4051770Sgtb 	ptr += 2;
4061770Sgtb /*
4071770Sgtb ** make sure we support the version returned -
4081770Sgtb */
4091770Sgtb /*
4101770Sgtb ** set password version is 0xff80, change password version is 1
4111770Sgtb */
4127934SMark.Phalan@Sun.COM 	if (version_number != 1 && version_number != 0xff80)
4131770Sgtb 	    return(KRB5KDC_ERR_BAD_PVNO);
4141770Sgtb /*
4151770Sgtb ** now fill in ap_rep with the reply -
4161770Sgtb */
4171770Sgtb /*
4181770Sgtb ** get the reply length -
4191770Sgtb */
4201770Sgtb 	ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
4211770Sgtb 	ptr += 2;
4221770Sgtb /*
4231770Sgtb ** validate ap_rep length agrees with the packet length -
4241770Sgtb */
4251770Sgtb 	if (ptr + ap_rep.length >= packet->data + packet->length)
4261770Sgtb 	    return(KRB5KRB_AP_ERR_MODIFIED);
4271770Sgtb /*
4281770Sgtb ** if data was returned, set the ap_rep ptr -
4291770Sgtb */
4301770Sgtb 	if( ap_rep.length ) {
4311770Sgtb 	    ap_rep.data = ptr;
4321770Sgtb 	    ptr += ap_rep.length;
4331770Sgtb 
4341770Sgtb 	    /*
4351770Sgtb 	     * Save send_subkey to later smash recv_subkey.
4361770Sgtb 	     */
4371770Sgtb 	    ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
4381770Sgtb 	    if (ret)
4391770Sgtb 		return ret;
4401770Sgtb 
4411770Sgtb 	    ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
4421770Sgtb 	    if (ret) {
4431770Sgtb 		krb5_free_keyblock(context, tmpkey);
4441770Sgtb 		return(ret);
4451770Sgtb 	    }
4461770Sgtb 
4471770Sgtb 	    krb5_free_ap_rep_enc_part(context, ap_rep_enc);
4481770Sgtb /*
4491770Sgtb ** now decrypt the result -
4501770Sgtb */
4511770Sgtb 	    cipherresult.data = ptr;
4521770Sgtb 	    cipherresult.length = (packet->data + packet->length) - ptr;
4531770Sgtb 
4541770Sgtb 	    /*
4551770Sgtb 	     * Smash recv_subkey to be send_subkey, per spec.
4561770Sgtb 	     */
4571770Sgtb 	    ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
4581770Sgtb 	    krb5_free_keyblock(context, tmpkey);
4591770Sgtb 	    if (ret)
4601770Sgtb 		return ret;
4611770Sgtb 
4621770Sgtb 	    ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
4631770Sgtb 			       NULL);
4641770Sgtb 	    if (ret)
4651770Sgtb 		return(ret);
4661770Sgtb 	} /*We got an ap_rep*/
4671770Sgtb 	else
4681770Sgtb 	    return (KRB5KRB_AP_ERR_MODIFIED);
4691770Sgtb     } /*Response instead of error*/
4701770Sgtb 
4711770Sgtb /*
4721770Sgtb ** validate the cleartext length
4731770Sgtb */
4741770Sgtb     if (clearresult.length < 2) {
4751770Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
4761770Sgtb 	goto cleanup;
4771770Sgtb     }
4781770Sgtb /*
4791770Sgtb ** now decode the result -
4801770Sgtb */
4811770Sgtb     ptr = clearresult.data;
4821770Sgtb 
4831770Sgtb     *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
4841770Sgtb     ptr += 2;
4851770Sgtb 
4861770Sgtb /*
4871770Sgtb ** result code 5 is access denied
4881770Sgtb */
4891770Sgtb     if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5))
4901770Sgtb     {
4911770Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
4921770Sgtb 	goto cleanup;
4931770Sgtb     }
4941770Sgtb /*
4951770Sgtb ** all success replies should be authenticated/encrypted
4961770Sgtb */
4971770Sgtb     if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) )
4981770Sgtb     {
4991770Sgtb 	ret = KRB5KRB_AP_ERR_MODIFIED;
5001770Sgtb 	goto cleanup;
5011770Sgtb     }
5021770Sgtb 
5031770Sgtb     if (result_data) {
5041770Sgtb 	result_data->length = (clearresult.data + clearresult.length) - ptr;
5051770Sgtb 
5061770Sgtb 	if (result_data->length)
5071770Sgtb 	{
5081770Sgtb 	    result_data->data = (char *) malloc(result_data->length);
5091770Sgtb 	    if (result_data->data)
5101770Sgtb 		memcpy(result_data->data, ptr, result_data->length);
5111770Sgtb 	}
5121770Sgtb 	else
5131770Sgtb 	    result_data->data = NULL;
5141770Sgtb     }
5151770Sgtb     ret = 0;
5161770Sgtb 
5171770Sgtb  cleanup:
5181770Sgtb     krb5_free_data_contents(context, &clearresult);
5191770Sgtb     return(ret);
5201770Sgtb }
5211770Sgtb 
5221770Sgtb krb5_error_code
krb5int_setpw_result_code_string(krb5_context context,int result_code,const char ** code_string)5231770Sgtb krb5int_setpw_result_code_string( krb5_context context, int result_code, const char **code_string )
5241770Sgtb {
5251770Sgtb    switch (result_code)
5261770Sgtb    {
5271770Sgtb    case KRB5_KPASSWD_MALFORMED:
5281770Sgtb       *code_string = "Malformed request error";
5291770Sgtb       break;
5301770Sgtb    case KRB5_KPASSWD_HARDERROR:
5311770Sgtb       *code_string = "Server error";
5321770Sgtb       break;
5331770Sgtb    case KRB5_KPASSWD_AUTHERROR:
5341770Sgtb       *code_string = "Authentication error";
5351770Sgtb       break;
5361770Sgtb    case KRB5_KPASSWD_SOFTERROR:
5371770Sgtb       *code_string = "Password change rejected";
5381770Sgtb       break;
5391770Sgtb    case 5: /* access denied */
5401770Sgtb       *code_string = "Access denied";
5411770Sgtb       break;
5421770Sgtb    case 6:	/* bad version */
5431770Sgtb       *code_string = "Wrong protocol version";
5441770Sgtb       break;
5451770Sgtb    case 7: /* initial flag is needed */
5461770Sgtb       *code_string = "Initial password required";
5471770Sgtb       break;
5481770Sgtb    case 0:
5491770Sgtb 	  *code_string = "Success";
5501770Sgtb    default:
5511770Sgtb       *code_string = "Password change failed";
5521770Sgtb       break;
5531770Sgtb    }
5541770Sgtb 
5551770Sgtb    return(0);
5561770Sgtb }
5571770Sgtb 
558