10Sstevel@tonic-gate /*
2*7934SMark.Phalan@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate
70Sstevel@tonic-gate #include <string.h>
80Sstevel@tonic-gate
9*7934SMark.Phalan@Sun.COM #include "k5-int.h"
100Sstevel@tonic-gate #include <kadm5/admin.h>
110Sstevel@tonic-gate #include <client_internal.h>
12*7934SMark.Phalan@Sun.COM #include "auth_con.h"
130Sstevel@tonic-gate #include <locale.h>
140Sstevel@tonic-gate
15*7934SMark.Phalan@Sun.COM
16*7934SMark.Phalan@Sun.COM krb5_error_code
krb5int_mk_chpw_req(krb5_context context,krb5_auth_context auth_context,krb5_data * ap_req,char * passwd,krb5_data * packet)17*7934SMark.Phalan@Sun.COM krb5int_mk_chpw_req(
18*7934SMark.Phalan@Sun.COM krb5_context context,
19*7934SMark.Phalan@Sun.COM krb5_auth_context auth_context,
20*7934SMark.Phalan@Sun.COM krb5_data *ap_req,
21*7934SMark.Phalan@Sun.COM char *passwd,
22*7934SMark.Phalan@Sun.COM krb5_data *packet)
230Sstevel@tonic-gate {
24*7934SMark.Phalan@Sun.COM krb5_error_code ret = 0;
25*7934SMark.Phalan@Sun.COM krb5_data clearpw;
26*7934SMark.Phalan@Sun.COM krb5_data cipherpw;
27*7934SMark.Phalan@Sun.COM krb5_replay_data replay;
28*7934SMark.Phalan@Sun.COM char *ptr;
29*7934SMark.Phalan@Sun.COM
30*7934SMark.Phalan@Sun.COM cipherpw.data = NULL;
310Sstevel@tonic-gate
32*7934SMark.Phalan@Sun.COM if ((ret = krb5_auth_con_setflags(context, auth_context,
33*7934SMark.Phalan@Sun.COM KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
34*7934SMark.Phalan@Sun.COM goto cleanup;
350Sstevel@tonic-gate
36*7934SMark.Phalan@Sun.COM clearpw.length = strlen(passwd);
37*7934SMark.Phalan@Sun.COM clearpw.data = passwd;
380Sstevel@tonic-gate
39*7934SMark.Phalan@Sun.COM if ((ret = krb5_mk_priv(context, auth_context,
40*7934SMark.Phalan@Sun.COM &clearpw, &cipherpw, &replay)))
41*7934SMark.Phalan@Sun.COM goto cleanup;
420Sstevel@tonic-gate
43*7934SMark.Phalan@Sun.COM packet->length = 6 + ap_req->length + cipherpw.length;
44*7934SMark.Phalan@Sun.COM packet->data = (char *) malloc(packet->length);
45*7934SMark.Phalan@Sun.COM if (packet->data == NULL)
460Sstevel@tonic-gate {
47*7934SMark.Phalan@Sun.COM ret = ENOMEM;
48*7934SMark.Phalan@Sun.COM goto cleanup;
490Sstevel@tonic-gate }
50*7934SMark.Phalan@Sun.COM ptr = packet->data;
510Sstevel@tonic-gate
52*7934SMark.Phalan@Sun.COM /* length */
53*7934SMark.Phalan@Sun.COM
54*7934SMark.Phalan@Sun.COM *ptr++ = (packet->length>> 8) & 0xff;
55*7934SMark.Phalan@Sun.COM *ptr++ = packet->length & 0xff;
560Sstevel@tonic-gate
57*7934SMark.Phalan@Sun.COM /* version == 0x0001 big-endian
58*7934SMark.Phalan@Sun.COM * NOTE: when MS and MIT start supporting the latest
59*7934SMark.Phalan@Sun.COM * version of the passwd change protocol (v2),
60*7934SMark.Phalan@Sun.COM * this value will change to 2.
61*7934SMark.Phalan@Sun.COM */
62*7934SMark.Phalan@Sun.COM *ptr++ = 0;
63*7934SMark.Phalan@Sun.COM *ptr++ = 1;
64*7934SMark.Phalan@Sun.COM
65*7934SMark.Phalan@Sun.COM /* ap_req length, big-endian */
660Sstevel@tonic-gate
67*7934SMark.Phalan@Sun.COM *ptr++ = (ap_req->length>>8) & 0xff;
68*7934SMark.Phalan@Sun.COM *ptr++ = ap_req->length & 0xff;
69*7934SMark.Phalan@Sun.COM
70*7934SMark.Phalan@Sun.COM /* ap-req data */
710Sstevel@tonic-gate
72*7934SMark.Phalan@Sun.COM memcpy(ptr, ap_req->data, ap_req->length);
73*7934SMark.Phalan@Sun.COM ptr += ap_req->length;
740Sstevel@tonic-gate
75*7934SMark.Phalan@Sun.COM /* krb-priv of password */
76*7934SMark.Phalan@Sun.COM
77*7934SMark.Phalan@Sun.COM memcpy(ptr, cipherpw.data, cipherpw.length);
780Sstevel@tonic-gate
790Sstevel@tonic-gate cleanup:
80*7934SMark.Phalan@Sun.COM if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */
81*7934SMark.Phalan@Sun.COM free(cipherpw.data);
82*7934SMark.Phalan@Sun.COM
83*7934SMark.Phalan@Sun.COM return(ret);
840Sstevel@tonic-gate }
850Sstevel@tonic-gate
86*7934SMark.Phalan@Sun.COM 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)87*7934SMark.Phalan@Sun.COM krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
880Sstevel@tonic-gate {
89*7934SMark.Phalan@Sun.COM char *ptr;
90*7934SMark.Phalan@Sun.COM int plen, vno;
91*7934SMark.Phalan@Sun.COM krb5_data ap_rep;
92*7934SMark.Phalan@Sun.COM krb5_ap_rep_enc_part *ap_rep_enc;
93*7934SMark.Phalan@Sun.COM krb5_error_code ret;
94*7934SMark.Phalan@Sun.COM krb5_data cipherresult;
95*7934SMark.Phalan@Sun.COM krb5_data clearresult;
96*7934SMark.Phalan@Sun.COM krb5_error *krberror;
97*7934SMark.Phalan@Sun.COM krb5_replay_data replay;
98*7934SMark.Phalan@Sun.COM krb5_keyblock *tmp;
99*7934SMark.Phalan@Sun.COM int local_result_code;
1000Sstevel@tonic-gate
101*7934SMark.Phalan@Sun.COM if (packet->length < 4)
102*7934SMark.Phalan@Sun.COM /* either this, or the server is printing bad messages,
103*7934SMark.Phalan@Sun.COM or the caller passed in garbage */
104*7934SMark.Phalan@Sun.COM return(KRB5KRB_AP_ERR_MODIFIED);
105*7934SMark.Phalan@Sun.COM
106*7934SMark.Phalan@Sun.COM ptr = packet->data;
107*7934SMark.Phalan@Sun.COM
108*7934SMark.Phalan@Sun.COM /* verify length */
109*7934SMark.Phalan@Sun.COM
110*7934SMark.Phalan@Sun.COM plen = (*ptr++ & 0xff);
111*7934SMark.Phalan@Sun.COM plen = (plen<<8) | (*ptr++ & 0xff);
112*7934SMark.Phalan@Sun.COM
113*7934SMark.Phalan@Sun.COM if (plen != packet->length)
114*7934SMark.Phalan@Sun.COM {
1150Sstevel@tonic-gate /*
116*7934SMark.Phalan@Sun.COM * MS KDCs *may* send back a KRB_ERROR. Although
117*7934SMark.Phalan@Sun.COM * not 100% correct via RFC3244, it's something
118*7934SMark.Phalan@Sun.COM * we can workaround here.
1190Sstevel@tonic-gate */
120*7934SMark.Phalan@Sun.COM if (krb5_is_krb_error(packet)) {
1210Sstevel@tonic-gate
122*7934SMark.Phalan@Sun.COM if ((ret = krb5_rd_error(context, packet, &krberror)))
123*7934SMark.Phalan@Sun.COM return(ret);
1240Sstevel@tonic-gate
125*7934SMark.Phalan@Sun.COM if (krberror->e_data.data == NULL) {
126*7934SMark.Phalan@Sun.COM ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
127*7934SMark.Phalan@Sun.COM krb5_free_error(context, krberror);
128*7934SMark.Phalan@Sun.COM return (ret);
129*7934SMark.Phalan@Sun.COM }
130*7934SMark.Phalan@Sun.COM }
131*7934SMark.Phalan@Sun.COM else
132*7934SMark.Phalan@Sun.COM {
133*7934SMark.Phalan@Sun.COM return(KRB5KRB_AP_ERR_MODIFIED);
134*7934SMark.Phalan@Sun.COM }
135*7934SMark.Phalan@Sun.COM }
136*7934SMark.Phalan@Sun.COM
1370Sstevel@tonic-gate
138*7934SMark.Phalan@Sun.COM /* verify version number */
1390Sstevel@tonic-gate
140*7934SMark.Phalan@Sun.COM vno = (*ptr++ & 0xff);
141*7934SMark.Phalan@Sun.COM vno = (vno<<8) | (*ptr++ & 0xff);
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate * when the servers update to v2 of the protocol,
1450Sstevel@tonic-gate * "2" will be a valid version number here
1460Sstevel@tonic-gate */
1470Sstevel@tonic-gate if (vno != 1 && vno != 2)
1480Sstevel@tonic-gate return (KRB5KDC_ERR_BAD_PVNO);
1490Sstevel@tonic-gate
150*7934SMark.Phalan@Sun.COM /* read, check ap-rep length */
1510Sstevel@tonic-gate
152*7934SMark.Phalan@Sun.COM ap_rep.length = (*ptr++ & 0xff);
153*7934SMark.Phalan@Sun.COM ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
15496Ssemery
155*7934SMark.Phalan@Sun.COM if (ptr + ap_rep.length >= packet->data + packet->length)
156*7934SMark.Phalan@Sun.COM return(KRB5KRB_AP_ERR_MODIFIED);
1570Sstevel@tonic-gate
158*7934SMark.Phalan@Sun.COM if (ap_rep.length) {
159*7934SMark.Phalan@Sun.COM /* verify ap_rep */
160*7934SMark.Phalan@Sun.COM ap_rep.data = ptr;
161*7934SMark.Phalan@Sun.COM ptr += ap_rep.length;
1620Sstevel@tonic-gate
163*7934SMark.Phalan@Sun.COM /*
164*7934SMark.Phalan@Sun.COM * Save send_subkey to later smash recv_subkey.
165*7934SMark.Phalan@Sun.COM */
166*7934SMark.Phalan@Sun.COM ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
167*7934SMark.Phalan@Sun.COM if (ret)
168*7934SMark.Phalan@Sun.COM return ret;
1690Sstevel@tonic-gate
170*7934SMark.Phalan@Sun.COM ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
171*7934SMark.Phalan@Sun.COM if (ret) {
172*7934SMark.Phalan@Sun.COM krb5_free_keyblock(context, tmp);
173*7934SMark.Phalan@Sun.COM return(ret);
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate
176*7934SMark.Phalan@Sun.COM krb5_free_ap_rep_enc_part(context, ap_rep_enc);
177*7934SMark.Phalan@Sun.COM
178*7934SMark.Phalan@Sun.COM /* extract and decrypt the result */
179*7934SMark.Phalan@Sun.COM
180*7934SMark.Phalan@Sun.COM cipherresult.data = ptr;
181*7934SMark.Phalan@Sun.COM cipherresult.length = (packet->data + packet->length) - ptr;
182*7934SMark.Phalan@Sun.COM
183*7934SMark.Phalan@Sun.COM /*
184*7934SMark.Phalan@Sun.COM * Smash recv_subkey to be send_subkey, per spec.
185*7934SMark.Phalan@Sun.COM */
186*7934SMark.Phalan@Sun.COM ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
187*7934SMark.Phalan@Sun.COM krb5_free_keyblock(context, tmp);
188*7934SMark.Phalan@Sun.COM if (ret)
189*7934SMark.Phalan@Sun.COM return ret;
190*7934SMark.Phalan@Sun.COM
191*7934SMark.Phalan@Sun.COM ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
192*7934SMark.Phalan@Sun.COM &replay);
1930Sstevel@tonic-gate
194*7934SMark.Phalan@Sun.COM if (ret)
195*7934SMark.Phalan@Sun.COM return(ret);
196*7934SMark.Phalan@Sun.COM } else {
197*7934SMark.Phalan@Sun.COM cipherresult.data = ptr;
198*7934SMark.Phalan@Sun.COM cipherresult.length = (packet->data + packet->length) - ptr;
199*7934SMark.Phalan@Sun.COM
200*7934SMark.Phalan@Sun.COM if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
201*7934SMark.Phalan@Sun.COM return(ret);
2020Sstevel@tonic-gate
203*7934SMark.Phalan@Sun.COM clearresult = krberror->e_data;
204*7934SMark.Phalan@Sun.COM }
205*7934SMark.Phalan@Sun.COM
206*7934SMark.Phalan@Sun.COM if (clearresult.length < 2) {
207*7934SMark.Phalan@Sun.COM ret = KRB5KRB_AP_ERR_MODIFIED;
208*7934SMark.Phalan@Sun.COM goto cleanup;
209*7934SMark.Phalan@Sun.COM }
210*7934SMark.Phalan@Sun.COM
211*7934SMark.Phalan@Sun.COM ptr = clearresult.data;
212*7934SMark.Phalan@Sun.COM
213*7934SMark.Phalan@Sun.COM local_result_code = (*ptr++ & 0xff);
214*7934SMark.Phalan@Sun.COM local_result_code = (local_result_code<<8) | (*ptr++ & 0xff);
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate if (result_code)
2170Sstevel@tonic-gate *result_code = local_result_code;
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate * Make sure the result code is in range for this
2210Sstevel@tonic-gate * protocol.
2220Sstevel@tonic-gate */
223*7934SMark.Phalan@Sun.COM if ((local_result_code < KRB5_KPASSWD_SUCCESS) ||
224*7934SMark.Phalan@Sun.COM (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
225*7934SMark.Phalan@Sun.COM ret = KRB5KRB_AP_ERR_MODIFIED;
226*7934SMark.Phalan@Sun.COM goto cleanup;
227*7934SMark.Phalan@Sun.COM }
2280Sstevel@tonic-gate
229*7934SMark.Phalan@Sun.COM /* all success replies should be authenticated/encrypted */
230*7934SMark.Phalan@Sun.COM
231*7934SMark.Phalan@Sun.COM if ((ap_rep.length == 0) && (local_result_code == KRB5_KPASSWD_SUCCESS)) {
232*7934SMark.Phalan@Sun.COM ret = KRB5KRB_AP_ERR_MODIFIED;
233*7934SMark.Phalan@Sun.COM goto cleanup;
234*7934SMark.Phalan@Sun.COM }
2350Sstevel@tonic-gate
236*7934SMark.Phalan@Sun.COM result_data->length = (clearresult.data + clearresult.length) - ptr;
2370Sstevel@tonic-gate
238*7934SMark.Phalan@Sun.COM if (result_data->length) {
239*7934SMark.Phalan@Sun.COM result_data->data = (char *) malloc(result_data->length);
240*7934SMark.Phalan@Sun.COM if (result_data->data == NULL) {
241*7934SMark.Phalan@Sun.COM ret = ENOMEM;
242*7934SMark.Phalan@Sun.COM goto cleanup;
2430Sstevel@tonic-gate }
244*7934SMark.Phalan@Sun.COM memcpy(result_data->data, ptr, result_data->length);
245*7934SMark.Phalan@Sun.COM } else {
246*7934SMark.Phalan@Sun.COM result_data->data = NULL;
247*7934SMark.Phalan@Sun.COM }
2480Sstevel@tonic-gate
249*7934SMark.Phalan@Sun.COM ret = 0;
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate cleanup:
252*7934SMark.Phalan@Sun.COM if (ap_rep.length) {
253*7934SMark.Phalan@Sun.COM krb5_xfree(clearresult.data);
254*7934SMark.Phalan@Sun.COM } else {
255*7934SMark.Phalan@Sun.COM krb5_free_error(context, krberror);
256*7934SMark.Phalan@Sun.COM }
2570Sstevel@tonic-gate
258*7934SMark.Phalan@Sun.COM return(ret);
2590Sstevel@tonic-gate }
260