xref: /freebsd/crypto/krb5/src/lib/krb5/krb/mk_req_ext.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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