2 * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 #include "krb5_locl.h"
36 static krb5_error_code
set_tgs_creds(krb5_context
, krb5_ccache
,
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
*);
44 static krb5_error_code
45 add_addrs(krb5_context context
,
55 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
)
58 tmp
= realloc(addr
->val
, (addr
->len
+ n
) * sizeof(*addr
->val
));
59 if (tmp
== NULL
&& (addr
->len
+ n
) != 0) {
60 ret
= krb5_enomem(context
);
64 for (i
= addr
->len
; i
< (addr
->len
+ n
); ++i
) {
65 addr
->val
[i
].addr_type
= 0;
66 krb5_data_zero(&addr
->val
[i
].address
);
69 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
72 ret
= krb5_sockaddr2address (context
, a
->ai_addr
, &ad
);
74 if (krb5_address_search(context
, &ad
, addr
))
75 krb5_free_address(context
, &ad
);
79 else if (ret
== KRB5_PROG_ATYPE_NOSUPP
)
80 krb5_clear_error_message (context
);
87 krb5_free_addresses (context
, addr
);
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.
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).
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.
111 * @return Return an error code or 0.
113 * @ingroup krb5_credential
116 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
117 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
,
126 krb5_flags flags
= 0;
130 flags
|= KDC_OPT_FORWARDED
;
133 flags
|= KDC_OPT_FORWARDABLE
;
135 if (hostname
== NULL
&&
136 krb5_principal_get_type(context
, server
) == KRB5_NT_SRV_HST
) {
137 const char *inst
= krb5_principal_get_comp_string(context
, server
, 0);
138 const char *host
= krb5_principal_get_comp_string(context
, server
, 1);
141 strcmp(inst
, "host") == 0 &&
143 krb5_principal_get_comp_string(context
, server
, 2) == NULL
)
148 * Fill-in the request creds, the server principal will be the TGS
149 * of either the client's or the server's realm.
151 ret
= set_tgs_creds(context
, ccache
, client
, server
, &creds
);
155 ret
= krb5_get_forwarded_creds (context
,
163 krb5_free_cred_contents(context
, &creds
);
168 * Gets tickets forwarded to hostname. If the tickets that are
169 * forwarded are address-less, the forwarded tickets will also be
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 * addresses, 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.
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.
189 * @return Return an error code or 0.
191 * @ingroup krb5_credential
194 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
195 krb5_get_forwarded_creds (krb5_context context
,
196 krb5_auth_context auth_context
,
199 const char *hostname
,
200 krb5_creds
*in_creds
,
206 /* Obtain the requested TGT */
207 ret
= get_cred(context
, ccache
, in_creds
, flags
, hostname
, &creds
);
211 /* Forward obtained creds */
212 ret
= _krb5_mk_1cred(context
, auth_context
, creds
, out_data
, NULL
);
213 krb5_free_creds(context
, creds
);
218 * Get a TGT for forwarding to hostname. If the client TGT is
219 * addressless, the forwarded ticket will also be addressless.
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
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
234 * @return Return an error code or 0.
237 static krb5_error_code
238 get_cred(krb5_context context
,
242 const char *hostname
,
243 krb5_creds
**out_creds
)
246 krb5_kdc_flags kdc_flags
;
247 krb5_addresses addrs
;
251 ret
= get_addresses(context
, ccache
, creds
, hostname
, &addrs
);
255 kdc_flags
.b
= int2KDCOptions(flags
);
256 ret
= krb5_get_kdc_cred(context
, ccache
, kdc_flags
, &addrs
, NULL
,
259 krb5_free_addresses(context
, &addrs
);
263 static krb5_error_code
264 set_tgs_creds(krb5_context context
,
266 krb5_const_principal client
,
267 krb5_const_principal server
,
271 krb5_const_realm client_realm
;
272 krb5_const_realm server_realm
;
273 krb5_boolean fwd_dest_tgt
;
274 krb5_creds
*client_tgt
;
276 client_realm
= krb5_principal_get_realm(context
, client
);
277 server_realm
= krb5_principal_get_realm(context
, server
);
279 memset (creds
, 0, sizeof(*creds
));
280 ret
= krb5_copy_principal(context
, client
, &creds
->client
);
283 ret
= krb5_make_principal(context
, &creds
->server
, client_realm
,
284 KRB5_TGS_NAME
, client_realm
, NULL
);
286 krb5_free_principal(context
, creds
->client
);
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.
294 * XXX: Note, when we have a start-realm, and delegate-destination-tgt
295 * is not set, we must use the start-realm.
297 krb5_appdefault_boolean(context
, NULL
, server_realm
,
298 "delegate-destination-tgt", FALSE
, &fwd_dest_tgt
);
301 ret
= krb5_get_credentials(context
, KRB5_GC_CACHED
, ccache
, creds
,
304 krb5_free_creds(context
, client_tgt
);
310 * Client TGT inapplicable or unavailable
312 krb5_free_principal(context
, creds
->server
);
314 return krb5_make_principal(context
, &creds
->server
, server_realm
,
315 KRB5_TGS_NAME
, server_realm
, NULL
);
319 * Obtain address list for hostname if server realm policy is not addressless.
321 static krb5_error_code
322 get_addresses(krb5_context context
,
325 const char *hostname
,
326 krb5_addresses
*addrs
)
330 krb5_const_realm realm
;
332 struct addrinfo
*ai
, hints
;
338 ret
= krb5_get_credentials(context
, 0, ccache
, creds
, &ticket
);
340 noaddr
= (ticket
->addresses
.len
== 0) ? TRUE
: FALSE
;
341 krb5_free_creds(context
, ticket
);
343 realm
= krb5_principal_get_realm(context
, creds
->server
);
344 krb5_appdefault_boolean(context
, NULL
, realm
, "no-addresses",
345 KRB5_ADDRESSLESS_DEFAULT
, &noaddr
);
351 /* Need addresses, get the address of the remote host. */
352 memset(&hints
, 0, sizeof(hints
));
353 if (krb5_config_get_bool(context
, NULL
, "libdefaults", "block_dns",
355 hints
.ai_flags
&= ~AI_CANONNAME
;
356 hints
.ai_flags
|= AI_NUMERICHOST
|AI_NUMERICSERV
;
358 eai
= getaddrinfo(hostname
, NULL
, &hints
, &ai
);
360 ret
= krb5_eai_to_heim_errno(eai
, errno
);
361 krb5_set_error_message(context
, ret
,
362 N_("resolving host %s failed: %s",
364 hostname
, gai_strerror(eai
));
368 ret
= add_addrs(context
, addrs
, ai
);