dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / krb5 / kadm5 / clnt / chpw.c
blobfcd433f6282eee49dcb7350f52ba4a290fe068f6
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
7 #include <string.h>
9 #include "k5-int.h"
10 #include <kadm5/admin.h>
11 #include <client_internal.h>
12 #include "auth_con.h"
13 #include <locale.h>
16 krb5_error_code
17 krb5int_mk_chpw_req(
18 krb5_context context,
19 krb5_auth_context auth_context,
20 krb5_data *ap_req,
21 char *passwd,
22 krb5_data *packet)
24 krb5_error_code ret = 0;
25 krb5_data clearpw;
26 krb5_data cipherpw;
27 krb5_replay_data replay;
28 char *ptr;
30 cipherpw.data = NULL;
32 if ((ret = krb5_auth_con_setflags(context, auth_context,
33 KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
34 goto cleanup;
36 clearpw.length = strlen(passwd);
37 clearpw.data = passwd;
39 if ((ret = krb5_mk_priv(context, auth_context,
40 &clearpw, &cipherpw, &replay)))
41 goto cleanup;
43 packet->length = 6 + ap_req->length + cipherpw.length;
44 packet->data = (char *) malloc(packet->length);
45 if (packet->data == NULL)
47 ret = ENOMEM;
48 goto cleanup;
50 ptr = packet->data;
52 /* length */
54 *ptr++ = (packet->length>> 8) & 0xff;
55 *ptr++ = packet->length & 0xff;
57 /* version == 0x0001 big-endian
58 * NOTE: when MS and MIT start supporting the latest
59 * version of the passwd change protocol (v2),
60 * this value will change to 2.
62 *ptr++ = 0;
63 *ptr++ = 1;
65 /* ap_req length, big-endian */
67 *ptr++ = (ap_req->length>>8) & 0xff;
68 *ptr++ = ap_req->length & 0xff;
70 /* ap-req data */
72 memcpy(ptr, ap_req->data, ap_req->length);
73 ptr += ap_req->length;
75 /* krb-priv of password */
77 memcpy(ptr, cipherpw.data, cipherpw.length);
79 cleanup:
80 free(cipherpw.data);
82 return(ret);
85 krb5_error_code
86 krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
88 char *ptr;
89 int plen, vno;
90 krb5_data ap_rep;
91 krb5_ap_rep_enc_part *ap_rep_enc;
92 krb5_error_code ret;
93 krb5_data cipherresult;
94 krb5_data clearresult;
95 krb5_error *krberror;
96 krb5_replay_data replay;
97 krb5_keyblock *tmp;
98 int local_result_code;
100 if (packet->length < 4)
101 /* either this, or the server is printing bad messages,
102 or the caller passed in garbage */
103 return(KRB5KRB_AP_ERR_MODIFIED);
105 ptr = packet->data;
107 /* verify length */
109 plen = (*ptr++ & 0xff);
110 plen = (plen<<8) | (*ptr++ & 0xff);
112 if (plen != packet->length)
115 * MS KDCs *may* send back a KRB_ERROR. Although
116 * not 100% correct via RFC3244, it's something
117 * we can workaround here.
119 if (krb5_is_krb_error(packet)) {
121 if ((ret = krb5_rd_error(context, packet, &krberror)))
122 return(ret);
124 if (krberror->e_data.data == NULL) {
125 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
126 krb5_free_error(context, krberror);
127 return (ret);
130 else
132 return(KRB5KRB_AP_ERR_MODIFIED);
137 /* verify version number */
139 vno = (*ptr++ & 0xff);
140 vno = (vno<<8) | (*ptr++ & 0xff);
143 * when the servers update to v2 of the protocol,
144 * "2" will be a valid version number here
146 if (vno != 1 && vno != 2)
147 return (KRB5KDC_ERR_BAD_PVNO);
149 /* read, check ap-rep length */
151 ap_rep.length = (*ptr++ & 0xff);
152 ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
154 if (ptr + ap_rep.length >= packet->data + packet->length)
155 return(KRB5KRB_AP_ERR_MODIFIED);
157 if (ap_rep.length) {
158 /* verify ap_rep */
159 ap_rep.data = ptr;
160 ptr += ap_rep.length;
163 * Save send_subkey to later smash recv_subkey.
165 ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
166 if (ret)
167 return ret;
169 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
170 if (ret) {
171 krb5_free_keyblock(context, tmp);
172 return(ret);
175 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
177 /* extract and decrypt the result */
179 cipherresult.data = ptr;
180 cipherresult.length = (packet->data + packet->length) - ptr;
183 * Smash recv_subkey to be send_subkey, per spec.
185 ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
186 krb5_free_keyblock(context, tmp);
187 if (ret)
188 return ret;
190 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
191 &replay);
193 if (ret)
194 return(ret);
195 } else {
196 cipherresult.data = ptr;
197 cipherresult.length = (packet->data + packet->length) - ptr;
199 if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
200 return(ret);
202 clearresult = krberror->e_data;
205 if (clearresult.length < 2) {
206 ret = KRB5KRB_AP_ERR_MODIFIED;
207 goto cleanup;
210 ptr = clearresult.data;
212 local_result_code = (*ptr++ & 0xff);
213 local_result_code = (local_result_code<<8) | (*ptr++ & 0xff);
215 if (result_code)
216 *result_code = local_result_code;
219 * Make sure the result code is in range for this
220 * protocol.
222 if ((local_result_code < KRB5_KPASSWD_SUCCESS) ||
223 (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
224 ret = KRB5KRB_AP_ERR_MODIFIED;
225 goto cleanup;
228 /* all success replies should be authenticated/encrypted */
230 if ((ap_rep.length == 0) && (local_result_code == KRB5_KPASSWD_SUCCESS)) {
231 ret = KRB5KRB_AP_ERR_MODIFIED;
232 goto cleanup;
235 result_data->length = (clearresult.data + clearresult.length) - ptr;
237 if (result_data->length) {
238 result_data->data = (char *) malloc(result_data->length);
239 if (result_data->data == NULL) {
240 ret = ENOMEM;
241 goto cleanup;
243 memcpy(result_data->data, ptr, result_data->length);
244 } else {
245 result_data->data = NULL;
248 ret = 0;
250 cleanup:
251 if (ap_rep.length) {
252 krb5_xfree(clearresult.data);
253 } else {
254 krb5_free_error(context, krberror);
257 return(ret);