1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/send_tgs.c - Construct a TGS request */
3 /*
4  * Copyright 1990,1991,2009,2013 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 #include "k5-int.h"
28 #include "int-proto.h"
29 #include "fast.h"
30 
31 /* Choose a random nonce for an AS or TGS request. */
32 krb5_error_code
k5_generate_nonce(krb5_context context,int32_t * out)33 k5_generate_nonce(krb5_context context, int32_t *out)
34 {
35     krb5_error_code ret;
36     unsigned char random_buf[4];
37     krb5_data random_data = make_data(random_buf, 4);
38 
39     *out = 0;
40 
41     /* We and Heimdal incorrectly encode nonces as signed, so make sure we use
42      * a non-negative value to avoid interoperability issues. */
43     ret = krb5_c_random_make_octets(context, &random_data);
44     if (ret)
45         return ret;
46     *out = 0x7FFFFFFF & load_32_n(random_buf);
47     return 0;
48 }
49 
50 /* Construct an AP-REQ message for a TGS request. */
51 static krb5_error_code
tgs_construct_ap_req(krb5_context context,krb5_data * checksum_data,krb5_creds * tgt,krb5_keyblock * subkey,krb5_data ** ap_req_asn1_out)52 tgs_construct_ap_req(krb5_context context, krb5_data *checksum_data,
53                      krb5_creds *tgt, krb5_keyblock *subkey,
54                      krb5_data **ap_req_asn1_out)
55 {
56     krb5_error_code ret;
57     krb5_checksum checksum;
58     krb5_authenticator authent;
59     krb5_ap_req ap_req;
60     krb5_data *authent_asn1 = NULL;
61     krb5_ticket *ticket = NULL;
62     krb5_enc_data authent_enc;
63 
64     *ap_req_asn1_out = NULL;
65     memset(&checksum, 0, sizeof(checksum));
66     memset(&ap_req, 0, sizeof(ap_req));
67     memset(&authent_enc, 0, sizeof(authent_enc));
68 
69     /* Generate checksum. */
70     ret = krb5_c_make_checksum(context, 0, &tgt->keyblock,
71                                KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, checksum_data,
72                                &checksum);
73     if (ret)
74         goto cleanup;
75 
76     /* Construct, encode, and encrypt an authenticator. */
77     authent.subkey = subkey;
78     authent.seq_number = 0;
79     authent.checksum = &checksum;
80     authent.client = tgt->client;
81     authent.authorization_data = tgt->authdata;
82     ret = krb5_us_timeofday(context, &authent.ctime, &authent.cusec);
83     if (ret)
84         goto cleanup;
85     ret = encode_krb5_authenticator(&authent, &authent_asn1);
86     if (ret)
87         goto cleanup;
88     ret = krb5_encrypt_helper(context, &tgt->keyblock,
89                               KRB5_KEYUSAGE_TGS_REQ_AUTH, authent_asn1,
90                               &authent_enc);
91     if (ret)
92         goto cleanup;
93 
94     ret = decode_krb5_ticket(&tgt->ticket, &ticket);
95     if (ret)
96         goto cleanup;
97 
98     /* Encode the AP-REQ. */
99     ap_req.authenticator = authent_enc;
100     ap_req.ticket = ticket;
101     ret = encode_krb5_ap_req(&ap_req, ap_req_asn1_out);
102 
103 cleanup:
104     free(checksum.contents);
105     krb5_free_ticket(context, ticket);
106     krb5_free_data_contents(context, &authent_enc.ciphertext);
107     if (authent_asn1 != NULL)
108         zapfree(authent_asn1->data, authent_asn1->length);
109     free(authent_asn1);
110     return ret;
111 }
112 
113 /*
114  * Construct a TGS request and return its ASN.1 encoding as well as the
115  * timestamp, nonce, and subkey used.  The pacb_fn callback allows the caller
116  * to amend the request padata after the nonce and subkey are determined.
117  */
118 krb5_error_code
k5_make_tgs_req(krb5_context context,struct krb5int_fast_request_state * fast_state,krb5_creds * tgt,krb5_flags kdcoptions,krb5_address * const * addrs,krb5_pa_data ** in_padata,krb5_creds * desired,k5_pacb_fn pacb_fn,void * pacb_data,krb5_data * req_asn1_out,krb5_timestamp * timestamp_out,krb5_int32 * nonce_out,krb5_keyblock ** subkey_out)119 k5_make_tgs_req(krb5_context context,
120                 struct krb5int_fast_request_state *fast_state,
121                 krb5_creds *tgt, krb5_flags kdcoptions,
122                 krb5_address *const *addrs, krb5_pa_data **in_padata,
123                 krb5_creds *desired, k5_pacb_fn pacb_fn, void *pacb_data,
124                 krb5_data *req_asn1_out, krb5_timestamp *timestamp_out,
125                 krb5_int32 *nonce_out, krb5_keyblock **subkey_out)
126 {
127     krb5_error_code ret;
128     krb5_kdc_req req;
129     krb5_data *authdata_asn1 = NULL, *req_body_asn1 = NULL;
130     krb5_data *ap_req_asn1 = NULL, *tgs_req_asn1 = NULL;
131     krb5_ticket *sec_ticket = NULL;
132     krb5_ticket *sec_ticket_arr[2];
133     krb5_timestamp time_now;
134     krb5_pa_data **padata = NULL, *pa;
135     krb5_keyblock *subkey = NULL;
136     krb5_enc_data authdata_enc;
137     krb5_enctype enctypes[2], *defenctypes = NULL;
138     size_t count, i;
139 
140     *req_asn1_out = empty_data();
141     *timestamp_out = 0;
142     *nonce_out = 0;
143     *subkey_out = NULL;
144     memset(&req, 0, sizeof(req));
145     memset(&authdata_enc, 0, sizeof(authdata_enc));
146 
147     /* tgt's client principal must match the desired client principal. */
148     if (!krb5_principal_compare(context, tgt->client, desired->client))
149         return KRB5_PRINC_NOMATCH;
150 
151     /* tgt must be an actual credential, not a template. */
152     if (!tgt->ticket.length)
153         return KRB5_NO_TKT_SUPPLIED;
154 
155     req.kdc_options = kdcoptions;
156     req.server = desired->server;
157     req.from = desired->times.starttime;
158     req.till = desired->times.endtime ? desired->times.endtime :
159         tgt->times.endtime;
160     req.rtime = desired->times.renew_till;
161     ret = k5_generate_nonce(context, &req.nonce);
162     if (ret)
163         return ret;
164     *nonce_out = req.nonce;
165     ret = krb5_timeofday(context, &time_now);
166     if (ret)
167         return ret;
168     *timestamp_out = time_now;
169 
170     req.addresses = (krb5_address **)addrs;
171 
172     /* Generate subkey. */
173     ret = krb5_generate_subkey(context, &tgt->keyblock, &subkey);
174     if (ret)
175         return ret;
176     TRACE_SEND_TGS_SUBKEY(context, subkey);
177 
178     ret = krb5int_fast_tgs_armor(context, fast_state, subkey, &tgt->keyblock,
179                                  NULL, NULL);
180     if (ret)
181         goto cleanup;
182 
183     if (desired->authdata != NULL) {
184         ret = encode_krb5_authdata(desired->authdata, &authdata_asn1);
185         if (ret)
186             goto cleanup;
187         ret = krb5_encrypt_helper(context, subkey,
188                                   KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
189                                   authdata_asn1, &authdata_enc);
190         if (ret)
191             goto cleanup;
192         req.authorization_data = authdata_enc;
193     }
194 
195     if (desired->keyblock.enctype != ENCTYPE_NULL) {
196         if (!krb5_c_valid_enctype(desired->keyblock.enctype)) {
197             ret = KRB5_PROG_ETYPE_NOSUPP;
198             goto cleanup;
199         }
200         enctypes[0] = desired->keyblock.enctype;
201         enctypes[1] = ENCTYPE_NULL;
202         req.ktype = enctypes;
203         req.nktypes = 1;
204     } else {
205         /* Get the default TGS enctypes. */
206         ret = krb5_get_tgs_ktypes(context, desired->server, &defenctypes);
207         if (ret)
208             goto cleanup;
209         for (count = 0; defenctypes[count]; count++);
210         req.ktype = defenctypes;
211         req.nktypes = count;
212     }
213     TRACE_SEND_TGS_ETYPES(context, req.ktype);
214 
215     if (kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
216         if (desired->second_ticket.length == 0) {
217             ret = KRB5_NO_2ND_TKT;
218             goto cleanup;
219         }
220         ret = decode_krb5_ticket(&desired->second_ticket, &sec_ticket);
221         if (ret)
222             goto cleanup;
223         sec_ticket_arr[0] = sec_ticket;
224         sec_ticket_arr[1] = NULL;
225         req.second_ticket = sec_ticket_arr;
226     }
227 
228     /* Encode the request body. */
229     ret = krb5int_fast_prep_req_body(context, fast_state, &req,
230                                      &req_body_asn1);
231     if (ret)
232         goto cleanup;
233 
234     ret = tgs_construct_ap_req(context, req_body_asn1, tgt, subkey,
235                                &ap_req_asn1);
236     if (ret)
237         goto cleanup;
238 
239     for (count = 0; in_padata != NULL && in_padata[count] != NULL; count++);
240 
241     /* Construct a padata array for the request, beginning with the ap-req. */
242     padata = k5calloc(count + 2, sizeof(krb5_pa_data *), &ret);
243     if (padata == NULL)
244         goto cleanup;
245     padata[0] = k5alloc(sizeof(krb5_pa_data), &ret);
246     if (padata[0] == NULL)
247         goto cleanup;
248     padata[0]->pa_type = KRB5_PADATA_AP_REQ;
249     padata[0]->contents = k5memdup(ap_req_asn1->data, ap_req_asn1->length,
250                                    &ret);
251     if (padata[0] == NULL)
252         goto cleanup;
253     padata[0]->length = ap_req_asn1->length;
254 
255     /* Append copies of any other supplied padata. */
256     for (i = 0; in_padata != NULL && in_padata[i] != NULL; i++) {
257         pa = k5alloc(sizeof(krb5_pa_data), &ret);
258         if (pa == NULL)
259             goto cleanup;
260         pa->pa_type = in_padata[i]->pa_type;
261         pa->length = in_padata[i]->length;
262         pa->contents = k5memdup(in_padata[i]->contents, in_padata[i]->length,
263                                 &ret);
264         if (pa->contents == NULL)
265             goto cleanup;
266         padata[i + 1] = pa;
267     }
268     req.padata = padata;
269 
270     if (pacb_fn != NULL) {
271         ret = (*pacb_fn)(context, subkey, &req, pacb_data);
272         if (ret)
273             goto cleanup;
274     }
275 
276     /* Encode the TGS-REQ.  Discard the krb5_data container. */
277     ret = krb5int_fast_prep_req(context, fast_state, &req, ap_req_asn1,
278                                 encode_krb5_tgs_req, &tgs_req_asn1);
279     if (ret)
280         goto cleanup;
281     *req_asn1_out = *tgs_req_asn1;
282     free(tgs_req_asn1);
283     tgs_req_asn1 = NULL;
284 
285     *subkey_out = subkey;
286     subkey = NULL;
287 
288 cleanup:
289     krb5_free_data(context, authdata_asn1);
290     krb5_free_data(context, req_body_asn1);
291     krb5_free_data(context, ap_req_asn1);
292     krb5_free_pa_data(context, req.padata);
293     krb5_free_ticket(context, sec_ticket);
294     krb5_free_data_contents(context, &authdata_enc.ciphertext);
295     krb5_free_keyblock(context, subkey);
296     free(defenctypes);
297     return ret;
298 }
299