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