1 /* 2 * Copyright 2005 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 #include "cc-int.h" 38 39 #define KRB5_OK 0 40 41 #define set(bits) (whichfields & bits) 42 #define flags_match(a,b) (((a) & (b)) == (a)) 43 #define times_match_exact(t1,t2) (memcmp((char *)(t1), (char *)(t2), sizeof(*(t1))) == 0) 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 /*LINTED*/ 102 while ((mdatap = *mdata) && (datap = *data)) { 103 if ((mdatap->ad_type != datap->ad_type) || 104 (mdatap->length != datap->length) || 105 (memcmp ((char *)mdatap->contents, 106 (char *)datap->contents, (unsigned) mdatap->length) != 0)) 107 return FALSE; 108 mdata++; 109 data++; 110 } 111 return (*mdata == NULL) && (*data == NULL); 112 } 113 114 static krb5_boolean 115 data_match(const krb5_data *data1, const krb5_data *data2) 116 { 117 if (!data1) { 118 if (!data2) 119 return TRUE; 120 else 121 return FALSE; 122 } 123 if (!data2) return FALSE; 124 125 if (data1->length != data2->length) 126 return FALSE; 127 else 128 return memcmp(data1->data, data2->data, (unsigned) data1->length) 129 ? FALSE : TRUE; 130 } 131 132 static int 133 pref (krb5_enctype my_ktype, int nktypes, krb5_enctype *ktypes) 134 { 135 int i; 136 for (i = 0; i < nktypes; i++) 137 if (my_ktype == ktypes[i]) 138 return i; 139 return -1; 140 } 141 142 /* 143 * Effects: 144 * Searches the credentials cache for a credential matching mcreds, 145 * with the fields specified by whichfields. If one if found, it is 146 * returned in creds, which should be freed by the caller with 147 * krb5_free_credentials(). 148 * 149 * The fields are interpreted in the following way (all constants are 150 * preceded by KRB5_TC_). MATCH_IS_SKEY requires the is_skey field to 151 * match exactly. MATCH_TIMES requires the requested lifetime to be 152 * at least as great as that specified; MATCH_TIMES_EXACT requires the 153 * requested lifetime to be exactly that specified. MATCH_FLAGS 154 * requires only the set bits in mcreds be set in creds; 155 * MATCH_FLAGS_EXACT requires all bits to match. 156 * 157 * Flag SUPPORTED_KTYPES means check all matching entries that have 158 * any supported enctype (according to tgs_enctypes) and return the one 159 * with the enctype listed earliest. Return CC_NOT_KTYPE if a match 160 * is found *except* for having a supported enctype. 161 * 162 * Errors: 163 * system errors 164 * permission errors 165 * KRB5_CC_NOMEM 166 * KRB5_CC_NOT_KTYPE 167 */ 168 169 krb5_boolean 170 krb5int_cc_creds_match_request(krb5_context context, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds) 171 { 172 if (((set(KRB5_TC_MATCH_SRV_NAMEONLY) && 173 srvname_match(context, mcreds, creds)) || 174 standard_fields_match(context, mcreds, creds)) 175 && 176 (! set(KRB5_TC_MATCH_IS_SKEY) || 177 mcreds->is_skey == creds->is_skey) 178 && 179 (! set(KRB5_TC_MATCH_FLAGS_EXACT) || 180 mcreds->ticket_flags == creds->ticket_flags) 181 && 182 (! set(KRB5_TC_MATCH_FLAGS) || 183 flags_match(mcreds->ticket_flags, creds->ticket_flags)) 184 && 185 (! set(KRB5_TC_MATCH_TIMES_EXACT) || 186 times_match_exact(&mcreds->times, &creds->times)) 187 && 188 (! set(KRB5_TC_MATCH_TIMES) || 189 times_match(&mcreds->times, &creds->times)) 190 && 191 ( ! set(KRB5_TC_MATCH_AUTHDATA) || 192 authdata_match(mcreds->authdata, creds->authdata)) 193 && 194 (! set(KRB5_TC_MATCH_2ND_TKT) || 195 data_match (&mcreds->second_ticket, &creds->second_ticket)) 196 && 197 ((! set(KRB5_TC_MATCH_KTYPE))|| 198 (mcreds->keyblock.enctype == creds->keyblock.enctype))) 199 return TRUE; 200 return FALSE; 201 } 202 203 static krb5_error_code 204 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) 205 { 206 /* This function could be considerably faster if it kept indexing */ 207 /* information.. sounds like a "next version" idea to me. :-) */ 208 209 krb5_cc_cursor cursor; 210 krb5_error_code kret; 211 krb5_error_code nomatch_err = KRB5_CC_NOTFOUND; 212 struct { 213 krb5_creds creds; 214 int pref; 215 } fetched, best; 216 int have_creds = 0; 217 #define fetchcreds (fetched.creds) 218 219 memset(&best, 0, sizeof (best)); 220 memset(&fetched, 0, sizeof (fetched)); 221 222 kret = krb5_cc_start_seq_get(context, id, &cursor); 223 if (kret != KRB5_OK) 224 return kret; 225 226 while ((kret = krb5_cc_next_cred(context, id, &cursor, &fetchcreds)) == KRB5_OK) { 227 if (krb5int_cc_creds_match_request(context, whichfields, mcreds, &fetchcreds)) 228 { 229 if (ktypes) { 230 fetched.pref = pref (fetchcreds.keyblock.enctype, 231 nktypes, ktypes); 232 if (fetched.pref < 0) 233 nomatch_err = KRB5_CC_NOT_KTYPE; 234 else if (!have_creds || fetched.pref < best.pref) { 235 if (have_creds) 236 krb5_free_cred_contents (context, &best.creds); 237 else 238 have_creds = 1; 239 best = fetched; 240 continue; 241 } 242 } else { 243 (void) krb5_cc_end_seq_get(context, id, &cursor); 244 *creds = fetchcreds; 245 creds->keyblock.hKey = CK_INVALID_HANDLE; 246 return KRB5_OK; 247 } 248 } 249 250 /* This one doesn't match */ 251 krb5_free_cred_contents(context, &fetchcreds); 252 } 253 254 /* If we get here, a match wasn't found */ 255 (void) krb5_cc_end_seq_get(context, id, &cursor); 256 if (have_creds) { 257 *creds = best.creds; 258 creds->keyblock.hKey = CK_INVALID_HANDLE; 259 return KRB5_OK; 260 } else 261 return nomatch_err; 262 } 263 264 krb5_error_code KRB5_CALLCONV 265 krb5_cc_retrieve_cred_default (krb5_context context, krb5_ccache id, krb5_flags flags, krb5_creds *mcreds, krb5_creds *creds) 266 { 267 krb5_enctype *ktypes; 268 int nktypes; 269 krb5_error_code ret; 270 271 if (flags & KRB5_TC_SUPPORTED_KTYPES) { 272 ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes); 273 if (ret) 274 return ret; 275 nktypes = 0; 276 while (ktypes[nktypes]) 277 nktypes++; 278 279 ret = krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds, 280 nktypes, ktypes); 281 free (ktypes); 282 return ret; 283 } else { 284 return krb5_cc_retrieve_cred_seq (context, id, flags, mcreds, creds, 285 0, (krb5_enctype *)NULL); 286 } 287 } 288