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