17c478bd9Sstevel@tonic-gate /*
2*159d09a2SMark Phalan * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
37c478bd9Sstevel@tonic-gate * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate */
57c478bd9Sstevel@tonic-gate
67c478bd9Sstevel@tonic-gate
77c478bd9Sstevel@tonic-gate #include <string.h>
87c478bd9Sstevel@tonic-gate
9*159d09a2SMark Phalan #include "k5-int.h"
107c478bd9Sstevel@tonic-gate #include <kadm5/admin.h>
117c478bd9Sstevel@tonic-gate #include <client_internal.h>
12*159d09a2SMark Phalan #include "auth_con.h"
137c478bd9Sstevel@tonic-gate #include <locale.h>
147c478bd9Sstevel@tonic-gate
15*159d09a2SMark Phalan
16*159d09a2SMark Phalan 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*159d09a2SMark Phalan krb5int_mk_chpw_req(
18*159d09a2SMark Phalan krb5_context context,
19*159d09a2SMark Phalan krb5_auth_context auth_context,
20*159d09a2SMark Phalan krb5_data *ap_req,
21*159d09a2SMark Phalan char *passwd,
22*159d09a2SMark Phalan krb5_data *packet)
237c478bd9Sstevel@tonic-gate {
247c478bd9Sstevel@tonic-gate krb5_error_code ret = 0;
257c478bd9Sstevel@tonic-gate krb5_data clearpw;
267c478bd9Sstevel@tonic-gate krb5_data cipherpw;
277c478bd9Sstevel@tonic-gate krb5_replay_data replay;
287c478bd9Sstevel@tonic-gate char *ptr;
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate cipherpw.data = NULL;
317c478bd9Sstevel@tonic-gate
32*159d09a2SMark Phalan if ((ret = krb5_auth_con_setflags(context, auth_context,
33*159d09a2SMark Phalan KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
347c478bd9Sstevel@tonic-gate goto cleanup;
357c478bd9Sstevel@tonic-gate
367c478bd9Sstevel@tonic-gate clearpw.length = strlen(passwd);
377c478bd9Sstevel@tonic-gate clearpw.data = passwd;
387c478bd9Sstevel@tonic-gate
39*159d09a2SMark Phalan if ((ret = krb5_mk_priv(context, auth_context,
40*159d09a2SMark Phalan &clearpw, &cipherpw, &replay)))
417c478bd9Sstevel@tonic-gate goto cleanup;
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate packet->length = 6 + ap_req->length + cipherpw.length;
447c478bd9Sstevel@tonic-gate packet->data = (char *) malloc(packet->length);
457c478bd9Sstevel@tonic-gate if (packet->data == NULL)
467c478bd9Sstevel@tonic-gate {
477c478bd9Sstevel@tonic-gate ret = ENOMEM;
487c478bd9Sstevel@tonic-gate goto cleanup;
497c478bd9Sstevel@tonic-gate }
507c478bd9Sstevel@tonic-gate ptr = packet->data;
517c478bd9Sstevel@tonic-gate
527c478bd9Sstevel@tonic-gate /* length */
53*159d09a2SMark Phalan
547c478bd9Sstevel@tonic-gate *ptr++ = (packet->length>> 8) & 0xff;
557c478bd9Sstevel@tonic-gate *ptr++ = packet->length & 0xff;
567c478bd9Sstevel@tonic-gate
57*159d09a2SMark Phalan /* version == 0x0001 big-endian
587c478bd9Sstevel@tonic-gate * NOTE: when MS and MIT start supporting the latest
597c478bd9Sstevel@tonic-gate * version of the passwd change protocol (v2),
607c478bd9Sstevel@tonic-gate * this value will change to 2.
617c478bd9Sstevel@tonic-gate */
627c478bd9Sstevel@tonic-gate *ptr++ = 0;
637c478bd9Sstevel@tonic-gate *ptr++ = 1;
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate /* ap_req length, big-endian */
66*159d09a2SMark Phalan
677c478bd9Sstevel@tonic-gate *ptr++ = (ap_req->length>>8) & 0xff;
687c478bd9Sstevel@tonic-gate *ptr++ = ap_req->length & 0xff;
697c478bd9Sstevel@tonic-gate
707c478bd9Sstevel@tonic-gate /* ap-req data */
71*159d09a2SMark Phalan
727c478bd9Sstevel@tonic-gate memcpy(ptr, ap_req->data, ap_req->length);
737c478bd9Sstevel@tonic-gate ptr += ap_req->length;
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate /* krb-priv of password */
76*159d09a2SMark Phalan
777c478bd9Sstevel@tonic-gate memcpy(ptr, cipherpw.data, cipherpw.length);
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate cleanup:
807c478bd9Sstevel@tonic-gate if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */
817c478bd9Sstevel@tonic-gate free(cipherpw.data);
827c478bd9Sstevel@tonic-gate
837c478bd9Sstevel@tonic-gate return(ret);
847c478bd9Sstevel@tonic-gate }
857c478bd9Sstevel@tonic-gate
86*159d09a2SMark Phalan 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*159d09a2SMark Phalan krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate char *ptr;
907c478bd9Sstevel@tonic-gate int plen, vno;
917c478bd9Sstevel@tonic-gate krb5_data ap_rep;
927c478bd9Sstevel@tonic-gate krb5_ap_rep_enc_part *ap_rep_enc;
937c478bd9Sstevel@tonic-gate krb5_error_code ret;
947c478bd9Sstevel@tonic-gate krb5_data cipherresult;
957c478bd9Sstevel@tonic-gate krb5_data clearresult;
967c478bd9Sstevel@tonic-gate krb5_error *krberror;
977c478bd9Sstevel@tonic-gate krb5_replay_data replay;
987c478bd9Sstevel@tonic-gate krb5_keyblock *tmp;
997c478bd9Sstevel@tonic-gate int local_result_code;
1007c478bd9Sstevel@tonic-gate
1017c478bd9Sstevel@tonic-gate if (packet->length < 4)
102*159d09a2SMark Phalan /* either this, or the server is printing bad messages,
103*159d09a2SMark Phalan or the caller passed in garbage */
1047c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_MODIFIED);
1057c478bd9Sstevel@tonic-gate
1067c478bd9Sstevel@tonic-gate ptr = packet->data;
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate /* verify length */
109*159d09a2SMark Phalan
1107c478bd9Sstevel@tonic-gate plen = (*ptr++ & 0xff);
1117c478bd9Sstevel@tonic-gate plen = (plen<<8) | (*ptr++ & 0xff);
1127c478bd9Sstevel@tonic-gate
1137c478bd9Sstevel@tonic-gate if (plen != packet->length)
114*159d09a2SMark Phalan {
115*159d09a2SMark Phalan /*
116*159d09a2SMark Phalan * MS KDCs *may* send back a KRB_ERROR. Although
117*159d09a2SMark Phalan * not 100% correct via RFC3244, it's something
118*159d09a2SMark Phalan * we can workaround here.
119*159d09a2SMark Phalan */
120*159d09a2SMark Phalan if (krb5_is_krb_error(packet)) {
121*159d09a2SMark Phalan
122*159d09a2SMark Phalan if ((ret = krb5_rd_error(context, packet, &krberror)))
123*159d09a2SMark Phalan return(ret);
124*159d09a2SMark Phalan
125*159d09a2SMark Phalan if (krberror->e_data.data == NULL) {
126*159d09a2SMark Phalan ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
127*159d09a2SMark Phalan krb5_free_error(context, krberror);
128*159d09a2SMark Phalan return (ret);
129*159d09a2SMark Phalan }
130*159d09a2SMark Phalan }
131*159d09a2SMark Phalan else
132*159d09a2SMark Phalan {
1337c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_MODIFIED);
134*159d09a2SMark Phalan }
135*159d09a2SMark Phalan }
136*159d09a2SMark Phalan
1377c478bd9Sstevel@tonic-gate
1387c478bd9Sstevel@tonic-gate /* verify version number */
139*159d09a2SMark Phalan
1407c478bd9Sstevel@tonic-gate vno = (*ptr++ & 0xff);
1417c478bd9Sstevel@tonic-gate vno = (vno<<8) | (*ptr++ & 0xff);
1427c478bd9Sstevel@tonic-gate
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate * when the servers update to v2 of the protocol,
1457c478bd9Sstevel@tonic-gate * "2" will be a valid version number here
1467c478bd9Sstevel@tonic-gate */
1477c478bd9Sstevel@tonic-gate if (vno != 1 && vno != 2)
1487c478bd9Sstevel@tonic-gate return (KRB5KDC_ERR_BAD_PVNO);
1497c478bd9Sstevel@tonic-gate
1507c478bd9Sstevel@tonic-gate /* read, check ap-rep length */
151*159d09a2SMark Phalan
1527c478bd9Sstevel@tonic-gate ap_rep.length = (*ptr++ & 0xff);
1537c478bd9Sstevel@tonic-gate ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
1547c478bd9Sstevel@tonic-gate
1557c478bd9Sstevel@tonic-gate if (ptr + ap_rep.length >= packet->data + packet->length)
1567c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_MODIFIED);
1577c478bd9Sstevel@tonic-gate
1587c478bd9Sstevel@tonic-gate if (ap_rep.length) {
1597c478bd9Sstevel@tonic-gate /* verify ap_rep */
1607c478bd9Sstevel@tonic-gate ap_rep.data = ptr;
1617c478bd9Sstevel@tonic-gate ptr += ap_rep.length;
1627c478bd9Sstevel@tonic-gate
16345526e97Ssemery /*
16445526e97Ssemery * Save send_subkey to later smash recv_subkey.
16545526e97Ssemery */
16645526e97Ssemery ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
16745526e97Ssemery if (ret)
168*159d09a2SMark Phalan return ret;
1697c478bd9Sstevel@tonic-gate
170*159d09a2SMark Phalan ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
171*159d09a2SMark Phalan if (ret) {
17245526e97Ssemery krb5_free_keyblock(context, tmp);
17345526e97Ssemery return(ret);
17445526e97Ssemery }
17545526e97Ssemery
1767c478bd9Sstevel@tonic-gate krb5_free_ap_rep_enc_part(context, ap_rep_enc);
1777c478bd9Sstevel@tonic-gate
1787c478bd9Sstevel@tonic-gate /* extract and decrypt the result */
179*159d09a2SMark Phalan
1807c478bd9Sstevel@tonic-gate cipherresult.data = ptr;
1817c478bd9Sstevel@tonic-gate cipherresult.length = (packet->data + packet->length) - ptr;
1827c478bd9Sstevel@tonic-gate
1837c478bd9Sstevel@tonic-gate /*
18445526e97Ssemery * Smash recv_subkey to be send_subkey, per spec.
1857c478bd9Sstevel@tonic-gate */
18645526e97Ssemery ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
18745526e97Ssemery krb5_free_keyblock(context, tmp);
18845526e97Ssemery if (ret)
189*159d09a2SMark Phalan return ret;
1907c478bd9Sstevel@tonic-gate
191*159d09a2SMark Phalan ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
192*159d09a2SMark Phalan &replay);
1937c478bd9Sstevel@tonic-gate
1947c478bd9Sstevel@tonic-gate if (ret)
1957c478bd9Sstevel@tonic-gate return(ret);
1967c478bd9Sstevel@tonic-gate } else {
1977c478bd9Sstevel@tonic-gate cipherresult.data = ptr;
1987c478bd9Sstevel@tonic-gate cipherresult.length = (packet->data + packet->length) - ptr;
1997c478bd9Sstevel@tonic-gate
200*159d09a2SMark Phalan if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
2017c478bd9Sstevel@tonic-gate return(ret);
2027c478bd9Sstevel@tonic-gate
2037c478bd9Sstevel@tonic-gate clearresult = krberror->e_data;
2047c478bd9Sstevel@tonic-gate }
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate if (clearresult.length < 2) {
2077c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED;
2087c478bd9Sstevel@tonic-gate goto cleanup;
2097c478bd9Sstevel@tonic-gate }
2107c478bd9Sstevel@tonic-gate
2117c478bd9Sstevel@tonic-gate ptr = clearresult.data;
2127c478bd9Sstevel@tonic-gate
2137c478bd9Sstevel@tonic-gate local_result_code = (*ptr++ & 0xff);
2147c478bd9Sstevel@tonic-gate local_result_code = (local_result_code<<8) | (*ptr++ & 0xff);
2157c478bd9Sstevel@tonic-gate
2167c478bd9Sstevel@tonic-gate if (result_code)
2177c478bd9Sstevel@tonic-gate *result_code = local_result_code;
2187c478bd9Sstevel@tonic-gate
2197c478bd9Sstevel@tonic-gate /*
2207c478bd9Sstevel@tonic-gate * Make sure the result code is in range for this
2217c478bd9Sstevel@tonic-gate * protocol.
2227c478bd9Sstevel@tonic-gate */
2237c478bd9Sstevel@tonic-gate if ((local_result_code < KRB5_KPASSWD_SUCCESS) ||
2247c478bd9Sstevel@tonic-gate (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
2257c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED;
2267c478bd9Sstevel@tonic-gate goto cleanup;
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate /* all success replies should be authenticated/encrypted */
230*159d09a2SMark Phalan
231*159d09a2SMark Phalan if ((ap_rep.length == 0) && (local_result_code == KRB5_KPASSWD_SUCCESS)) {
2327c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED;
2337c478bd9Sstevel@tonic-gate goto cleanup;
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate
2367c478bd9Sstevel@tonic-gate result_data->length = (clearresult.data + clearresult.length) - ptr;
2377c478bd9Sstevel@tonic-gate
2387c478bd9Sstevel@tonic-gate if (result_data->length) {
2397c478bd9Sstevel@tonic-gate result_data->data = (char *) malloc(result_data->length);
2407c478bd9Sstevel@tonic-gate if (result_data->data == NULL) {
2417c478bd9Sstevel@tonic-gate ret = ENOMEM;
2427c478bd9Sstevel@tonic-gate goto cleanup;
2437c478bd9Sstevel@tonic-gate }
2447c478bd9Sstevel@tonic-gate memcpy(result_data->data, ptr, result_data->length);
2457c478bd9Sstevel@tonic-gate } else {
2467c478bd9Sstevel@tonic-gate result_data->data = NULL;
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gate ret = 0;
2507c478bd9Sstevel@tonic-gate
2517c478bd9Sstevel@tonic-gate cleanup:
2527c478bd9Sstevel@tonic-gate if (ap_rep.length) {
2537c478bd9Sstevel@tonic-gate krb5_xfree(clearresult.data);
2547c478bd9Sstevel@tonic-gate } else {
2557c478bd9Sstevel@tonic-gate krb5_free_error(context, krberror);
2567c478bd9Sstevel@tonic-gate }
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate return(ret);
2597c478bd9Sstevel@tonic-gate }
260