1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_retr.c */
3 /*
4 * Copyright 1990,1991,1999,2007,2008 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "k5-int.h"
28 #include "cc-int.h"
29 #include "../krb/int-proto.h"
30
31 #define KRB5_OK 0
32
33 static int
times_match_exact(const krb5_ticket_times * t1,const krb5_ticket_times * t2)34 times_match_exact(const krb5_ticket_times *t1, const krb5_ticket_times *t2)
35 {
36 return (t1->authtime == t2->authtime &&
37 t1->starttime == t2->starttime &&
38 t1->endtime == t2->endtime &&
39 t1->renew_till == t2->renew_till);
40 }
41
42 static krb5_boolean
times_match(const krb5_ticket_times * t1,const krb5_ticket_times * t2)43 times_match(const krb5_ticket_times *t1, const krb5_ticket_times *t2)
44 {
45 if (t1->renew_till) {
46 if (ts_after(t1->renew_till, t2->renew_till))
47 return FALSE; /* this one expires too late */
48 }
49 if (t1->endtime) {
50 if (ts_after(t1->endtime, t2->endtime))
51 return FALSE; /* this one expires too late */
52 }
53 /* only care about expiration on a times_match */
54 return TRUE;
55 }
56
57 static krb5_boolean
princs_match(krb5_context context,krb5_flags whichfields,const krb5_creds * mcreds,const krb5_creds * creds)58 princs_match(krb5_context context, krb5_flags whichfields,
59 const krb5_creds *mcreds, const krb5_creds *creds)
60 {
61 if (mcreds->client != NULL &&
62 !krb5_principal_compare(context, mcreds->client, creds->client))
63 return FALSE;
64 if (mcreds->server == NULL)
65 return TRUE;
66 if (whichfields & KRB5_TC_MATCH_SRV_NAMEONLY) {
67 return krb5_principal_compare_any_realm(context, mcreds->server,
68 creds->server);
69 } else {
70 return krb5_principal_compare(context, mcreds->server, creds->server);
71 }
72 }
73
74 static krb5_boolean
authdata_match(krb5_authdata * const * mdata,krb5_authdata * const * data)75 authdata_match(krb5_authdata *const *mdata, krb5_authdata *const *data)
76 {
77 const krb5_authdata *mdatap, *datap;
78
79 if (mdata == data)
80 return TRUE;
81
82 if (mdata == NULL)
83 return *data == NULL;
84
85 if (data == NULL)
86 return *mdata == NULL;
87
88 while ((mdatap = *mdata) && (datap = *data)) {
89 if ((mdatap->ad_type != datap->ad_type) ||
90 (mdatap->length != datap->length) ||
91 (memcmp ((char *)mdatap->contents,
92 (char *)datap->contents, (unsigned) mdatap->length) != 0))
93 return FALSE;
94 mdata++;
95 data++;
96 }
97 return (*mdata == NULL) && (*data == NULL);
98 }
99
100 static krb5_boolean
data_match(const krb5_data * data1,const krb5_data * data2)101 data_match(const krb5_data *data1, const krb5_data *data2)
102 {
103 if (!data1) {
104 if (!data2)
105 return TRUE;
106 else
107 return FALSE;
108 }
109 if (!data2) return FALSE;
110
111 return data_eq(*data1, *data2) ? TRUE : FALSE;
112 }
113
114 static int
pref(krb5_enctype my_ktype,int nktypes,krb5_enctype * ktypes)115 pref (krb5_enctype my_ktype, int nktypes, krb5_enctype *ktypes)
116 {
117 int i;
118 for (i = 0; i < nktypes; i++)
119 if (my_ktype == ktypes[i])
120 return i;
121 return -1;
122 }
123
124 /*
125 * Effects:
126 * Searches the credentials cache for a credential matching mcreds,
127 * with the fields specified by whichfields. If one if found, it is
128 * returned in creds, which should be freed by the caller with
129 * krb5_free_credentials().
130 *
131 * The fields are interpreted in the following way (all constants are
132 * preceded by KRB5_TC_). MATCH_IS_SKEY requires the is_skey field to
133 * match exactly. MATCH_TIMES requires the requested lifetime to be
134 * at least as great as that specified; MATCH_TIMES_EXACT requires the
135 * requested lifetime to be exactly that specified. MATCH_FLAGS
136 * requires only the set bits in mcreds be set in creds;
137 * MATCH_FLAGS_EXACT requires all bits to match.
138 *
139 * Flag SUPPORTED_KTYPES means check all matching entries that have
140 * any supported enctype (according to tgs_enctypes) and return the one
141 * with the enctype listed earliest. Return CC_NOT_KTYPE if a match
142 * is found *except* for having a supported enctype.
143 *
144 * Errors:
145 * system errors
146 * permission errors
147 * KRB5_CC_NOMEM
148 * KRB5_CC_NOT_KTYPE
149 */
150
151 krb5_boolean
krb5int_cc_creds_match_request(krb5_context context,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)152 krb5int_cc_creds_match_request(krb5_context context, krb5_flags whichfields,
153 krb5_creds *mcreds, krb5_creds *creds)
154 {
155 krb5_boolean is_skey;
156
157 if (!princs_match(context, whichfields, mcreds, creds))
158 return FALSE;
159
160 /* Only match a user-to-user credential if explicitly asked for, since the
161 * ticket won't work as a regular service ticket. */
162 is_skey = (whichfields & KRB5_TC_MATCH_IS_SKEY) ? mcreds->is_skey : FALSE;
163 if (creds->is_skey != is_skey)
164 return FALSE;
165
166 if ((whichfields & KRB5_TC_MATCH_FLAGS_EXACT) &&
167 mcreds->ticket_flags != creds->ticket_flags)
168 return FALSE;
169 if ((whichfields & KRB5_TC_MATCH_FLAGS) &&
170 (creds->ticket_flags & mcreds->ticket_flags) != mcreds->ticket_flags)
171 return FALSE;
172
173 if ((whichfields & KRB5_TC_MATCH_TIMES_EXACT) &&
174 !times_match_exact(&mcreds->times, &creds->times))
175 return FALSE;
176 if ((whichfields & KRB5_TC_MATCH_TIMES) &&
177 !times_match(&mcreds->times, &creds->times))
178 return FALSE;
179
180 if ((whichfields & KRB5_TC_MATCH_AUTHDATA) &&
181 !authdata_match(mcreds->authdata, creds->authdata))
182 return FALSE;
183
184 if ((whichfields & KRB5_TC_MATCH_2ND_TKT) &&
185 !data_match(&mcreds->second_ticket, &creds->second_ticket))
186 return FALSE;
187
188 if ((whichfields & KRB5_TC_MATCH_KTYPE) &&
189 mcreds->keyblock.enctype != creds->keyblock.enctype)
190 return FALSE;
191
192 return TRUE;
193 }
194
195 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)196 krb5_cc_retrieve_cred_seq (krb5_context context, krb5_ccache id,
197 krb5_flags whichfields, krb5_creds *mcreds,
198 krb5_creds *creds, int nktypes, krb5_enctype *ktypes)
199 {
200 /* This function could be considerably faster if it kept indexing */
201 /* information.. sounds like a "next version" idea to me. :-) */
202
203 krb5_cc_cursor cursor;
204 krb5_error_code kret;
205 krb5_error_code nomatch_err = KRB5_CC_NOTFOUND;
206 struct {
207 krb5_creds creds;
208 int pref;
209 } fetched, best;
210 int have_creds = 0;
211 #define fetchcreds (fetched.creds)
212
213 kret = krb5_cc_start_seq_get(context, id, &cursor);
214 if (kret != KRB5_OK)
215 return kret;
216
217 while (krb5_cc_next_cred(context, id, &cursor, &fetchcreds) == KRB5_OK) {
218 if (krb5int_cc_creds_match_request(context, whichfields, mcreds, &fetchcreds))
219 {
220 if (ktypes) {
221 fetched.pref = pref (fetchcreds.keyblock.enctype,
222 nktypes, ktypes);
223 if (fetched.pref < 0)
224 nomatch_err = KRB5_CC_NOT_KTYPE;
225 else if (!have_creds || fetched.pref < best.pref) {
226 if (have_creds)
227 krb5_free_cred_contents (context, &best.creds);
228 else
229 have_creds = 1;
230 best = fetched;
231 continue;
232 }
233 } else {
234 krb5_cc_end_seq_get(context, id, &cursor);
235 *creds = fetchcreds;
236 return KRB5_OK;
237 }
238 }
239
240 /* This one doesn't match */
241 krb5_free_cred_contents(context, &fetchcreds);
242 }
243
244 /* If we get here, a match wasn't found */
245 krb5_cc_end_seq_get(context, id, &cursor);
246 if (have_creds) {
247 *creds = best.creds;
248 return KRB5_OK;
249 } else
250 return nomatch_err;
251 }
252
253 krb5_error_code
k5_cc_retrieve_cred_default(krb5_context context,krb5_ccache id,krb5_flags flags,krb5_creds * mcreds,krb5_creds * creds)254 k5_cc_retrieve_cred_default(krb5_context context, krb5_ccache id,
255 krb5_flags flags, krb5_creds *mcreds,
256 krb5_creds *creds)
257 {
258 krb5_enctype *ktypes;
259 int nktypes;
260 krb5_error_code ret;
261
262 if (flags & KRB5_TC_SUPPORTED_KTYPES) {
263 ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes);
264 if (ret)
265 return ret;
266 nktypes = k5_count_etypes (ktypes);
267
268 ret = krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds,
269 nktypes, ktypes);
270 free (ktypes);
271 return ret;
272 } else {
273 return krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds,
274 0, 0);
275 }
276 }
277