aboutsummaryrefslogtreecommitdiff
path: root/src/lib/krb5/krb/gc_frm_kdc.c
blob: 321922ed20228f4a24d45d984ed94270d518ca81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
 * $Source$
 * $Author$
 *
 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * For copying and distribution information, please see the file
 * <krb5/copyright.h>.
 *
 * Get credentials from some KDC somewhere, possibly accumulating tgts
 * along the way.
 */

#if !defined(lint) && !defined(SABER)
static char rcsid_gcfkdc_c[] =
"$Id$";
#endif	/* !lint & !SABER */


#include <krb5/krb5.h>
#include <krb5/ext-proto.h>
#include "int-proto.h"

/*
 * Retrieve credentials for principal creds->client,
 * server creds->server, ticket flags creds->ticket_flags, possibly
 * second_ticket if needed by ticket_flags.
 * 
 * Credentials are requested from the KDC for the server's realm.  Any
 * TGT credentials obtained in the process of contacting the KDC are
 * returned in an array of credentials; tgts is filled in to point to an
 * array of pointers to credential structures (if no TGT's were used, the
 * pointer is zeroed).  TGT's may be returned even if no useful end ticket
 * was obtained.
 * 
 * The returned credentials are NOT cached.
 *
 * This routine should not be called if the credentials are already in
 * the cache.
 * 
 * If credentials are obtained, creds is filled in with the results;
 * creds->ticket and creds->keyblock->key are set to allocated storage,
 * which should be freed by the caller when finished.
 * 
 * returns errors, system errors.
 */

extern krb5_cksumtype krb5_kdc_req_sumtype;

/* helper function: convert flags to necessary KDC options */
#define flags2options(flags) (flags & KDC_TKT_COMMON_MASK)

krb5_error_code
krb5_get_cred_from_kdc (ccache, cred, tgts)
    krb5_ccache ccache;
    krb5_creds *cred;
    krb5_creds ***tgts;
{
    krb5_creds tgt, tgtq;
    krb5_creds **ret_tgts = 0;
    krb5_principal *tgs_list, *next_server;
    krb5_principal final_server;
    krb5_error_code retval;
    int nservers;
    krb5_enctype etype;

    /* in case we never get a TGT, zero the return */
    *tgts = 0;

    /*
     * we know that the desired credentials aren't in the cache yet.
     *
     * To get them, we first need a tgt for the realm of the server.
     * first, we see if we have such a TGT in cache.
     */
    
    /*
     * look for ticket with:
     * client == cred->client,
     * server == "krbtgt/realmof(cred->server)@realmof(cred->client)"
     */

    /*
     * XXX we're sharing some substructure here, which is
     * probably not safe...
     */
    memset((char *)&tgtq, 0, sizeof(tgtq));
    tgtq.client = cred->client;

    if (retval = krb5_tgtname(krb5_princ_realm(cred->server),
			      krb5_princ_realm(cred->client), &final_server))
	return retval;
    tgtq.server = final_server;

    /* try to fetch it directly */
    retval = krb5_cc_retrieve_cred (ccache,
				    0,	/* default is client & server */
				    &tgtq,
				    &tgt);

    if (retval != 0) {
	if (retval != KRB5_CC_NOTFOUND)
	    goto out;
	/* don't have the right TGT in the cred cache.  Time to iterate
	   across realms to get the right TGT. */

	/* get a list of realms to consult */
	retval = krb5_walk_realm_tree(krb5_princ_realm(cred->client),
				      krb5_princ_realm(cred->server),
				      &tgs_list);
	if (retval)
	    goto out;
	/* walk the list BACKWARDS until we find a cached
	   TGT, then move forward obtaining TGTs until we get the last
	   TGT needed */
	for (next_server = tgs_list; *next_server; next_server++);
	nservers = next_server - tgs_list;
	next_server--;

	/* next_server now points to the last TGT */
	for (; next_server >= tgs_list; next_server--) {
	    tgtq.server = *next_server;
	    retval = krb5_cc_retrieve_cred (ccache,
					    KRB5_TC_MATCH_SRV_NAMEONLY,
					    &tgtq,
					    &tgt);
	    if (retval) {
		if (retval != KRB5_CC_NOTFOUND) {
		    krb5_free_realm_tree(tgs_list);
		    goto out;
		}
		continue;
	    }
	    next_server++;
	    break;			/* found one! */
	}
	if (next_server < tgs_list) {
	    /* didn't find any */
	    retval = KRB5_NO_TKT_IN_RLM;
	    krb5_free_realm_tree(tgs_list);
	    goto out;
	}
	/* allocate storage for TGT pointers. */
	ret_tgts = (krb5_creds **)calloc(nservers+1, sizeof(krb5_creds));
	if (!ret_tgts) {
	    retval = ENOMEM;
	    krb5_free_realm_tree(tgs_list);
	    goto out;
	}
	*tgts = ret_tgts;
	for (nservers = 0; *next_server; next_server++, nservers++) {

	    krb5_data *tmpdata;

	    if (!valid_keytype(tgt.keyblock.keytype)) {
		retval = KRB5_PROG_KEYTYPE_NOSUPP;
		krb5_free_realm_tree(tgs_list);
		goto out;
	    }
	    /* now get the TGTs */
	    memset((char *)&tgtq, 0, sizeof(tgtq));
	    tgtq.times = tgt.times;
	    tgtq.client = tgt.client;

	    /* ask each realm for a tgt to the end */
	    if (retval = krb5_copy_data((*next_server)[0], &tmpdata)) {
		krb5_free_realm_tree(tgs_list);
		goto out;
	    }
	    krb5_free_data(final_server[0]);
	    final_server[0] = tmpdata;
	    tgtq.server = final_server;

	    tgtq.is_skey = FALSE;
	    tgtq.ticket_flags = tgt.ticket_flags;

	    etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
	    if (retval = krb5_get_cred_via_tgt(&tgt,
					       flags2options(tgtq.ticket_flags),
					       etype,
					       krb5_kdc_req_sumtype,
					       &tgtq)) {
		krb5_free_realm_tree(tgs_list);
		goto out;
	    }
	    /* make sure the returned ticket is somewhere in the remaining
	       list, but we can tolerate different expected issuing realms */
	    while (*++next_server &&
		   !krb5_principal_compare(&(next_server[0])[1],
					   &(tgtq.server[1])));
	    if (!next_server) {
		/* what we got back wasn't in the list! */
		krb5_free_realm_tree(tgs_list);
		retval = KRB5_KDCREP_MODIFIED; /* XXX? */
		goto out;
	    }
							   
	    /* save tgt in return array */
	    if (retval = krb5_copy_creds(&tgtq, &ret_tgts[nservers])) {
		krb5_free_realm_tree(tgs_list);
		goto out;
	    }
	    tgt = *ret_tgts[nservers];
	    tgtq.client = 0;
	    tgtq.server = 0;
	    krb5_free_cred_contents(&tgtq);
	}
	krb5_free_realm_tree(tgs_list);
    }
    /* got/finally have tgt! */
    if (!valid_keytype(tgt.keyblock.keytype)) {
	retval = KRB5_PROG_KEYTYPE_NOSUPP;
	goto out;
    }
    etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;

    retval = krb5_get_cred_via_tgt(&tgt,
				   flags2options(tgt.ticket_flags),
				   etype,
				   krb5_kdc_req_sumtype,
				   cred);
    krb5_free_cred_contents(&tgt);
out:
    krb5_free_principal(final_server);
    return retval;
}