xref: /freebsd/crypto/krb5/src/lib/krb5/ccache/cc_retr.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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