2 * Copyright (c) 1994 by the Massachusetts Institute of Technology.
3 * Copyright (c) 1994 CyberSAFE Corporation
4 * Copyright (c) 1993 Open Computing Security Group
5 * Copyright (c) 1990,1991 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Neither M.I.T., the Open Computing Security Group, nor
21 * CyberSAFE Corporation make any representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
25 * krb5_get_cred_from_kdc()
26 * Get credentials from some KDC somewhere, possibly accumulating tgts
32 #include "int-proto.h"
35 * Retrieve credentials for principal creds->client,
36 * server creds->server, ticket flags creds->ticket_flags, possibly
37 * second_ticket if needed by ticket_flags.
39 * Credentials are requested from the KDC for the server's realm. Any
40 * TGT credentials obtained in the process of contacting the KDC are
41 * returned in an array of credentials; tgts is filled in to point to an
42 * array of pointers to credential structures (if no TGT's were used, the
43 * pointer is zeroed). TGT's may be returned even if no useful end ticket
46 * The returned credentials are NOT cached.
48 * This routine should not be called if the credentials are already in
51 * If credentials are obtained, creds is filled in with the results;
52 * creds->ticket and creds->keyblock->key are set to allocated storage,
53 * which should be freed by the caller when finished.
55 * returns errors, system errors.
58 extern krb5_cksumtype krb5_kdc_req_sumtype;
60 /* helper macro: convert flags to necessary KDC options */
62 #define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
64 krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
67 krb5_get_cred_from_kdc(context, ccache, in_cred, out_cred, tgts)
71 krb5_creds **out_cred;
74 krb5_creds **ret_tgts = NULL;
77 krb5_creds tgt, tgtq, *tgtr = NULL;
79 krb5_error_code retval;
80 krb5_principal int_server = NULL; /* Intermediate server for request */
82 krb5_principal *tgs_list = NULL;
83 krb5_principal *top_server = NULL;
84 krb5_principal *next_server = NULL;
87 /* in case we never get a TGT, zero the return */
91 memset((char *)&tgtq, 0, sizeof(tgtq));
92 memset((char *)&tgt, 0, sizeof(tgt));
95 * we know that the desired credentials aren't in the cache yet.
97 * To get them, we first need a tgt for the realm of the server.
98 * first, we see if we have such a TGT in cache. if not, then
99 * we ask the kdc to give us one. if that doesn't work, then
100 * we try to get a tgt for a realm that is closest to the target.
101 * once we have that, then we ask that realm if it can give us
102 * tgt for the target. if not, we do the process over with this
107 * (the ticket may be issued by some other intermediate
108 * realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY)
110 if (retval = krb5_copy_principal(context, in_cred->client, &tgtq.client))
113 /* get target tgt from cache */
114 if (retval = krb5_tgtname(context, krb5_princ_realm(context, in_cred->server),
115 krb5_princ_realm(context, in_cred->client),
120 if (retval = krb5_copy_principal(context, int_server, &tgtq.server)) {
124 if (retval = krb5_cc_retrieve_cred(context, ccache,
125 KRB5_TC_MATCH_SRV_NAMEONLY,
129 if (retval != KRB5_CC_NOTFOUND) {
134 * Note that we want to request a TGT from our local KDC, even
135 * if we already have a TGT for some intermediate realm. The
136 * reason is that our local KDC may have a shortcut to the
137 * destination realm, and if it does we want to use the
138 * shortcut because it will provide greater security. - bcn
142 * didn't find it in the cache so it is time to get a local
143 * tgt and walk the realms tree.
145 krb5_free_principal(context, int_server);
147 if (retval = krb5_tgtname(context,
148 krb5_princ_realm(context, in_cred->client),
149 krb5_princ_realm(context, in_cred->client),
154 krb5_free_cred_contents(context, &tgtq);
155 memset((char *)&tgtq, 0, sizeof(tgtq));
156 if(retval = krb5_copy_principal(context, in_cred->client, &tgtq.client))
158 if(retval = krb5_copy_principal(context, int_server, &tgtq.server))
161 if (retval = krb5_cc_retrieve_cred(context, ccache,
162 KRB5_TC_MATCH_SRV_NAMEONLY,
168 /* get a list of realms to consult */
170 if (retval = krb5_walk_realm_tree(context,
171 krb5_princ_realm(context,in_cred->client),
172 krb5_princ_realm(context,in_cred->server),
174 KRB5_REALM_BRANCH_CHAR)) {
178 for (nservers = 0; tgs_list[nservers]; nservers++)
181 /* allocate storage for TGT pointers. */
183 if (!(ret_tgts = (krb5_creds **) calloc(nservers+1, sizeof(krb5_creds)))) {
190 * step one is to take the current tgt and see if there is a tgt for
191 * krbtgt/realmof(target)@realmof(tgt). if not, try to get one with
194 * if we don't get a tgt for the target, then try to find a tgt as
195 * close to the target realm as possible. at each step if there isn't
196 * a tgt in the cache we have to try and get one with our latest tgt.
197 * once we have a tgt for a closer realm, we go back to step one.
199 * once we have a tgt for the target, we go try and get credentials.
202 for (top_server = tgs_list;
203 top_server < tgs_list + nservers;
204 top_server = next_server) {
206 /* look in cache for a tgt for the destination */
208 krb5_free_cred_contents(context, &tgtq);
209 memset(&tgtq, 0, sizeof(tgtq));
210 if(retval = krb5_copy_principal(context, tgt.client, &tgtq.client))
213 krb5_free_principal(context, int_server);
215 if (retval = krb5_tgtname(context,
216 krb5_princ_realm(context, in_cred->server),
217 krb5_princ_realm(context, *top_server),
222 if(retval = krb5_copy_principal(context, int_server, &tgtq.server))
225 if (retval = krb5_cc_retrieve_cred(context, ccache,
226 KRB5_TC_MATCH_SRV_NAMEONLY,
230 if (retval != KRB5_CC_NOTFOUND) {
234 /* didn't find it in the cache so try and get one */
235 /* with current tgt. */
237 if (!valid_keytype(tgt.keyblock.keytype)) {
238 retval = KRB5_PROG_KEYTYPE_NOSUPP;
242 krb5_free_cred_contents(context, &tgtq);
243 memset(&tgtq, 0, sizeof(tgtq));
244 tgtq.times = tgt.times;
245 if (retval = krb5_copy_principal(context, tgt.client, &tgtq.client))
247 if(retval = krb5_copy_principal(context, int_server, &tgtq.server))
249 tgtq.is_skey = FALSE;
250 tgtq.ticket_flags = tgt.ticket_flags;
252 if(retval = krb5_get_cred_via_tgt(context, &tgt,
253 FLAGS2OPTS(tgtq.ticket_flags),
254 krb5_kdc_req_sumtype,
258 * couldn't get one so now loop backwards through the realms
259 * list and try and get a tgt for a realm as close to the
260 * target as possible. the kdc should give us a tgt for the
261 * closest one it knows about, but not all kdc's do this yet.
264 for (next_server = tgs_list + nservers - 1;
265 next_server > top_server;
267 krb5_free_cred_contents(context, &tgtq);
268 memset(&tgtq, 0, sizeof(tgtq));
269 if (retval = krb5_copy_principal(context, tgt.client, &tgtq.client))
272 krb5_free_principal(context, int_server);
274 if (retval = krb5_tgtname(context,
275 krb5_princ_realm(context, *next_server),
276 krb5_princ_realm(context, *top_server),
281 if(retval = krb5_copy_principal(context, int_server, &tgtq.server))
284 if(retval = krb5_cc_retrieve_cred(context, ccache,
285 KRB5_TC_MATCH_SRV_NAMEONLY,
287 if (retval != KRB5_CC_NOTFOUND) {
291 /* not in the cache so try and get one with our current tgt. */
293 if (!valid_keytype(tgt.keyblock.keytype)) {
294 retval = KRB5_PROG_KEYTYPE_NOSUPP;
298 krb5_free_cred_contents(context, &tgtq);
299 memset(&tgtq, 0, sizeof(tgtq));
300 tgtq.times = tgt.times;
301 if (retval = krb5_copy_principal(context,tgt.client,&tgtq.client))
303 if(retval = krb5_copy_principal(context,int_server,&tgtq.server))
305 tgtq.is_skey = FALSE;
306 tgtq.ticket_flags = tgt.ticket_flags;
308 if (retval = krb5_get_cred_via_tgt(context, &tgt,
309 FLAGS2OPTS(tgtq.ticket_flags),
310 krb5_kdc_req_sumtype,
315 /* save tgt in return array */
316 if (retval = krb5_copy_creds(context, tgtr, &ret_tgts[ntgts])) {
319 krb5_free_creds(context, tgtr);
322 tgt = *ret_tgts[ntgts++];
325 /* got one as close as possible, now start all over */
330 if (next_server == top_server) {
337 * Got a tgt. If it is for the target realm we can go try for the
338 * credentials. If it is not for the target realm, then make sure it
339 * is in the realms hierarchy and if so, save it and start the loop
340 * over from there. Note that we only need to compare the instance
341 * names since that is the target realm of the tgt.
344 for (next_server = top_server; *next_server; next_server++) {
345 krb5_data *realm_1 = krb5_princ_component(context, next_server[0], 1);
346 krb5_data *realm_2 = krb5_princ_component(context, tgtr->server, 1);
347 if (realm_1->length == realm_2->length &&
348 !memcmp(realm_1->data, realm_2->data, realm_1->length)) {
354 retval = KRB5_KDCREP_MODIFIED;
358 if (retval = krb5_copy_creds(context, tgtr, &ret_tgts[ntgts])) {
361 krb5_free_creds(context, tgtr);
364 tgt = *ret_tgts[ntgts++];
366 /* we're done if it is the target */
368 if (!*next_server++) break;
373 /* got/finally have tgt! try for the creds */
375 if (!valid_keytype(tgt.keyblock.keytype)) {
376 retval = KRB5_PROG_KEYTYPE_NOSUPP;
381 if (in_cred->second_ticket.length) {
382 retval = krb5_get_cred_via_2tgt(context, &tgt,
383 KDC_OPT_ENC_TKT_IN_SKEY |
384 FLAGS2OPTS(tgt.ticket_flags),
385 krb5_kdc_req_sumtype, in_cred, out_cred);
387 retval = krb5_get_cred_via_tgt(context, &tgt,
388 FLAGS2OPTS(tgt.ticket_flags),
389 krb5_kdc_req_sumtype, in_cred, out_cred);
392 /* cleanup and return */
396 if (tgtr) krb5_free_creds(context, tgtr);
397 if(tgs_list) krb5_free_realm_tree(context, tgs_list);
398 krb5_free_cred_contents(context, &tgtq);
399 if (int_server) krb5_free_principal(context, int_server);
402 if (ret_tgts) free(ret_tgts);
403 krb5_free_cred_contents(context, &tgt);