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,
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
99 request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
100 request.authenticator.ciphertext.data = NULL;
101 request.ticket = 0;
102
103 if (!in_creds->ticket.length)
104 return(KRB5_NO_TKT_SUPPLIED);
105
106 if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) &&
107 !(ap_req_options & AP_OPTS_MUTUAL_REQUIRED))
108 return(EINVAL);
109
110 /* we need a native ticket */
111 if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
112 return(retval);
113
114 /* verify that the ticket is not expired */
115 if ((retval = krb5int_validate_times(context, &in_creds->times)) != 0)
116 goto cleanup;
117
118 /* generate auth_context if needed */
119 if (*auth_context == NULL) {
120 if ((retval = krb5_auth_con_init(context, &new_auth_context)))
121 goto cleanup;
122 *auth_context = new_auth_context;
123 }
124
125 if ((*auth_context)->key != NULL) {
126 krb5_k_free_key(context, (*auth_context)->key);
127 (*auth_context)->key = NULL;
128 }
129
130 /* set auth context keyblock */
131 if ((retval = krb5_k_create_key(context, &in_creds->keyblock,
132 &((*auth_context)->key))))
133 goto cleanup;
134
135 /* generate seq number if needed */
136 if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
137 || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
138 && ((*auth_context)->local_seq_number == 0)) {
139 if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock,
140 &(*auth_context)->local_seq_number)))
141 goto cleanup;
142 }
143
144 /* generate subkey if needed */
145 if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
146 retval = k5_generate_and_save_subkey(context, *auth_context,
147 &in_creds->keyblock,
148 in_creds->keyblock.enctype);
149 if (retval)
150 goto cleanup;
151 }
152
153
154 if (!in_data && (*auth_context)->checksum_func) {
155 retval = (*auth_context)->checksum_func( context,
156 *auth_context,
157 (*auth_context)->checksum_func_data,
158 &in_data);
159 if (retval)
160 goto cleanup;
161 }
162
163 if (in_data) {
164 if ((*auth_context)->req_cksumtype == 0x8003) {
165 /* XXX Special hack for GSSAPI */
166 checksum.checksum_type = 0x8003;
167 checksum.length = in_data->length;
168 checksum.contents = (krb5_octet *) in_data->data;
169 } else {
170 retval = krb5_k_make_checksum(context, 0, (*auth_context)->key,
171 KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
172 in_data, &checksum);
173 if (retval)
174 goto cleanup_cksum;
175 }
176 checksump = &checksum;
177 }
178
179 /* Generate authenticator */
180 if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
181 krb5_authenticator))) == NULL) {
182 retval = ENOMEM;
183 goto cleanup_cksum;
184 }
185
186 if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) {
187 if ((*auth_context)->permitted_etypes == NULL) {
188 retval = krb5_get_tgs_ktypes(context, in_creds->server, &desired_etypes);
189 if (retval)
190 goto cleanup_cksum;
191 } else
192 desired_etypes = (*auth_context)->permitted_etypes;
193 }
194
195 TRACE_MK_REQ(context, in_creds, (*auth_context)->local_seq_number,
196 (*auth_context)->send_subkey, &in_creds->keyblock);
197 if ((retval = generate_authenticator(context,
198 (*auth_context)->authentp,
199 in_creds->client, checksump,
200 (*auth_context)->send_subkey,
201 (*auth_context)->local_seq_number,
202 in_creds->authdata,
203 (*auth_context)->ad_context,
204 desired_etypes,
205 in_creds->keyblock.enctype)))
206 goto cleanup_cksum;
207
208 /* encode the authenticator */
209 if ((retval = encode_krb5_authenticator((*auth_context)->authentp,
210 &scratch)))
211 goto cleanup_cksum;
212
213 /* call the encryption routine */
214 if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
215 KRB5_KEYUSAGE_AP_REQ_AUTH,
216 scratch, &request.authenticator)))
217 goto cleanup_cksum;
218
219 if ((retval = encode_krb5_ap_req(&request, &toutbuf)))
220 goto cleanup_cksum;
221 *outbuf = *toutbuf;
222
223 free(toutbuf);
224
225 cleanup_cksum:
226 /* Null out these fields, to prevent pointer sharing problems;
227 * they were supplied by the caller
228 */
229 if ((*auth_context)->authentp != NULL) {
230 (*auth_context)->authentp->client = NULL;
231 (*auth_context)->authentp->checksum = NULL;
232 }
233 if (checksump && checksump->checksum_type != 0x8003)
234 free(checksump->contents);
235
236 cleanup:
237 if (desired_etypes &&
238 desired_etypes != (*auth_context)->permitted_etypes)
239 free(desired_etypes);
240 if (request.ticket)
241 krb5_free_ticket(context, request.ticket);
242 if (request.authenticator.ciphertext.data) {
243 (void) memset(request.authenticator.ciphertext.data, 0,
244 request.authenticator.ciphertext.length);
245 free(request.authenticator.ciphertext.data);
246 }
247 if (scratch) {
248 memset(scratch->data, 0, scratch->length);
249 free(scratch->data);
250 free(scratch);
251 }
252 return retval;
253 }
254
255 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_enctype tkt_enctype)256 generate_authenticator(krb5_context context, krb5_authenticator *authent,
257 krb5_principal client, krb5_checksum *cksum,
258 krb5_key key, krb5_ui_4 seq_number,
259 krb5_authdata **authorization,
260 krb5_authdata_context ad_context,
261 krb5_enctype *desired_etypes,
262 krb5_enctype tkt_enctype)
263 {
264 krb5_error_code retval;
265 krb5_authdata **ext_authdata = NULL, **ap_authdata, **combined;
266 int client_aware_cb;
267
268 authent->client = client;
269 authent->checksum = cksum;
270 if (key) {
271 retval = krb5_k_key_keyblock(context, key, &authent->subkey);
272 if (retval)
273 return retval;
274 } else
275 authent->subkey = 0;
276 authent->seq_number = seq_number;
277 authent->authorization_data = NULL;
278
279 if (ad_context != NULL) {
280 retval = krb5_authdata_export_authdata(context,
281 ad_context,
282 AD_USAGE_AP_REQ,
283 &ext_authdata);
284 if (retval)
285 return retval;
286 }
287
288 if (authorization != NULL || ext_authdata != NULL) {
289 retval = krb5_merge_authdata(context,
290 authorization,
291 ext_authdata,
292 &authent->authorization_data);
293 if (retval) {
294 krb5_free_authdata(context, ext_authdata);
295 return retval;
296 }
297 krb5_free_authdata(context, ext_authdata);
298 }
299
300 retval = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
301 KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS, NULL,
302 FALSE, &client_aware_cb);
303 if (retval)
304 return retval;
305
306 /* Add etype negotiation or channel-binding awareness authdata to the
307 * front, if appropriate. */
308 retval = make_ap_authdata(context, desired_etypes, tkt_enctype,
309 client_aware_cb, &ap_authdata);
310 if (retval)
311 return retval;
312 if (ap_authdata != NULL) {
313 retval = krb5_merge_authdata(context, ap_authdata,
314 authent->authorization_data, &combined);
315 krb5_free_authdata(context, ap_authdata);
316 if (retval)
317 return retval;
318 krb5_free_authdata(context, authent->authorization_data);
319 authent->authorization_data = combined;
320 }
321
322 return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
323 }
324
325 /* Set *out to a DER-encoded RFC 4537 etype list, or to NULL if no etype list
326 * should be sent. */
327 static krb5_error_code
make_etype_list(krb5_context context,krb5_enctype * desired_enctypes,krb5_enctype tkt_enctype,krb5_data ** out)328 make_etype_list(krb5_context context, krb5_enctype *desired_enctypes,
329 krb5_enctype tkt_enctype, krb5_data **out)
330 {
331 krb5_etype_list etlist;
332 int count;
333
334 *out = NULL;
335
336 /* Only send a list if we prefer another enctype to tkt_enctype. */
337 if (desired_enctypes == NULL || desired_enctypes[0] == tkt_enctype)
338 return 0;
339
340 /* Count elements of desired_etypes, stopping at tkt_enctypes if present.
341 * (Per RFC 4537, it must be the last option if it is included.) */
342 for (count = 0; desired_enctypes[count] != ENCTYPE_NULL; count++) {
343 if (count > 0 && desired_enctypes[count - 1] == tkt_enctype)
344 break;
345 }
346
347 etlist.etypes = desired_enctypes;
348 etlist.length = count;
349 return encode_krb5_etype_list(&etlist, out);
350 }
351
352 /* Set *authdata_out to appropriate authenticator authdata for the request,
353 * encoded in a single AD_IF_RELEVANT element. */
354 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)355 make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
356 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
357 krb5_authdata ***authdata_out)
358 {
359 krb5_error_code ret;
360 krb5_authdata etypes_ad, flags_ad, *list[3];
361 krb5_data *der_etypes = NULL;
362 size_t count = 0;
363 uint8_t flagbuf[4];
364 const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
365
366 *authdata_out = NULL;
367
368 /* Include an ETYPE_NEGOTIATION element if appropriate. */
369 ret = make_etype_list(context, desired_enctypes, tkt_enctype, &der_etypes);
370 if (ret)
371 goto cleanup;
372 if (der_etypes != NULL) {
373 etypes_ad.magic = KV5M_AUTHDATA;
374 etypes_ad.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
375 etypes_ad.length = der_etypes->length;
376 etypes_ad.contents = (uint8_t *)der_etypes->data;
377 list[count++] = &etypes_ad;
378 }
379
380 /* Include an AP_OPTIONS element if the CBT flag is configured. */
381 if (client_aware_cb != 0) {
382 store_32_le(KERB_AP_OPTIONS_CBT, flagbuf);
383 flags_ad.magic = KV5M_AUTHDATA;
384 flags_ad.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
385 flags_ad.length = 4;
386 flags_ad.contents = flagbuf;
387 list[count++] = &flags_ad;
388 }
389
390 if (count > 0) {
391 list[count] = NULL;
392 ret = krb5_encode_authdata_container(context,
393 KRB5_AUTHDATA_IF_RELEVANT,
394 list, authdata_out);
395 }
396
397 cleanup:
398 krb5_free_data(context, der_etypes);
399 return ret;
400 }
401