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 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 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 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 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 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 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 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 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 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