1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/mk_req_ext.c */
3 /*
4 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 /*
28 *
29 * krb5_mk_req_extended()
30 */
31
32
33 #include "k5-int.h"
34 #include "int-proto.h"
35 #include "auth_con.h"
36
37 /*
38 Formats a KRB_AP_REQ message into outbuf, with more complete options than
39 krb_mk_req.
40
41 outbuf, ap_req_options, checksum, and ccache are used in the
42 same fashion as for krb5_mk_req.
43
44 creds is used to supply the credentials (ticket and session key) needed
45 to form the request.
46
47 if creds->ticket has no data (length == 0), then a ticket is obtained
48 from either the cache or the TGS, passing creds to krb5_get_credentials().
49 kdc_options specifies the options requested for the ticket to be used.
50 If a ticket with appropriate flags is not found in the cache, then these
51 options are passed on in a request to an appropriate KDC.
52
53 ap_req_options specifies the KRB_AP_REQ options desired.
54
55 if ap_req_options specifies AP_OPTS_USE_SESSION_KEY, then creds->ticket
56 must contain the appropriate ENC-TKT-IN-SKEY ticket.
57
58 checksum specifies the checksum to be used in the authenticator.
59
60 The outbuf buffer storage is allocated, and should be freed by the
61 caller when finished.
62
63 On an error return, the credentials pointed to by creds might have been
64 augmented with additional fields from the obtained credentials; the entire
65 credentials should be released by calling krb5_free_creds().
66
67 returns system errors
68 */
69
70 static krb5_error_code
71 make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
72 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
73 krb5_authdata ***authdata_out);
74
75 static krb5_error_code
76 generate_authenticator(krb5_context,
77 krb5_authenticator *, krb5_principal,
78 krb5_checksum *, krb5_key,
79 krb5_ui_4, krb5_authdata **,
80 krb5_authdata_context ad_context,
81 krb5_enctype *desired_etypes, krb5_boolean cbt_flag,
82 krb5_enctype tkt_enctype);
83
84 krb5_error_code KRB5_CALLCONV
krb5_mk_req_extended(krb5_context context,krb5_auth_context * auth_context,krb5_flags ap_req_options,krb5_data * in_data,krb5_creds * in_creds,krb5_data * outbuf)85 krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
86 krb5_flags ap_req_options, krb5_data *in_data,
87 krb5_creds *in_creds, krb5_data *outbuf)
88 {
89 krb5_error_code retval;
90 krb5_checksum checksum;
91 krb5_checksum *checksump = 0;
92 krb5_auth_context new_auth_context;
93 krb5_enctype *desired_etypes = NULL;
94
95 krb5_ap_req request;
96 krb5_data *scratch = 0;
97 krb5_data *toutbuf;
98 krb5_boolean cbt_flag = (ap_req_options & AP_OPTS_CBT_FLAG) != 0;
99
100 request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
101 request.authenticator.ciphertext.data = NULL;
102 request.ticket = 0;
103
104 if (!in_creds->ticket.length)
105 return(KRB5_NO_TKT_SUPPLIED);
106
107 if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) &&
108 !(ap_req_options & AP_OPTS_MUTUAL_REQUIRED))
109 return(EINVAL);
110
111 /* we need a native ticket */
112 if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
113 return(retval);
114
115 /* verify that the ticket is not expired */
116 if ((retval = krb5int_validate_times(context, &in_creds->times)) != 0)
117 goto cleanup;
118
119 /* generate auth_context if needed */
120 if (*auth_context == NULL) {
121 if ((retval = krb5_auth_con_init(context, &new_auth_context)))
122 goto cleanup;
123 *auth_context = new_auth_context;
124 }
125
126 if ((*auth_context)->key != NULL) {
127 krb5_k_free_key(context, (*auth_context)->key);
128 (*auth_context)->key = NULL;
129 }
130
131 /* set auth context keyblock */
132 if ((retval = krb5_k_create_key(context, &in_creds->keyblock,
133 &((*auth_context)->key))))
134 goto cleanup;
135
136 /* generate seq number if needed */
137 if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
138 || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
139 && ((*auth_context)->local_seq_number == 0)) {
140 if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock,
141 &(*auth_context)->local_seq_number)))
142 goto cleanup;
143 }
144
145 /* generate subkey if needed */
146 if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
147 retval = k5_generate_and_save_subkey(context, *auth_context,
148 &in_creds->keyblock,
149 in_creds->keyblock.enctype);
150 if (retval)
151 goto cleanup;
152 }
153
154
155 if (!in_data && (*auth_context)->checksum_func) {
156 retval = (*auth_context)->checksum_func( context,
157 *auth_context,
158 (*auth_context)->checksum_func_data,
159 &in_data);
160 if (retval)
161 goto cleanup;
162 }
163
164 if (in_data) {
165 if ((*auth_context)->req_cksumtype == 0x8003) {
166 /* XXX Special hack for GSSAPI */
167 checksum.checksum_type = 0x8003;
168 checksum.length = in_data->length;
169 checksum.contents = (krb5_octet *) in_data->data;
170 } else {
171 retval = krb5_k_make_checksum(context, 0, (*auth_context)->key,
172 KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
173 in_data, &checksum);
174 if (retval)
175 goto cleanup_cksum;
176 }
177 checksump = &checksum;
178 }
179
180 /* Generate authenticator */
181 if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
182 krb5_authenticator))) == NULL) {
183 retval = ENOMEM;
184 goto cleanup_cksum;
185 }
186
187 if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) {
188 if ((*auth_context)->permitted_etypes == NULL) {
189 retval = krb5_get_tgs_ktypes(context, in_creds->server, &desired_etypes);
190 if (retval)
191 goto cleanup_cksum;
192 } else
193 desired_etypes = (*auth_context)->permitted_etypes;
194 }
195
196 TRACE_MK_REQ(context, in_creds, (*auth_context)->local_seq_number,
197 (*auth_context)->send_subkey, &in_creds->keyblock);
198 if ((retval = generate_authenticator(context,
199 (*auth_context)->authentp,
200 in_creds->client, checksump,
201 (*auth_context)->send_subkey,
202 (*auth_context)->local_seq_number,
203 in_creds->authdata,
204 (*auth_context)->ad_context,
205 desired_etypes, cbt_flag,
206 in_creds->keyblock.enctype)))
207 goto cleanup_cksum;
208
209 /* encode the authenticator */
210 if ((retval = encode_krb5_authenticator((*auth_context)->authentp,
211 &scratch)))
212 goto cleanup_cksum;
213
214 /* call the encryption routine */
215 if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
216 KRB5_KEYUSAGE_AP_REQ_AUTH,
217 scratch, &request.authenticator)))
218 goto cleanup_cksum;
219
220 if ((retval = encode_krb5_ap_req(&request, &toutbuf)))
221 goto cleanup_cksum;
222 *outbuf = *toutbuf;
223
224 free(toutbuf);
225
226 cleanup_cksum:
227 /* Null out these fields, to prevent pointer sharing problems;
228 * they were supplied by the caller
229 */
230 if ((*auth_context)->authentp != NULL) {
231 (*auth_context)->authentp->client = NULL;
232 (*auth_context)->authentp->checksum = NULL;
233 }
234 if (checksump && checksump->checksum_type != 0x8003)
235 free(checksump->contents);
236
237 cleanup:
238 if (desired_etypes &&
239 desired_etypes != (*auth_context)->permitted_etypes)
240 free(desired_etypes);
241 if (request.ticket)
242 krb5_free_ticket(context, request.ticket);
243 if (request.authenticator.ciphertext.data) {
244 (void) memset(request.authenticator.ciphertext.data, 0,
245 request.authenticator.ciphertext.length);
246 free(request.authenticator.ciphertext.data);
247 }
248 if (scratch) {
249 memset(scratch->data, 0, scratch->length);
250 free(scratch->data);
251 free(scratch);
252 }
253 return retval;
254 }
255
256 static krb5_error_code
generate_authenticator(krb5_context context,krb5_authenticator * authent,krb5_principal client,krb5_checksum * cksum,krb5_key key,krb5_ui_4 seq_number,krb5_authdata ** authorization,krb5_authdata_context ad_context,krb5_enctype * desired_etypes,krb5_boolean cbt_flag,krb5_enctype tkt_enctype)257 generate_authenticator(krb5_context context, krb5_authenticator *authent,
258 krb5_principal client, krb5_checksum *cksum,
259 krb5_key key, krb5_ui_4 seq_number,
260 krb5_authdata **authorization,
261 krb5_authdata_context ad_context,
262 krb5_enctype *desired_etypes, krb5_boolean cbt_flag,
263 krb5_enctype tkt_enctype)
264 {
265 krb5_error_code retval;
266 krb5_authdata **ext_authdata = NULL, **ap_authdata, **combined;
267 int client_aware_cb;
268
269 authent->client = client;
270 authent->checksum = cksum;
271 if (key) {
272 retval = krb5_k_key_keyblock(context, key, &authent->subkey);
273 if (retval)
274 return retval;
275 } else
276 authent->subkey = 0;
277 authent->seq_number = seq_number;
278 authent->authorization_data = NULL;
279
280 if (ad_context != NULL) {
281 retval = krb5_authdata_export_authdata(context,
282 ad_context,
283 AD_USAGE_AP_REQ,
284 &ext_authdata);
285 if (retval)
286 return retval;
287 }
288
289 if (authorization != NULL || ext_authdata != NULL) {
290 retval = krb5_merge_authdata(context,
291 authorization,
292 ext_authdata,
293 &authent->authorization_data);
294 if (retval) {
295 krb5_free_authdata(context, ext_authdata);
296 return retval;
297 }
298 krb5_free_authdata(context, ext_authdata);
299 }
300
301 if (cbt_flag) {
302 client_aware_cb = TRUE;
303 } else {
304 retval = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
305 KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS, NULL,
306 FALSE, &client_aware_cb);
307 if (retval)
308 return retval;
309 }
310
311 /* Add etype negotiation or channel-binding awareness authdata to the
312 * front, if appropriate. */
313 retval = make_ap_authdata(context, desired_etypes, tkt_enctype,
314 client_aware_cb, &ap_authdata);
315 if (retval)
316 return retval;
317 if (ap_authdata != NULL) {
318 retval = krb5_merge_authdata(context, ap_authdata,
319 authent->authorization_data, &combined);
320 krb5_free_authdata(context, ap_authdata);
321 if (retval)
322 return retval;
323 krb5_free_authdata(context, authent->authorization_data);
324 authent->authorization_data = combined;
325 }
326
327 return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
328 }
329
330 /* Set *out to a DER-encoded RFC 4537 etype list, or to NULL if no etype list
331 * should be sent. */
332 static krb5_error_code
make_etype_list(krb5_context context,krb5_enctype * desired_enctypes,krb5_enctype tkt_enctype,krb5_data ** out)333 make_etype_list(krb5_context context, krb5_enctype *desired_enctypes,
334 krb5_enctype tkt_enctype, krb5_data **out)
335 {
336 krb5_etype_list etlist;
337 int count;
338
339 *out = NULL;
340
341 /* Only send a list if we prefer another enctype to tkt_enctype. */
342 if (desired_enctypes == NULL || desired_enctypes[0] == tkt_enctype)
343 return 0;
344
345 /* Count elements of desired_etypes, stopping at tkt_enctypes if present.
346 * (Per RFC 4537, it must be the last option if it is included.) */
347 for (count = 0; desired_enctypes[count] != ENCTYPE_NULL; count++) {
348 if (count > 0 && desired_enctypes[count - 1] == tkt_enctype)
349 break;
350 }
351
352 etlist.etypes = desired_enctypes;
353 etlist.length = count;
354 return encode_krb5_etype_list(&etlist, out);
355 }
356
357 /* Set *authdata_out to appropriate authenticator authdata for the request,
358 * encoded in a single AD_IF_RELEVANT element. */
359 static krb5_error_code
make_ap_authdata(krb5_context context,krb5_enctype * desired_enctypes,krb5_enctype tkt_enctype,krb5_boolean client_aware_cb,krb5_authdata *** authdata_out)360 make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
361 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
362 krb5_authdata ***authdata_out)
363 {
364 krb5_error_code ret;
365 krb5_authdata etypes_ad, flags_ad, *list[3];
366 krb5_data *der_etypes = NULL;
367 size_t count = 0;
368 uint8_t flagbuf[4];
369 const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
370
371 *authdata_out = NULL;
372
373 /* Include an ETYPE_NEGOTIATION element if appropriate. */
374 ret = make_etype_list(context, desired_enctypes, tkt_enctype, &der_etypes);
375 if (ret)
376 goto cleanup;
377 if (der_etypes != NULL) {
378 etypes_ad.magic = KV5M_AUTHDATA;
379 etypes_ad.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
380 etypes_ad.length = der_etypes->length;
381 etypes_ad.contents = (uint8_t *)der_etypes->data;
382 list[count++] = &etypes_ad;
383 }
384
385 /* Include an AP_OPTIONS element if the CBT flag is configured. */
386 if (client_aware_cb != 0) {
387 store_32_le(KERB_AP_OPTIONS_CBT, flagbuf);
388 flags_ad.magic = KV5M_AUTHDATA;
389 flags_ad.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
390 flags_ad.length = 4;
391 flags_ad.contents = flagbuf;
392 list[count++] = &flags_ad;
393 }
394
395 if (count > 0) {
396 list[count] = NULL;
397 ret = krb5_encode_authdata_container(context,
398 KRB5_AUTHDATA_IF_RELEVANT,
399 list, authdata_out);
400 }
401
402 cleanup:
403 krb5_free_data(context, der_etypes);
404 return ret;
405 }
406