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