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