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