1 /*
2 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /*
6 * lib/krb5/krb/send_tgs.c
7 *
8 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
9 * All Rights Reserved.
10 *
11 * Export of this software from the United States of America may
12 * require a specific license from the United States Government.
13 * It is the responsibility of any person or organization contemplating
14 * export to obtain such a license before exporting.
15 *
16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17 * distribute this software and its documentation for any purpose and
18 * without fee is hereby granted, provided that the above copyright
19 * notice appear in all copies and that both that copyright notice and
20 * this permission notice appear in supporting documentation, and that
21 * the name of M.I.T. not be used in advertising or publicity pertaining
22 * to distribution of the software without specific, written prior
23 * permission. Furthermore if you modify this software you must label
24 * your software as modified software and not distribute it in such a
25 * fashion that it might be confused with the original M.I.T. software.
26 * M.I.T. makes no representations about the suitability of
27 * this software for any purpose. It is provided "as is" without express
28 * or implied warranty.
29 *
30 *
31 * krb5_send_tgs()
32 */
33
34 #include "k5-int.h"
35
36 /*
37 Sends a request to the TGS and waits for a response.
38 options is used for the options in the KRB_TGS_REQ.
39 timestruct values are used for from, till, rtime " " "
40 enctype is used for enctype " " ", and to encrypt the authorization data,
41 sname is used for sname " " "
42 addrs, if non-NULL, is used for addresses " " "
43 authorization_dat, if non-NULL, is used for authorization_dat " " "
44 second_ticket, if required by options, is used for the 2nd ticket in the req.
45 in_cred is used for the ticket & session key in the KRB_AP_REQ header " " "
46 (the KDC realm is extracted from in_cred->server's realm)
47
48 The response is placed into *rep.
49 rep->response.data is set to point at allocated storage which should be
50 freed by the caller when finished.
51
52 returns system errors
53 */
54 static krb5_error_code
krb5_send_tgs_basic(krb5_context context,krb5_data * in_data,krb5_creds * in_cred,krb5_data * outbuf)55 krb5_send_tgs_basic(krb5_context context, krb5_data *in_data, krb5_creds *in_cred, krb5_data *outbuf)
56 {
57 krb5_error_code retval;
58 krb5_checksum checksum;
59 krb5_authenticator authent;
60 krb5_ap_req request;
61 krb5_data * scratch;
62 krb5_data * toutbuf;
63
64 /* Generate checksum */
65 if ((retval = krb5_c_make_checksum(context, context->kdc_req_sumtype,
66 &in_cred->keyblock,
67 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
68 in_data, &checksum))) {
69 free(checksum.contents);
70 return(retval);
71 }
72
73 /* gen authenticator */
74 authent.subkey = 0;
75 authent.seq_number = 0;
76 authent.checksum = &checksum;
77 authent.client = in_cred->client;
78 authent.authorization_data = in_cred->authdata;
79 if ((retval = krb5_us_timeofday(context, &authent.ctime,
80 &authent.cusec))) {
81 free(checksum.contents);
82 return(retval);
83 }
84
85 /* encode the authenticator */
86 if ((retval = encode_krb5_authenticator(&authent, &scratch))) {
87 free(checksum.contents);
88 return(retval);
89 }
90
91 free(checksum.contents);
92
93 request.authenticator.ciphertext.data = 0;
94 request.authenticator.kvno = 0;
95 request.ap_options = 0;
96 request.ticket = 0;
97
98 if ((retval = decode_krb5_ticket(&(in_cred)->ticket, &request.ticket)))
99 /* Cleanup scratch and scratch data */
100 goto cleanup_data;
101
102 /* call the encryption routine */
103 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock,
104 KRB5_KEYUSAGE_TGS_REQ_AUTH,
105 scratch, &request.authenticator)))
106 goto cleanup_ticket;
107
108 retval = encode_krb5_ap_req(&request, &toutbuf);
109 /* Solaris Kerberos */
110 if (retval == 0) {
111 *outbuf = *toutbuf;
112 krb5_xfree(toutbuf);
113 }
114
115
116 memset(request.authenticator.ciphertext.data, 0,
117 request.authenticator.ciphertext.length);
118 free(request.authenticator.ciphertext.data);
119
120 cleanup_ticket:
121 krb5_free_ticket(context, request.ticket);
122
123 cleanup_data:
124 memset(scratch->data, 0, scratch->length);
125 free(scratch->data);
126
127 free(scratch);
128
129 return retval;
130 }
131
132 krb5_error_code
krb5_send_tgs(krb5_context context,krb5_flags kdcoptions,const krb5_ticket_times * timestruct,const krb5_enctype * ktypes,krb5_const_principal sname,krb5_address * const * addrs,krb5_authdata * const * authorization_data,krb5_pa_data * const * padata,const krb5_data * second_ticket,krb5_creds * in_cred,krb5_response * rep)133 krb5_send_tgs(krb5_context context, krb5_flags kdcoptions,
134 const krb5_ticket_times *timestruct, const krb5_enctype *ktypes,
135 krb5_const_principal sname, krb5_address *const *addrs,
136 krb5_authdata *const *authorization_data,
137 krb5_pa_data *const *padata, const krb5_data *second_ticket,
138 krb5_creds *in_cred, krb5_response *rep)
139 {
140 return (krb5_send_tgs2(context, kdcoptions,
141 timestruct, ktypes,
142 sname, addrs,
143 authorization_data,
144 padata, second_ticket,
145 in_cred, rep,
146 NULL));
147 }
148
149 /*
150 * Solaris Kerberos
151 * Same as krb5_send_tgs plus an extra arg to return the FQDN
152 * of the KDC sent the request.
153 */
154 krb5_error_code
krb5_send_tgs2(krb5_context context,krb5_flags kdcoptions,const krb5_ticket_times * timestruct,const krb5_enctype * ktypes,krb5_const_principal sname,krb5_address * const * addrs,krb5_authdata * const * authorization_data,krb5_pa_data * const * padata,const krb5_data * second_ticket,krb5_creds * in_cred,krb5_response * rep,char ** hostname_used)155 krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions,
156 const krb5_ticket_times *timestruct, const krb5_enctype *ktypes,
157 krb5_const_principal sname, krb5_address *const *addrs,
158 krb5_authdata *const *authorization_data,
159 krb5_pa_data *const *padata, const krb5_data *second_ticket,
160 krb5_creds *in_cred, krb5_response *rep, char **hostname_used)
161 {
162 krb5_error_code retval;
163 krb5_kdc_req tgsreq;
164 krb5_data *scratch, scratch2;
165 krb5_ticket *sec_ticket = 0;
166 krb5_ticket *sec_ticket_arr[2];
167 krb5_timestamp time_now;
168 krb5_pa_data **combined_padata;
169 krb5_pa_data ap_req_padata;
170 int tcp_only = 0, use_master;
171
172 /*
173 * in_creds MUST be a valid credential NOT just a partially filled in
174 * place holder for us to get credentials for the caller.
175 */
176 if (!in_cred->ticket.length)
177 return(KRB5_NO_TKT_SUPPLIED);
178
179 memset((char *)&tgsreq, 0, sizeof(tgsreq));
180
181 tgsreq.kdc_options = kdcoptions;
182 tgsreq.server = (krb5_principal) sname;
183
184 tgsreq.from = timestruct->starttime;
185 tgsreq.till = timestruct->endtime ? timestruct->endtime :
186 in_cred->times.endtime;
187 tgsreq.rtime = timestruct->renew_till;
188 if ((retval = krb5_timeofday(context, &time_now)))
189 return(retval);
190 /* XXX we know they are the same size... */
191 rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now;
192 rep->request_time = time_now;
193
194 tgsreq.addresses = (krb5_address **) addrs;
195
196 if (authorization_data) {
197 /* need to encrypt it in the request */
198
199 if ((retval = encode_krb5_authdata(authorization_data,
200 &scratch)))
201 return(retval);
202
203 if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock,
204 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
205 scratch,
206 &tgsreq.authorization_data))) {
207 krb5_xfree(tgsreq.authorization_data.ciphertext.data);
208 krb5_free_data(context, scratch);
209 return retval;
210 }
211
212 krb5_free_data(context, scratch);
213 }
214
215 /* Get the encryption types list */
216 if (ktypes) {
217 /* Check passed ktypes and make sure they're valid. */
218 for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) {
219 if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes]))
220 return KRB5_PROG_ETYPE_NOSUPP;
221 }
222 tgsreq.ktype = (krb5_enctype *)ktypes;
223 } else {
224 /* Get the default ktypes */
225 /* Solaris Kerberos */
226 if ((retval = krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype))))
227 goto send_tgs_error_2;
228 for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++);
229 }
230
231 if (second_ticket) {
232 if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket)))
233 goto send_tgs_error_1;
234 sec_ticket_arr[0] = sec_ticket;
235 sec_ticket_arr[1] = 0;
236 tgsreq.second_ticket = sec_ticket_arr;
237 } else
238 tgsreq.second_ticket = 0;
239
240 /* encode the body; then checksum it */
241 if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch)))
242 goto send_tgs_error_2;
243
244 /*
245 * Get an ap_req.
246 */
247 if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) {
248 krb5_free_data(context, scratch);
249 goto send_tgs_error_2;
250 }
251 krb5_free_data(context, scratch);
252
253 ap_req_padata.pa_type = KRB5_PADATA_AP_REQ;
254 ap_req_padata.length = scratch2.length;
255 ap_req_padata.contents = (krb5_octet *)scratch2.data;
256
257 /* combine in any other supplied padata */
258 if (padata) {
259 krb5_pa_data * const * counter;
260 register unsigned int i = 0;
261 for (counter = padata; *counter; counter++, i++);
262 combined_padata = malloc((i+2) * sizeof(*combined_padata));
263 if (!combined_padata) {
264 krb5_xfree(ap_req_padata.contents);
265 retval = ENOMEM;
266 goto send_tgs_error_2;
267 }
268 combined_padata[0] = &ap_req_padata;
269 for (i = 1, counter = padata; *counter; counter++, i++)
270 combined_padata[i] = (krb5_pa_data *) *counter;
271 combined_padata[i] = 0;
272 } else {
273 combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata));
274 if (!combined_padata) {
275 krb5_xfree(ap_req_padata.contents);
276 retval = ENOMEM;
277 goto send_tgs_error_2;
278 }
279 combined_padata[0] = &ap_req_padata;
280 combined_padata[1] = 0;
281 }
282 tgsreq.padata = combined_padata;
283
284 /* the TGS_REQ is assembled in tgsreq, so encode it */
285 if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) {
286 krb5_xfree(ap_req_padata.contents);
287 krb5_xfree(combined_padata);
288 goto send_tgs_error_2;
289 }
290 krb5_xfree(ap_req_padata.contents);
291 krb5_xfree(combined_padata);
292
293 /* now send request & get response from KDC */
294 send_again:
295 use_master = 0;
296 retval = krb5_sendto_kdc2(context, scratch,
297 krb5_princ_realm(context, sname),
298 &rep->response, &use_master, tcp_only,
299 hostname_used);
300 if (retval == 0) {
301 if (krb5_is_krb_error(&rep->response)) {
302 if (!tcp_only) {
303 krb5_error *err_reply;
304 retval = decode_krb5_error(&rep->response, &err_reply);
305 /* Solaris Kerberos */
306 if (retval == 0) {
307 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) {
308 tcp_only = 1;
309 krb5_free_error(context, err_reply);
310 free(rep->response.data);
311 rep->response.data = 0;
312 goto send_again;
313 }
314 krb5_free_error(context, err_reply);
315 }
316 }
317 } else if (krb5_is_tgs_rep(&rep->response))
318 rep->message_type = KRB5_TGS_REP;
319 else /* XXX: assume it's an error */
320 rep->message_type = KRB5_ERROR;
321 }
322
323 krb5_free_data(context, scratch);
324
325 send_tgs_error_2:;
326 if (sec_ticket)
327 krb5_free_ticket(context, sec_ticket);
328
329 send_tgs_error_1:;
330 if (ktypes == NULL)
331 krb5_xfree(tgsreq.ktype);
332 if (tgsreq.authorization_data.ciphertext.data) {
333 memset(tgsreq.authorization_data.ciphertext.data, 0,
334 tgsreq.authorization_data.ciphertext.length);
335 krb5_xfree(tgsreq.authorization_data.ciphertext.data);
336 }
337
338 return retval;
339 }
340