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