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 set_tgs_creds(krb5_context, krb5_ccache,
37 : krb5_const_principal,
38 : krb5_const_principal, krb5_creds *);
39 : static krb5_error_code get_cred(krb5_context, krb5_ccache, krb5_creds *,
40 : krb5_flags, const char *, krb5_creds **);
41 : static krb5_error_code get_addresses(krb5_context, krb5_ccache, krb5_creds *,
42 : const char *, krb5_addresses *);
43 :
44 : static krb5_error_code
45 0 : add_addrs(krb5_context context,
46 : krb5_addresses *addr,
47 : struct addrinfo *ai)
48 : {
49 0 : krb5_error_code ret;
50 0 : unsigned n, i;
51 0 : void *tmp;
52 0 : struct addrinfo *a;
53 :
54 0 : n = 0;
55 0 : for (a = ai; a != NULL; a = a->ai_next)
56 0 : ++n;
57 :
58 0 : tmp = realloc(addr->val, (addr->len + n) * sizeof(*addr->val));
59 0 : if (tmp == NULL && (addr->len + n) != 0) {
60 0 : ret = krb5_enomem(context);
61 0 : goto fail;
62 : }
63 0 : addr->val = tmp;
64 0 : for (i = addr->len; i < (addr->len + n); ++i) {
65 0 : addr->val[i].addr_type = 0;
66 0 : krb5_data_zero(&addr->val[i].address);
67 : }
68 0 : i = addr->len;
69 0 : for (a = ai; a != NULL; a = a->ai_next) {
70 0 : krb5_address ad;
71 :
72 0 : ret = krb5_sockaddr2address (context, a->ai_addr, &ad);
73 0 : if (ret == 0) {
74 0 : if (krb5_address_search(context, &ad, addr))
75 0 : krb5_free_address(context, &ad);
76 : else
77 0 : addr->val[i++] = ad;
78 : }
79 0 : else if (ret == KRB5_PROG_ATYPE_NOSUPP)
80 0 : krb5_clear_error_message (context);
81 : else
82 0 : goto fail;
83 0 : addr->len = i;
84 : }
85 0 : return 0;
86 0 : fail:
87 0 : krb5_free_addresses (context, addr);
88 0 : return ret;
89 : }
90 :
91 : /**
92 : * Forward credentials for client to host hostname, making them
93 : * forwardable if forwardable, and returning the blob of data to sent
94 : * in out_data. If hostname == NULL, pick it from server.
95 : *
96 : * If the server's realm is configured for delegation of destination
97 : * TGTs, forward a TGT for the server realm, rather than the client
98 : * realm. This works better with destinations on the far side of a
99 : * firewall. We also forward the destination TGT when the client
100 : * TGT is not available (we may have just the destination TGT).
101 : *
102 : * @param context A kerberos 5 context.
103 : * @param auth_context the auth context with the key to encrypt the out_data.
104 : * @param hostname the host to forward the tickets too.
105 : * @param client the client to delegate from.
106 : * @param server the server to delegate the credential too.
107 : * @param ccache credential cache to use.
108 : * @param forwardable make the forwarded ticket forwabledable.
109 : * @param out_data the resulting credential.
110 : *
111 : * @return Return an error code or 0.
112 : *
113 : * @ingroup krb5_credential
114 : */
115 :
116 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
117 23039 : krb5_fwd_tgt_creds(krb5_context context,
118 : krb5_auth_context auth_context,
119 : const char *hostname,
120 : krb5_const_principal client,
121 : krb5_const_principal server,
122 : krb5_ccache ccache,
123 : int forwardable,
124 : krb5_data *out_data)
125 : {
126 23039 : krb5_flags flags = 0;
127 1035 : krb5_creds creds;
128 1035 : krb5_error_code ret;
129 :
130 23039 : flags |= KDC_OPT_FORWARDED;
131 :
132 23039 : if (forwardable)
133 23039 : flags |= KDC_OPT_FORWARDABLE;
134 :
135 23039 : if (hostname == NULL &&
136 0 : krb5_principal_get_type(context, server) == KRB5_NT_SRV_HST) {
137 0 : const char *inst = krb5_principal_get_comp_string(context, server, 0);
138 0 : const char *host = krb5_principal_get_comp_string(context, server, 1);
139 :
140 0 : if (inst != NULL &&
141 0 : strcmp(inst, "host") == 0 &&
142 0 : host != NULL &&
143 0 : krb5_principal_get_comp_string(context, server, 2) == NULL)
144 0 : hostname = host;
145 : }
146 :
147 : /*
148 : * Fill-in the request creds, the server principal will be the TGS
149 : * of either the client's or the server's realm.
150 : */
151 23039 : ret = set_tgs_creds(context, ccache, client, server, &creds);
152 23039 : if (ret)
153 0 : return ret;
154 :
155 23039 : ret = krb5_get_forwarded_creds (context,
156 : auth_context,
157 : ccache,
158 : flags,
159 : hostname,
160 : &creds,
161 : out_data);
162 :
163 23039 : krb5_free_cred_contents(context, &creds);
164 23039 : return ret;
165 : }
166 :
167 : /**
168 : * Gets tickets forwarded to hostname. If the tickets that are
169 : * forwarded are address-less, the forwarded tickets will also be
170 : * address-less.
171 : *
172 : * If the ticket have any address, hostname will be used for figure
173 : * out the address to forward the ticket too. This since this might
174 : * use DNS, its insecure and also doesn't represent configured all
175 : * addresses of the host. For example, the host might have two
176 : * adresses, one IPv4 and one IPv6 address where the later is not
177 : * published in DNS. This IPv6 address might be used communications
178 : * and thus the resulting ticket useless.
179 : *
180 : * @param context A kerberos 5 context.
181 : * @param auth_context the auth context with the key to encrypt the out_data.
182 : * @param ccache credential cache to use
183 : * @param flags the flags to control the resulting ticket flags
184 : * @param hostname the host to forward the tickets too.
185 : * @param in_creds the in client and server ticket names. The client
186 : * and server components forwarded to the remote host.
187 : * @param out_data the resulting credential.
188 : *
189 : * @return Return an error code or 0.
190 : *
191 : * @ingroup krb5_credential
192 : */
193 :
194 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
195 23039 : krb5_get_forwarded_creds (krb5_context context,
196 : krb5_auth_context auth_context,
197 : krb5_ccache ccache,
198 : krb5_flags flags,
199 : const char *hostname,
200 : krb5_creds *in_creds,
201 : krb5_data *out_data)
202 : {
203 1035 : krb5_error_code ret;
204 1035 : krb5_creds *creds;
205 :
206 : /* Obtain the requested TGT */
207 23039 : ret = get_cred(context, ccache, in_creds, flags, hostname, &creds);
208 23039 : if (ret)
209 11 : return ret;
210 :
211 : /* Forward obtained creds */
212 23028 : ret = _krb5_mk_1cred(context, auth_context, creds, out_data, NULL);
213 23028 : krb5_free_creds(context, creds);
214 23028 : return ret;
215 : }
216 :
217 : /*
218 : * Get a TGT for forwarding to hostname. If the client TGT is
219 : * addressless, the forwarded ticket will also be addressless.
220 : *
221 : * If the TGT has any addresses, hostname will be used to determine
222 : * the address to forward the ticket to. Thus, since this might use DNS,
223 : * it's insecure and also may not capture all the addresses of the host.
224 : * In general addressless tickets are more robust, be it at a small
225 : * security penalty.
226 : *
227 : * @param context A kerberos 5 context.
228 : * @param ccache The credential cache to use
229 : * @param creds Creds with client and server principals
230 : * @param flags The flags to control the resulting ticket flags
231 : * @param hostname The hostname of server
232 : * @param out_creds The resulting credential
233 : *
234 : * @return Return an error code or 0.
235 : */
236 :
237 : static krb5_error_code
238 23039 : get_cred(krb5_context context,
239 : krb5_ccache ccache,
240 : krb5_creds *creds,
241 : krb5_flags flags,
242 : const char *hostname,
243 : krb5_creds **out_creds)
244 : {
245 1035 : krb5_error_code ret;
246 1035 : krb5_kdc_flags kdc_flags;
247 1035 : krb5_addresses addrs;
248 :
249 23039 : addrs.len = 0;
250 23039 : addrs.val = NULL;
251 23039 : ret = get_addresses(context, ccache, creds, hostname, &addrs);
252 23039 : if (ret)
253 0 : return ret;
254 :
255 23039 : kdc_flags.b = int2KDCOptions(flags);
256 23039 : ret = krb5_get_kdc_cred(context, ccache, kdc_flags, &addrs, NULL,
257 : creds, out_creds);
258 :
259 23039 : krb5_free_addresses(context, &addrs);
260 23039 : return ret;
261 : }
262 :
263 : static krb5_error_code
264 23039 : set_tgs_creds(krb5_context context,
265 : krb5_ccache ccache,
266 : krb5_const_principal client,
267 : krb5_const_principal server,
268 : krb5_creds *creds)
269 : {
270 1035 : krb5_error_code ret;
271 1035 : krb5_const_realm client_realm;
272 1035 : krb5_const_realm server_realm;
273 1035 : krb5_boolean fwd_dest_tgt;
274 1035 : krb5_creds *client_tgt;
275 :
276 23039 : client_realm = krb5_principal_get_realm(context, client);
277 23039 : server_realm = krb5_principal_get_realm(context, server);
278 :
279 23039 : memset (creds, 0, sizeof(*creds));
280 23039 : ret = krb5_copy_principal(context, client, &creds->client);
281 23039 : if (ret)
282 0 : return ret;
283 23039 : ret = krb5_make_principal(context, &creds->server, client_realm,
284 : KRB5_TGS_NAME, client_realm, NULL);
285 23039 : if (ret) {
286 0 : krb5_free_principal(context, creds->client);
287 0 : return ret;
288 : }
289 :
290 : /*
291 : * Optionally delegate a TGT for the server's realm, rather than
292 : * the client's. Do this also when we don't have a client realm TGT.
293 : *
294 : * XXX: Note, when we have a start-realm, and delegate-destination-tgt
295 : * is not set, we must use the start-realm.
296 : */
297 23039 : krb5_appdefault_boolean(context, NULL, server_realm,
298 : "delegate-destination-tgt", FALSE, &fwd_dest_tgt);
299 :
300 23039 : if (!fwd_dest_tgt) {
301 23039 : ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, creds,
302 : &client_tgt);
303 23039 : if (ret == 0) {
304 23030 : krb5_free_creds(context, client_tgt);
305 23030 : return ret;
306 : }
307 : }
308 :
309 : /*
310 : * Client TGT inapplicable or unavailable
311 : */
312 9 : krb5_free_principal(context, creds->server);
313 9 : creds->server = 0;
314 9 : return krb5_make_principal(context, &creds->server, server_realm,
315 : KRB5_TGS_NAME, server_realm, NULL);
316 : }
317 :
318 : /*
319 : * Obtain address list for hostname if server realm policy is not addressless.
320 : */
321 : static krb5_error_code
322 23039 : get_addresses(krb5_context context,
323 : krb5_ccache ccache,
324 : krb5_creds *creds,
325 : const char *hostname,
326 : krb5_addresses *addrs)
327 : {
328 1035 : krb5_error_code ret;
329 1035 : krb5_creds *ticket;
330 1035 : krb5_const_realm realm;
331 1035 : krb5_boolean noaddr;
332 1035 : struct addrinfo *ai, hints;
333 1035 : int eai;
334 :
335 23039 : if (hostname == 0)
336 0 : return 0;
337 :
338 23039 : ret = krb5_get_credentials(context, 0, ccache, creds, &ticket);
339 23039 : if (ret == 0) {
340 23030 : noaddr = (ticket->addresses.len == 0) ? TRUE : FALSE;
341 23030 : krb5_free_creds(context, ticket);
342 : } else {
343 9 : realm = krb5_principal_get_realm(context, creds->server);
344 9 : krb5_appdefault_boolean(context, NULL, realm, "no-addresses",
345 : KRB5_ADDRESSLESS_DEFAULT, &noaddr);
346 : }
347 :
348 23039 : if (noaddr)
349 22004 : return 0;
350 :
351 : /* Need addresses, get the address of the remote host. */
352 0 : memset(&hints, 0, sizeof(hints));
353 0 : if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns",
354 : NULL)) {
355 0 : hints.ai_flags &= ~AI_CANONNAME;
356 0 : hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV;
357 : }
358 0 : eai = getaddrinfo(hostname, NULL, &hints, &ai);
359 0 : if (eai) {
360 0 : ret = krb5_eai_to_heim_errno(eai, errno);
361 0 : krb5_set_error_message(context, ret,
362 0 : N_("resolving host %s failed: %s",
363 : "hostname, error"),
364 : hostname, gai_strerror(eai));
365 0 : return ret;
366 : }
367 :
368 0 : ret = add_addrs(context, addrs, ai);
369 0 : freeaddrinfo(ai);
370 :
371 0 : return ret;
372 : }
|