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