Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * 3. Neither the name of the Institute nor the names of its contributors
18 : * may be used to endorse or promote products derived from this software
19 : * without specific prior written permission.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 : * SUCH DAMAGE.
32 : */
33 :
34 : #include "krb5_locl.h"
35 :
36 : static krb5_error_code
37 0 : add_addrs(krb5_context context,
38 : krb5_addresses *addr,
39 : struct addrinfo *ai)
40 : {
41 : krb5_error_code ret;
42 : unsigned n, i;
43 : void *tmp;
44 : struct addrinfo *a;
45 :
46 0 : n = 0;
47 0 : for (a = ai; a != NULL; a = a->ai_next)
48 0 : ++n;
49 :
50 0 : tmp = realloc(addr->val, (addr->len + n) * sizeof(*addr->val));
51 0 : if (tmp == NULL && (addr->len + n) != 0) {
52 0 : ret = ENOMEM;
53 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
54 0 : goto fail;
55 : }
56 0 : addr->val = tmp;
57 0 : for (i = addr->len; i < (addr->len + n); ++i) {
58 0 : addr->val[i].addr_type = 0;
59 0 : krb5_data_zero(&addr->val[i].address);
60 : }
61 0 : i = addr->len;
62 0 : for (a = ai; a != NULL; a = a->ai_next) {
63 : krb5_address ad;
64 :
65 0 : ret = krb5_sockaddr2address (context, a->ai_addr, &ad);
66 0 : if (ret == 0) {
67 0 : if (krb5_address_search(context, &ad, addr))
68 0 : krb5_free_address(context, &ad);
69 : else
70 0 : addr->val[i++] = ad;
71 : }
72 0 : else if (ret == KRB5_PROG_ATYPE_NOSUPP)
73 0 : krb5_clear_error_message (context);
74 : else
75 0 : goto fail;
76 0 : addr->len = i;
77 : }
78 0 : return 0;
79 0 : fail:
80 0 : krb5_free_addresses (context, addr);
81 0 : return ret;
82 : }
83 :
84 : /**
85 : * Forward credentials for client to host hostname , making them
86 : * forwardable if forwardable, and returning the blob of data to sent
87 : * in out_data. If hostname == NULL, pick it from server.
88 : *
89 : * @param context A kerberos 5 context.
90 : * @param auth_context the auth context with the key to encrypt the out_data.
91 : * @param hostname the host to forward the tickets too.
92 : * @param client the client to delegate from.
93 : * @param server the server to delegate the credential too.
94 : * @param ccache credential cache to use.
95 : * @param forwardable make the forwarded ticket forwabledable.
96 : * @param out_data the resulting credential.
97 : *
98 : * @return Return an error code or 0.
99 : *
100 : * @ingroup krb5_credential
101 : */
102 :
103 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
104 0 : krb5_fwd_tgt_creds (krb5_context context,
105 : krb5_auth_context auth_context,
106 : const char *hostname,
107 : krb5_principal client,
108 : krb5_principal server,
109 : krb5_ccache ccache,
110 : int forwardable,
111 : krb5_data *out_data)
112 : {
113 0 : krb5_flags flags = 0;
114 : krb5_creds creds;
115 : krb5_error_code ret;
116 : krb5_const_realm client_realm;
117 :
118 0 : flags |= KDC_OPT_FORWARDED;
119 :
120 0 : if (forwardable)
121 0 : flags |= KDC_OPT_FORWARDABLE;
122 :
123 0 : if (hostname == NULL &&
124 0 : krb5_principal_get_type(context, server) == KRB5_NT_SRV_HST) {
125 0 : const char *inst = krb5_principal_get_comp_string(context, server, 0);
126 0 : const char *host = krb5_principal_get_comp_string(context, server, 1);
127 :
128 0 : if (inst != NULL &&
129 0 : strcmp(inst, "host") == 0 &&
130 0 : host != NULL &&
131 0 : krb5_principal_get_comp_string(context, server, 2) == NULL)
132 0 : hostname = host;
133 : }
134 :
135 0 : client_realm = krb5_principal_get_realm(context, client);
136 :
137 0 : memset (&creds, 0, sizeof(creds));
138 0 : creds.client = client;
139 :
140 0 : ret = krb5_make_principal(context,
141 : &creds.server,
142 : client_realm,
143 : KRB5_TGS_NAME,
144 : client_realm,
145 : NULL);
146 0 : if (ret)
147 0 : return ret;
148 :
149 0 : ret = krb5_get_forwarded_creds (context,
150 : auth_context,
151 : ccache,
152 : flags,
153 : hostname,
154 : &creds,
155 : out_data);
156 0 : return ret;
157 : }
158 :
159 : /**
160 : * Gets tickets forwarded to hostname. If the tickets that are
161 : * forwarded are address-less, the forwarded tickets will also be
162 : * address-less.
163 : *
164 : * If the ticket have any address, hostname will be used for figure
165 : * out the address to forward the ticket too. This since this might
166 : * use DNS, its insecure and also doesn't represent configured all
167 : * addresses of the host. For example, the host might have two
168 : * adresses, one IPv4 and one IPv6 address where the later is not
169 : * published in DNS. This IPv6 address might be used communications
170 : * and thus the resulting ticket useless.
171 : *
172 : * @param context A kerberos 5 context.
173 : * @param auth_context the auth context with the key to encrypt the out_data.
174 : * @param ccache credential cache to use
175 : * @param flags the flags to control the resulting ticket flags
176 : * @param hostname the host to forward the tickets too.
177 : * @param in_creds the in client and server ticket names. The client
178 : * and server components forwarded to the remote host.
179 : * @param out_data the resulting credential.
180 : *
181 : * @return Return an error code or 0.
182 : *
183 : * @ingroup krb5_credential
184 : */
185 :
186 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
187 19981 : krb5_get_forwarded_creds (krb5_context context,
188 : krb5_auth_context auth_context,
189 : krb5_ccache ccache,
190 : krb5_flags flags,
191 : const char *hostname,
192 : krb5_creds *in_creds,
193 : krb5_data *out_data)
194 : {
195 : krb5_error_code ret;
196 : krb5_creds *out_creds;
197 : krb5_addresses addrs, *paddrs;
198 : KRB_CRED cred;
199 : KrbCredInfo *krb_cred_info;
200 : EncKrbCredPart enc_krb_cred_part;
201 : size_t len;
202 : unsigned char *buf;
203 : size_t buf_size;
204 : krb5_kdc_flags kdc_flags;
205 : krb5_crypto crypto;
206 : struct addrinfo *ai;
207 : krb5_creds *ticket;
208 :
209 19981 : paddrs = NULL;
210 19981 : addrs.len = 0;
211 19981 : addrs.val = NULL;
212 :
213 19981 : ret = krb5_get_credentials(context, 0, ccache, in_creds, &ticket);
214 19981 : if(ret == 0) {
215 19978 : if (ticket->addresses.len)
216 0 : paddrs = &addrs;
217 19978 : krb5_free_creds (context, ticket);
218 : } else {
219 : krb5_boolean noaddr;
220 3 : krb5_appdefault_boolean(context, NULL,
221 : krb5_principal_get_realm(context,
222 3 : in_creds->client),
223 : "no-addresses", KRB5_ADDRESSLESS_DEFAULT,
224 : &noaddr);
225 3 : if (!noaddr)
226 0 : paddrs = &addrs;
227 : }
228 :
229 : /*
230 : * If tickets have addresses, get the address of the remote host.
231 : */
232 :
233 19981 : if (paddrs != NULL) {
234 :
235 0 : ret = getaddrinfo (hostname, NULL, NULL, &ai);
236 0 : if (ret) {
237 0 : krb5_error_code ret2 = krb5_eai_to_heim_errno(ret, errno);
238 0 : krb5_set_error_message(context, ret2,
239 0 : N_("resolving host %s failed: %s",
240 : "hostname, error"),
241 : hostname, gai_strerror(ret));
242 0 : return ret2;
243 : }
244 :
245 0 : ret = add_addrs (context, &addrs, ai);
246 0 : freeaddrinfo (ai);
247 0 : if (ret)
248 0 : return ret;
249 : }
250 :
251 19981 : kdc_flags.b = int2KDCOptions(flags);
252 :
253 19981 : ret = krb5_get_kdc_cred (context,
254 : ccache,
255 : kdc_flags,
256 : paddrs,
257 : NULL,
258 : in_creds,
259 : &out_creds);
260 19981 : krb5_free_addresses (context, &addrs);
261 19981 : if (ret)
262 189 : return ret;
263 :
264 19792 : memset (&cred, 0, sizeof(cred));
265 19792 : cred.pvno = 5;
266 19792 : cred.msg_type = krb_cred;
267 19792 : ALLOC_SEQ(&cred.tickets, 1);
268 19792 : if (cred.tickets.val == NULL) {
269 0 : ret = ENOMEM;
270 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
271 0 : goto out2;
272 : }
273 38834 : ret = decode_Ticket(out_creds->ticket.data,
274 19792 : out_creds->ticket.length,
275 : cred.tickets.val, &len);
276 19792 : if (ret)
277 0 : goto out3;
278 :
279 19792 : memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
280 19792 : ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1);
281 19792 : if (enc_krb_cred_part.ticket_info.val == NULL) {
282 0 : ret = ENOMEM;
283 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
284 0 : goto out4;
285 : }
286 :
287 19792 : if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
288 : krb5_timestamp sec;
289 : int32_t usec;
290 :
291 19792 : krb5_us_timeofday (context, &sec, &usec);
292 :
293 19792 : ALLOC(enc_krb_cred_part.timestamp, 1);
294 19792 : if (enc_krb_cred_part.timestamp == NULL) {
295 0 : ret = ENOMEM;
296 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
297 0 : goto out4;
298 : }
299 19792 : *enc_krb_cred_part.timestamp = sec;
300 19792 : ALLOC(enc_krb_cred_part.usec, 1);
301 19792 : if (enc_krb_cred_part.usec == NULL) {
302 0 : ret = ENOMEM;
303 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
304 0 : goto out4;
305 : }
306 19792 : *enc_krb_cred_part.usec = usec;
307 : } else {
308 0 : enc_krb_cred_part.timestamp = NULL;
309 0 : enc_krb_cred_part.usec = NULL;
310 : }
311 :
312 19792 : if (auth_context->local_address && auth_context->local_port && paddrs) {
313 :
314 0 : ret = krb5_make_addrport (context,
315 : &enc_krb_cred_part.s_address,
316 0 : auth_context->local_address,
317 0 : auth_context->local_port);
318 0 : if (ret)
319 0 : goto out4;
320 : }
321 :
322 19792 : if (auth_context->remote_address) {
323 0 : if (auth_context->remote_port) {
324 : krb5_boolean noaddr;
325 : krb5_const_realm srealm;
326 :
327 0 : srealm = krb5_principal_get_realm(context, out_creds->server);
328 : /* Is this correct, and should we use the paddrs == NULL
329 : trick here as well? Having an address-less ticket may
330 : indicate that we don't know our own global address, but
331 : it does not necessary mean that we don't know the
332 : server's. */
333 0 : krb5_appdefault_boolean(context, NULL, srealm, "no-addresses",
334 : FALSE, &noaddr);
335 0 : if (!noaddr) {
336 0 : ret = krb5_make_addrport (context,
337 : &enc_krb_cred_part.r_address,
338 0 : auth_context->remote_address,
339 0 : auth_context->remote_port);
340 0 : if (ret)
341 0 : goto out4;
342 : }
343 : } else {
344 0 : ALLOC(enc_krb_cred_part.r_address, 1);
345 0 : if (enc_krb_cred_part.r_address == NULL) {
346 0 : ret = ENOMEM;
347 0 : krb5_set_error_message(context, ret,
348 0 : N_("malloc: out of memory", ""));
349 0 : goto out4;
350 : }
351 :
352 0 : ret = krb5_copy_address (context, auth_context->remote_address,
353 0 : enc_krb_cred_part.r_address);
354 0 : if (ret)
355 0 : goto out4;
356 : }
357 : }
358 :
359 : /* fill ticket_info.val[0] */
360 :
361 19792 : enc_krb_cred_part.ticket_info.len = 1;
362 :
363 19792 : krb_cred_info = enc_krb_cred_part.ticket_info.val;
364 :
365 19792 : copy_EncryptionKey (&out_creds->session, &krb_cred_info->key);
366 19792 : ALLOC(krb_cred_info->prealm, 1);
367 19792 : copy_Realm (&out_creds->client->realm, krb_cred_info->prealm);
368 19792 : ALLOC(krb_cred_info->pname, 1);
369 19792 : copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname);
370 19792 : ALLOC(krb_cred_info->flags, 1);
371 19792 : *krb_cred_info->flags = out_creds->flags.b;
372 19792 : ALLOC(krb_cred_info->authtime, 1);
373 19792 : *krb_cred_info->authtime = out_creds->times.authtime;
374 19792 : ALLOC(krb_cred_info->starttime, 1);
375 19792 : *krb_cred_info->starttime = out_creds->times.starttime;
376 19792 : ALLOC(krb_cred_info->endtime, 1);
377 19792 : *krb_cred_info->endtime = out_creds->times.endtime;
378 19792 : ALLOC(krb_cred_info->renew_till, 1);
379 19792 : *krb_cred_info->renew_till = out_creds->times.renew_till;
380 19792 : ALLOC(krb_cred_info->srealm, 1);
381 19792 : copy_Realm (&out_creds->server->realm, krb_cred_info->srealm);
382 19792 : ALLOC(krb_cred_info->sname, 1);
383 19792 : copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname);
384 19792 : ALLOC(krb_cred_info->caddr, 1);
385 19792 : copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr);
386 :
387 19792 : krb5_free_creds (context, out_creds);
388 :
389 : /* encode EncKrbCredPart */
390 :
391 19792 : ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size,
392 : &enc_krb_cred_part, &len, ret);
393 19792 : free_EncKrbCredPart (&enc_krb_cred_part);
394 19792 : if (ret) {
395 0 : free_KRB_CRED(&cred);
396 0 : return ret;
397 : }
398 19792 : if(buf_size != len)
399 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
400 :
401 : /**
402 : * Some older of the MIT gssapi library used clear-text tickets
403 : * (warped inside AP-REQ encryption), use the krb5_auth_context
404 : * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those
405 : * tickets. The session key is used otherwise to encrypt the
406 : * forwarded ticket.
407 : */
408 :
409 19792 : if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
410 19792 : cred.enc_part.etype = KRB5_ENCTYPE_NULL;
411 19792 : cred.enc_part.kvno = NULL;
412 19792 : cred.enc_part.cipher.data = buf;
413 19792 : cred.enc_part.cipher.length = buf_size;
414 : } else {
415 : /*
416 : * Here older versions then 0.7.2 of Heimdal used the local or
417 : * remote subkey. That is wrong, the session key should be
418 : * used. Heimdal 0.7.2 and newer have code to try both in the
419 : * receiving end.
420 : */
421 :
422 0 : ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
423 0 : if (ret) {
424 0 : free(buf);
425 0 : free_KRB_CRED(&cred);
426 0 : return ret;
427 : }
428 0 : ret = krb5_encrypt_EncryptedData (context,
429 : crypto,
430 : KRB5_KU_KRB_CRED,
431 : buf,
432 : len,
433 : 0,
434 : &cred.enc_part);
435 0 : free(buf);
436 0 : krb5_crypto_destroy(context, crypto);
437 0 : if (ret) {
438 0 : free_KRB_CRED(&cred);
439 0 : return ret;
440 : }
441 : }
442 :
443 19792 : ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret);
444 19792 : free_KRB_CRED (&cred);
445 19792 : if (ret)
446 0 : return ret;
447 19792 : if(buf_size != len)
448 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
449 19792 : out_data->length = len;
450 19792 : out_data->data = buf;
451 19792 : return 0;
452 0 : out4:
453 0 : free_EncKrbCredPart(&enc_krb_cred_part);
454 0 : out3:
455 0 : free_KRB_CRED(&cred);
456 0 : out2:
457 0 : krb5_free_creds (context, out_creds);
458 0 : return ret;
459 : }
|