Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / cc_retr.c
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 #define set(bits) (whichfields & bits)
34 #define flags_match(a,b) (((a) & (b)) == (a))
35
36 static int
37 times_match_exact(const krb5_ticket_times *t1, const krb5_ticket_times *t2)
38 {
39     return (t1->authtime == t2->authtime &&
40             t1->starttime == t2->starttime &&
41             t1->endtime == t2->endtime &&
42             t1->renew_till == t2->renew_till);
43 }
44
45 static krb5_boolean
46 times_match(const krb5_ticket_times *t1, const krb5_ticket_times *t2)
47 {
48     if (t1->renew_till) {
49         if (t1->renew_till > t2->renew_till)
50             return FALSE;               /* this one expires too late */
51     }
52     if (t1->endtime) {
53         if (t1->endtime > t2->endtime)
54             return FALSE;               /* this one expires too late */
55     }
56     /* only care about expiration on a times_match */
57     return TRUE;
58 }
59
60 static krb5_boolean
61 standard_fields_match(krb5_context context, const krb5_creds *mcreds, const krb5_creds *creds)
62 {
63     return (krb5_principal_compare(context, mcreds->client,creds->client)
64             && krb5_principal_compare(context, mcreds->server,creds->server));
65 }
66
67 /* only match the server name portion, not the server realm portion */
68
69 static krb5_boolean
70 srvname_match(krb5_context context, const krb5_creds *mcreds, const krb5_creds *creds)
71 {
72     krb5_boolean retval;
73     krb5_principal_data p1, p2;
74
75     retval = krb5_principal_compare(context, mcreds->client,creds->client);
76     if (retval != TRUE)
77         return retval;
78     /*
79      * Hack to ignore the server realm for the purposes of the compare.
80      */
81     p1 = *mcreds->server;
82     p2 = *creds->server;
83     p1.realm = p2.realm;
84     return krb5_principal_compare(context, &p1, &p2);
85 }
86
87 static krb5_boolean
88 authdata_match(krb5_authdata *const *mdata, krb5_authdata *const *data)
89 {
90     const krb5_authdata *mdatap, *datap;
91
92     if (mdata == data)
93         return TRUE;
94
95     if (mdata == NULL)
96         return *data == NULL;
97
98     if (data == NULL)
99         return *mdata == NULL;
100
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
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     return data_eq(*data1, *data2) ? TRUE : FALSE;
125 }
126
127 static int
128 pref (krb5_enctype my_ktype, int nktypes, krb5_enctype *ktypes)
129 {
130     int i;
131     for (i = 0; i < nktypes; i++)
132         if (my_ktype == ktypes[i])
133             return i;
134     return -1;
135 }
136
137 /*
138  * Effects:
139  * Searches the credentials cache for a credential matching mcreds,
140  * with the fields specified by whichfields.  If one if found, it is
141  * returned in creds, which should be freed by the caller with
142  * krb5_free_credentials().
143  *
144  * The fields are interpreted in the following way (all constants are
145  * preceded by KRB5_TC_).  MATCH_IS_SKEY requires the is_skey field to
146  * match exactly.  MATCH_TIMES requires the requested lifetime to be
147  * at least as great as that specified; MATCH_TIMES_EXACT requires the
148  * requested lifetime to be exactly that specified.  MATCH_FLAGS
149  * requires only the set bits in mcreds be set in creds;
150  * MATCH_FLAGS_EXACT requires all bits to match.
151  *
152  * Flag SUPPORTED_KTYPES means check all matching entries that have
153  * any supported enctype (according to tgs_enctypes) and return the one
154  * with the enctype listed earliest.  Return CC_NOT_KTYPE if a match
155  * is found *except* for having a supported enctype.
156  *
157  * Errors:
158  * system errors
159  * permission errors
160  * KRB5_CC_NOMEM
161  * KRB5_CC_NOT_KTYPE
162  */
163
164 krb5_boolean
165 krb5int_cc_creds_match_request(krb5_context context, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
166 {
167     if (((set(KRB5_TC_MATCH_SRV_NAMEONLY) &&
168           srvname_match(context, mcreds, creds)) ||
169          standard_fields_match(context, mcreds, creds))
170         &&
171         (! set(KRB5_TC_MATCH_IS_SKEY) ||
172          mcreds->is_skey == creds->is_skey)
173         &&
174         (! set(KRB5_TC_MATCH_FLAGS_EXACT) ||
175          mcreds->ticket_flags == creds->ticket_flags)
176         &&
177         (! set(KRB5_TC_MATCH_FLAGS) ||
178          flags_match(mcreds->ticket_flags, creds->ticket_flags))
179         &&
180         (! set(KRB5_TC_MATCH_TIMES_EXACT) ||
181          times_match_exact(&mcreds->times, &creds->times))
182         &&
183         (! set(KRB5_TC_MATCH_TIMES) ||
184          times_match(&mcreds->times, &creds->times))
185         &&
186         ( ! set(KRB5_TC_MATCH_AUTHDATA) ||
187           authdata_match(mcreds->authdata, creds->authdata))
188         &&
189         (! set(KRB5_TC_MATCH_2ND_TKT) ||
190          data_match (&mcreds->second_ticket, &creds->second_ticket))
191         &&
192         ((! set(KRB5_TC_MATCH_KTYPE))||
193          (mcreds->keyblock.enctype == creds->keyblock.enctype)))
194         return TRUE;
195     return FALSE;
196 }
197
198 static krb5_error_code
199 krb5_cc_retrieve_cred_seq (krb5_context context, krb5_ccache id,
200                            krb5_flags whichfields, krb5_creds *mcreds,
201                            krb5_creds *creds, int nktypes, krb5_enctype *ktypes)
202 {
203     /* This function could be considerably faster if it kept indexing */
204     /* information.. sounds like a "next version" idea to me. :-) */
205
206     krb5_cc_cursor cursor;
207     krb5_error_code kret;
208     krb5_error_code nomatch_err = KRB5_CC_NOTFOUND;
209     struct {
210         krb5_creds creds;
211         int pref;
212     } fetched, best;
213     int have_creds = 0;
214     krb5_flags oflags = 0;
215 #define fetchcreds (fetched.creds)
216
217     kret = krb5_cc_start_seq_get(context, id, &cursor);
218     if (kret != KRB5_OK)
219         return kret;
220
221     while (krb5_cc_next_cred(context, id, &cursor, &fetchcreds) == KRB5_OK) {
222         if (krb5int_cc_creds_match_request(context, whichfields, mcreds, &fetchcreds))
223         {
224             if (ktypes) {
225                 fetched.pref = pref (fetchcreds.keyblock.enctype,
226                                      nktypes, ktypes);
227                 if (fetched.pref < 0)
228                     nomatch_err = KRB5_CC_NOT_KTYPE;
229                 else if (!have_creds || fetched.pref < best.pref) {
230                     if (have_creds)
231                         krb5_free_cred_contents (context, &best.creds);
232                     else
233                         have_creds = 1;
234                     best = fetched;
235                     continue;
236                 }
237             } else {
238                 krb5_cc_end_seq_get(context, id, &cursor);
239                 *creds = fetchcreds;
240                 return KRB5_OK;
241             }
242         }
243
244         /* This one doesn't match */
245         krb5_free_cred_contents(context, &fetchcreds);
246     }
247
248     /* If we get here, a match wasn't found */
249     krb5_cc_end_seq_get(context, id, &cursor);
250     if (have_creds) {
251         *creds = best.creds;
252         return KRB5_OK;
253     } else
254         return nomatch_err;
255 }
256
257 krb5_error_code
258 k5_cc_retrieve_cred_default(krb5_context context, krb5_ccache id,
259                             krb5_flags flags, krb5_creds *mcreds,
260                             krb5_creds *creds)
261 {
262     krb5_enctype *ktypes;
263     int nktypes;
264     krb5_error_code ret;
265
266     if (flags & KRB5_TC_SUPPORTED_KTYPES) {
267         ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes);
268         if (ret)
269             return ret;
270         nktypes = k5_count_etypes (ktypes);
271
272         ret = krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds,
273                                          nktypes, ktypes);
274         free (ktypes);
275         return ret;
276     } else {
277         return krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds,
278                                           0, 0);
279     }
280 }