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