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  */
55 static krb5_error_code
krb5_send_tgs_basic(krb5_context context,krb5_data * in_data,krb5_creds * in_cred,krb5_data * outbuf)56 krb5_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 
121 cleanup_ticket:
122     krb5_free_ticket(context, request.ticket);
123 
124 cleanup_data:
125     memset(scratch->data, 0, scratch->length);
126     free(scratch->data);
127 
128     free(scratch);
129 
130     return retval;
131 }
132 
133 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)134 krb5_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  */
155 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)156 krb5_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 */
301 send_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 
332 send_tgs_error_2:;
333     if (sec_ticket)
334 	krb5_free_ticket(context, sec_ticket);
335 
336 send_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