xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c (revision 299625c6492013aa7bd163862f0d181854f69b3c)
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
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
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
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