1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * lib/krb5/ccache/cc_retr.c
9 *
10 * Copyright 1990,1991,1999 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
31 *
32 *
33 */
34
35 #include "k5-int.h"
36 #include "cc-int.h"
37
38 #define KRB5_OK 0
39
40 #define set(bits) (whichfields & bits)
41 #define flags_match(a,b) (((a) & (b)) == (a))
42 #define times_match_exact(t1,t2) (memcmp((char *)(t1), (char *)(t2), sizeof(*(t1))) == 0)
43
44 static krb5_boolean
times_match(const krb5_ticket_times * t1,const krb5_ticket_times * t2)45 times_match(const krb5_ticket_times *t1, const krb5_ticket_times *t2)
46 {
47 if (t1->renew_till) {
48 if (t1->renew_till > t2->renew_till)
49 return FALSE; /* this one expires too late */
50 }
51 if (t1->endtime) {
52 if (t1->endtime > t2->endtime)
53 return FALSE; /* this one expires too late */
54 }
55 /* only care about expiration on a times_match */
56 return TRUE;
57 }
58
59 static krb5_boolean
standard_fields_match(krb5_context context,const krb5_creds * mcreds,const krb5_creds * creds)60 standard_fields_match(krb5_context context, const krb5_creds *mcreds, const krb5_creds *creds)
61 {
62 return (krb5_principal_compare(context, mcreds->client,creds->client)
63 && krb5_principal_compare(context, mcreds->server,creds->server));
64 }
65
66 /* only match the server name portion, not the server realm portion */
67
68 static krb5_boolean
srvname_match(krb5_context context,const krb5_creds * mcreds,const krb5_creds * creds)69 srvname_match(krb5_context context, const krb5_creds *mcreds, const krb5_creds *creds)
70 {
71 krb5_boolean retval;
72 krb5_principal_data p1, p2;
73
74 retval = krb5_principal_compare(context, mcreds->client,creds->client);
75 if (retval != TRUE)
76 return retval;
77 /*
78 * Hack to ignore the server realm for the purposes of the compare.
79 */
80 p1 = *mcreds->server;
81 p2 = *creds->server;
82 p1.realm = p2.realm;
83 return krb5_principal_compare(context, &p1, &p2);
84 }
85
86 static krb5_boolean
authdata_match(krb5_authdata * const * mdata,krb5_authdata * const * data)87 authdata_match(krb5_authdata *const *mdata, krb5_authdata *const *data)
88 {
89 const krb5_authdata *mdatap, *datap;
90
91 if (mdata == data)
92 return TRUE;
93
94 if (mdata == NULL)
95 return *data == NULL;
96
97 if (data == NULL)
98 return *mdata == NULL;
99
100 /*LINTED*/
101 while ((mdatap = *mdata) && (datap = *data)) {
102 if ((mdatap->ad_type != datap->ad_type) ||
103 (mdatap->length != datap->length) ||
104 (memcmp ((char *)mdatap->contents,
105 (char *)datap->contents, (unsigned) mdatap->length) != 0))
106 return FALSE;
107 mdata++;
108 data++;
109 }
110 return (*mdata == NULL) && (*data == NULL);
111 }
112
113 static krb5_boolean
data_match(const krb5_data * data1,const krb5_data * data2)114 data_match(const krb5_data *data1, const krb5_data *data2)
115 {
116 if (!data1) {
117 if (!data2)
118 return TRUE;
119 else
120 return FALSE;
121 }
122 if (!data2) return FALSE;
123
124 if (data1->length != data2->length)
125 return FALSE;
126 else
127 return memcmp(data1->data, data2->data, (unsigned) data1->length)
128 ? FALSE : TRUE;
129 }
130
131 static int
pref(krb5_enctype my_ktype,int nktypes,krb5_enctype * ktypes)132 pref (krb5_enctype my_ktype, int nktypes, krb5_enctype *ktypes)
133 {
134 int i;
135 for (i = 0; i < nktypes; i++)
136 if (my_ktype == ktypes[i])
137 return i;
138 return -1;
139 }
140
141 /*
142 * Effects:
143 * Searches the credentials cache for a credential matching mcreds,
144 * with the fields specified by whichfields. If one if found, it is
145 * returned in creds, which should be freed by the caller with
146 * krb5_free_credentials().
147 *
148 * The fields are interpreted in the following way (all constants are
149 * preceded by KRB5_TC_). MATCH_IS_SKEY requires the is_skey field to
150 * match exactly. MATCH_TIMES requires the requested lifetime to be
151 * at least as great as that specified; MATCH_TIMES_EXACT requires the
152 * requested lifetime to be exactly that specified. MATCH_FLAGS
153 * requires only the set bits in mcreds be set in creds;
154 * MATCH_FLAGS_EXACT requires all bits to match.
155 *
156 * Flag SUPPORTED_KTYPES means check all matching entries that have
157 * any supported enctype (according to tgs_enctypes) and return the one
158 * with the enctype listed earliest. Return CC_NOT_KTYPE if a match
159 * is found *except* for having a supported enctype.
160 *
161 * Errors:
162 * system errors
163 * permission errors
164 * KRB5_CC_NOMEM
165 * KRB5_CC_NOT_KTYPE
166 */
167
168 krb5_boolean
krb5int_cc_creds_match_request(krb5_context context,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)169 krb5int_cc_creds_match_request(krb5_context context, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
170 {
171 if (((set(KRB5_TC_MATCH_SRV_NAMEONLY) &&
172 srvname_match(context, mcreds, creds)) ||
173 standard_fields_match(context, mcreds, creds))
174 &&
175 (! set(KRB5_TC_MATCH_IS_SKEY) ||
176 mcreds->is_skey == creds->is_skey)
177 &&
178 (! set(KRB5_TC_MATCH_FLAGS_EXACT) ||
179 mcreds->ticket_flags == creds->ticket_flags)
180 &&
181 (! set(KRB5_TC_MATCH_FLAGS) ||
182 flags_match(mcreds->ticket_flags, creds->ticket_flags))
183 &&
184 (! set(KRB5_TC_MATCH_TIMES_EXACT) ||
185 times_match_exact(&mcreds->times, &creds->times))
186 &&
187 (! set(KRB5_TC_MATCH_TIMES) ||
188 times_match(&mcreds->times, &creds->times))
189 &&
190 ( ! set(KRB5_TC_MATCH_AUTHDATA) ||
191 authdata_match(mcreds->authdata, creds->authdata))
192 &&
193 (! set(KRB5_TC_MATCH_2ND_TKT) ||
194 data_match (&mcreds->second_ticket, &creds->second_ticket))
195 &&
196 ((! set(KRB5_TC_MATCH_KTYPE))||
197 (mcreds->keyblock.enctype == creds->keyblock.enctype)))
198 return TRUE;
199 return FALSE;
200 }
201
202 static krb5_error_code
krb5_cc_retrieve_cred_seq(krb5_context context,krb5_ccache id,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds,int nktypes,krb5_enctype * ktypes)203 krb5_cc_retrieve_cred_seq (krb5_context context, krb5_ccache id,
204 krb5_flags whichfields, krb5_creds *mcreds,
205 krb5_creds *creds, int nktypes, krb5_enctype *ktypes)
206 {
207 /* This function could be considerably faster if it kept indexing */
208 /* information.. sounds like a "next version" idea to me. :-) */
209
210 krb5_cc_cursor cursor;
211 krb5_error_code kret;
212 krb5_error_code nomatch_err = KRB5_CC_NOTFOUND;
213 struct {
214 krb5_creds creds;
215 int pref;
216 } fetched, best;
217 int have_creds = 0;
218 krb5_flags oflags = 0;
219 #define fetchcreds (fetched.creds)
220
221 /* Solaris Kerberos */
222 memset(&best, 0, sizeof (best));
223 memset(&fetched, 0, sizeof (fetched));
224
225 kret = krb5_cc_get_flags(context, id, &oflags);
226 if (kret != KRB5_OK)
227 return kret;
228 if (oflags & KRB5_TC_OPENCLOSE)
229 (void) krb5_cc_set_flags(context, id, oflags & ~KRB5_TC_OPENCLOSE);
230 kret = krb5_cc_start_seq_get(context, id, &cursor);
231 if (kret != KRB5_OK) {
232 if (oflags & KRB5_TC_OPENCLOSE)
233 krb5_cc_set_flags(context, id, oflags);
234 return kret;
235 }
236
237 while ((kret = krb5_cc_next_cred(context, id, &cursor, &fetchcreds)) == KRB5_OK) {
238 if (krb5int_cc_creds_match_request(context, whichfields, mcreds, &fetchcreds))
239 {
240 if (ktypes) {
241 fetched.pref = pref (fetchcreds.keyblock.enctype,
242 nktypes, ktypes);
243 if (fetched.pref < 0)
244 nomatch_err = KRB5_CC_NOT_KTYPE;
245 else if (!have_creds || fetched.pref < best.pref) {
246 if (have_creds)
247 krb5_free_cred_contents (context, &best.creds);
248 else
249 have_creds = 1;
250 best = fetched;
251 continue;
252 }
253 } else {
254 krb5_cc_end_seq_get(context, id, &cursor);
255 *creds = fetchcreds;
256 /* Solaris Kerberos */
257 creds->keyblock.hKey = CK_INVALID_HANDLE;
258 if (oflags & KRB5_TC_OPENCLOSE)
259 krb5_cc_set_flags(context, id, oflags);
260 return KRB5_OK;
261 }
262 }
263
264 /* This one doesn't match */
265 krb5_free_cred_contents(context, &fetchcreds);
266 }
267
268 /* If we get here, a match wasn't found */
269 krb5_cc_end_seq_get(context, id, &cursor);
270 if (oflags & KRB5_TC_OPENCLOSE)
271 krb5_cc_set_flags(context, id, oflags);
272 if (have_creds) {
273 *creds = best.creds;
274 /* Solaris Kerberos */
275 creds->keyblock.hKey = CK_INVALID_HANDLE;
276 return KRB5_OK;
277 } else
278 return nomatch_err;
279 }
280
281 krb5_error_code KRB5_CALLCONV
krb5_cc_retrieve_cred_default(krb5_context context,krb5_ccache id,krb5_flags flags,krb5_creds * mcreds,krb5_creds * creds)282 krb5_cc_retrieve_cred_default (krb5_context context, krb5_ccache id, krb5_flags flags, krb5_creds *mcreds, krb5_creds *creds)
283 {
284 krb5_enctype *ktypes;
285 int nktypes;
286 krb5_error_code ret;
287
288 if (flags & KRB5_TC_SUPPORTED_KTYPES) {
289 ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes);
290 if (ret)
291 return ret;
292 nktypes = 0;
293 while (ktypes[nktypes])
294 nktypes++;
295
296 ret = krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds,
297 nktypes, ktypes);
298 free (ktypes);
299 return ret;
300 } else {
301 /* Solaris Kerberos */
302 return krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds,
303 0, (krb5_enctype *)NULL);
304 }
305 }
306
307 /* The following function duplicates some of the functionality above and */
308 /* should probably be merged with it at some point. It is used by the */
309 /* CCAPI krb5_cc_remove to figure out if the opaque credentials object */
310 /* returned by the CCAPI is the same creds as the caller passed in. */
311 /* Unlike the code above it requires that all structures be identical. */
312
313 krb5_boolean KRB5_CALLCONV
krb5_creds_compare(krb5_context in_context,krb5_creds * in_creds,krb5_creds * in_compare_creds)314 krb5_creds_compare (krb5_context in_context,
315 krb5_creds *in_creds,
316 krb5_creds *in_compare_creds)
317 {
318 /* Set to 0 when we hit the first mismatch and then fall through */
319 int equal = 1;
320
321 if (equal) {
322 equal = krb5_principal_compare (in_context, in_creds->client,
323 in_compare_creds->client);
324 }
325
326 if (equal) {
327 equal = krb5_principal_compare (in_context, in_creds->server,
328 in_compare_creds->server);
329 }
330
331 if (equal) {
332 equal = (in_creds->keyblock.enctype == in_compare_creds->keyblock.enctype &&
333 in_creds->keyblock.length == in_compare_creds->keyblock.length &&
334 (!in_creds->keyblock.length ||
335 !memcmp (in_creds->keyblock.contents, in_compare_creds->keyblock.contents,
336 in_creds->keyblock.length)));
337 }
338
339 if (equal) {
340 equal = (in_creds->times.authtime == in_compare_creds->times.authtime &&
341 in_creds->times.starttime == in_compare_creds->times.starttime &&
342 in_creds->times.endtime == in_compare_creds->times.endtime &&
343 in_creds->times.renew_till == in_compare_creds->times.renew_till);
344 }
345
346 if (equal) {
347 equal = (in_creds->is_skey == in_compare_creds->is_skey);
348 }
349
350 if (equal) {
351 equal = (in_creds->ticket_flags == in_compare_creds->ticket_flags);
352 }
353
354 if (equal) {
355 krb5_address **addresses = in_creds->addresses;
356 krb5_address **compare_addresses = in_compare_creds->addresses;
357 unsigned int i;
358
359 if (addresses && compare_addresses) {
360 for (i = 0; (equal && addresses[i] && compare_addresses[i]); i++) {
361 equal = krb5_address_compare (in_context, addresses[i],
362 compare_addresses[i]);
363 }
364 if (equal) { equal = (!addresses[i] && !compare_addresses[i]); }
365 } else {
366 if (equal) { equal = (!addresses && !compare_addresses); }
367 }
368 }
369
370 if (equal) {
371 equal = (in_creds->ticket.length == in_compare_creds->ticket.length &&
372 (!in_creds->ticket.length ||
373 !memcmp (in_creds->ticket.data, in_compare_creds->ticket.data,
374 in_creds->ticket.length)));
375 }
376
377 if (equal) {
378 equal = (in_creds->second_ticket.length == in_compare_creds->second_ticket.length &&
379 (!in_creds->second_ticket.length ||
380 !memcmp (in_creds->second_ticket.data, in_compare_creds->second_ticket.data,
381 in_creds->second_ticket.length)));
382 }
383
384 if (equal) {
385 krb5_authdata **authdata = in_creds->authdata;
386 krb5_authdata **compare_authdata = in_compare_creds->authdata;
387 unsigned int i;
388
389 if (authdata && compare_authdata) {
390 for (i = 0; (equal && authdata[i] && compare_authdata[i]); i++) {
391 equal = (authdata[i]->ad_type == compare_authdata[i]->ad_type &&
392 authdata[i]->length == compare_authdata[i]->length &&
393 (!authdata[i]->length ||
394 !memcmp (authdata[i]->contents, compare_authdata[i]->contents,
395 authdata[i]->length)));
396 }
397 if (equal) { equal = (!authdata[i] && !compare_authdata[i]); }
398 } else {
399 if (equal) { equal = (!authdata && !compare_authdata); }
400 }
401 }
402
403 return equal;
404 }
405