1 /* 2 * Copyright (c) 1997-2001 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 36 RCSID("$Id: cache.c,v 1.47 2001/05/14 06:14:45 assar Exp $"); 37 38 /* 39 * Add a new ccache type with operations `ops', overwriting any 40 * existing one if `override'. 41 * Return an error code or 0. 42 */ 43 44 krb5_error_code 45 krb5_cc_register(krb5_context context, 46 const krb5_cc_ops *ops, 47 krb5_boolean override) 48 { 49 char *prefix_copy; 50 int i; 51 52 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 53 if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) { 54 if(override) 55 free(context->cc_ops[i].prefix); 56 else { 57 krb5_set_error_string(context, 58 "ccache type %s already exists", 59 ops->prefix); 60 return KRB5_CC_TYPE_EXISTS; 61 } 62 } 63 } 64 prefix_copy = strdup(ops->prefix); 65 if (prefix_copy == NULL) { 66 krb5_set_error_string(context, "malloc: out of memory"); 67 return KRB5_CC_NOMEM; 68 } 69 if(i == context->num_cc_ops) { 70 krb5_cc_ops *o = realloc(context->cc_ops, 71 (context->num_cc_ops + 1) * 72 sizeof(*context->cc_ops)); 73 if(o == NULL) { 74 krb5_set_error_string(context, "malloc: out of memory"); 75 free(prefix_copy); 76 return KRB5_CC_NOMEM; 77 } 78 context->num_cc_ops++; 79 context->cc_ops = o; 80 memset(context->cc_ops + i, 0, 81 (context->num_cc_ops - i) * sizeof(*context->cc_ops)); 82 } 83 memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i])); 84 context->cc_ops[i].prefix = prefix_copy; 85 return 0; 86 } 87 88 /* 89 * Allocate memory for a new ccache in `id' with operations `ops' 90 * and name `residual'. 91 * Return 0 or an error code. 92 */ 93 94 static krb5_error_code 95 allocate_ccache (krb5_context context, 96 const krb5_cc_ops *ops, 97 const char *residual, 98 krb5_ccache *id) 99 { 100 krb5_error_code ret; 101 krb5_ccache p; 102 103 p = malloc(sizeof(*p)); 104 if(p == NULL) { 105 krb5_set_error_string(context, "malloc: out of memory"); 106 return KRB5_CC_NOMEM; 107 } 108 p->ops = ops; 109 *id = p; 110 ret = p->ops->resolve(context, id, residual); 111 if(ret) 112 free(p); 113 return ret; 114 } 115 116 /* 117 * Find and allocate a ccache in `id' from the specification in `residual'. 118 * If the ccache name doesn't contain any colon, interpret it as a file name. 119 * Return 0 or an error code. 120 */ 121 122 krb5_error_code 123 krb5_cc_resolve(krb5_context context, 124 const char *name, 125 krb5_ccache *id) 126 { 127 int i; 128 129 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 130 size_t prefix_len = strlen(context->cc_ops[i].prefix); 131 132 if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0 133 && name[prefix_len] == ':') { 134 return allocate_ccache (context, &context->cc_ops[i], 135 name + prefix_len + 1, 136 id); 137 } 138 } 139 if (strchr (name, ':') == NULL) 140 return allocate_ccache (context, &krb5_fcc_ops, name, id); 141 else { 142 krb5_set_error_string(context, "unknown ccache type %s", name); 143 return KRB5_CC_UNKNOWN_TYPE; 144 } 145 } 146 147 /* 148 * Generate a new ccache of type `ops' in `id'. 149 * Return 0 or an error code. 150 */ 151 152 krb5_error_code 153 krb5_cc_gen_new(krb5_context context, 154 const krb5_cc_ops *ops, 155 krb5_ccache *id) 156 { 157 krb5_ccache p; 158 159 p = malloc (sizeof(*p)); 160 if (p == NULL) { 161 krb5_set_error_string(context, "malloc: out of memory"); 162 return KRB5_CC_NOMEM; 163 } 164 p->ops = ops; 165 *id = p; 166 return p->ops->gen_new(context, id); 167 } 168 169 /* 170 * Return the name of the ccache `id' 171 */ 172 173 const char* 174 krb5_cc_get_name(krb5_context context, 175 krb5_ccache id) 176 { 177 return id->ops->get_name(context, id); 178 } 179 180 /* 181 * Return the type of the ccache `id'. 182 */ 183 184 const char* 185 krb5_cc_get_type(krb5_context context, 186 krb5_ccache id) 187 { 188 return id->ops->prefix; 189 } 190 191 /* 192 * Return a pointer to a static string containing the default ccache name. 193 */ 194 195 const char* 196 krb5_cc_default_name(krb5_context context) 197 { 198 static char name[1024]; 199 char *p; 200 201 p = getenv("KRB5CCNAME"); 202 if(p) 203 strlcpy (name, p, sizeof(name)); 204 else 205 snprintf(name, 206 sizeof(name), 207 "FILE:/tmp/krb5cc_%u", 208 (unsigned)getuid()); 209 return name; 210 } 211 212 /* 213 * Open the default ccache in `id'. 214 * Return 0 or an error code. 215 */ 216 217 krb5_error_code 218 krb5_cc_default(krb5_context context, 219 krb5_ccache *id) 220 { 221 return krb5_cc_resolve(context, 222 krb5_cc_default_name(context), 223 id); 224 } 225 226 /* 227 * Create a new ccache in `id' for `primary_principal'. 228 * Return 0 or an error code. 229 */ 230 231 krb5_error_code 232 krb5_cc_initialize(krb5_context context, 233 krb5_ccache id, 234 krb5_principal primary_principal) 235 { 236 return id->ops->init(context, id, primary_principal); 237 } 238 239 240 /* 241 * Remove the ccache `id'. 242 * Return 0 or an error code. 243 */ 244 245 krb5_error_code 246 krb5_cc_destroy(krb5_context context, 247 krb5_ccache id) 248 { 249 krb5_error_code ret; 250 251 ret = id->ops->destroy(context, id); 252 krb5_cc_close (context, id); 253 return ret; 254 } 255 256 /* 257 * Stop using the ccache `id' and free the related resources. 258 * Return 0 or an error code. 259 */ 260 261 krb5_error_code 262 krb5_cc_close(krb5_context context, 263 krb5_ccache id) 264 { 265 krb5_error_code ret; 266 ret = id->ops->close(context, id); 267 free(id); 268 return ret; 269 } 270 271 /* 272 * Store `creds' in the ccache `id'. 273 * Return 0 or an error code. 274 */ 275 276 krb5_error_code 277 krb5_cc_store_cred(krb5_context context, 278 krb5_ccache id, 279 krb5_creds *creds) 280 { 281 return id->ops->store(context, id, creds); 282 } 283 284 /* 285 * Retrieve the credential identified by `mcreds' (and `whichfields') 286 * from `id' in `creds'. 287 * Return 0 or an error code. 288 */ 289 290 krb5_error_code 291 krb5_cc_retrieve_cred(krb5_context context, 292 krb5_ccache id, 293 krb5_flags whichfields, 294 const krb5_creds *mcreds, 295 krb5_creds *creds) 296 { 297 krb5_error_code ret; 298 krb5_cc_cursor cursor; 299 krb5_cc_start_seq_get(context, id, &cursor); 300 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){ 301 if(krb5_compare_creds(context, whichfields, mcreds, creds)){ 302 ret = 0; 303 break; 304 } 305 krb5_free_creds_contents (context, creds); 306 } 307 krb5_cc_end_seq_get(context, id, &cursor); 308 return ret; 309 } 310 311 /* 312 * Return the principal of `id' in `principal'. 313 * Return 0 or an error code. 314 */ 315 316 krb5_error_code 317 krb5_cc_get_principal(krb5_context context, 318 krb5_ccache id, 319 krb5_principal *principal) 320 { 321 return id->ops->get_princ(context, id, principal); 322 } 323 324 /* 325 * Start iterating over `id', `cursor' is initialized to the 326 * beginning. 327 * Return 0 or an error code. 328 */ 329 330 krb5_error_code 331 krb5_cc_start_seq_get (krb5_context context, 332 const krb5_ccache id, 333 krb5_cc_cursor *cursor) 334 { 335 return id->ops->get_first(context, id, cursor); 336 } 337 338 /* 339 * Retrieve the next cred pointed to by (`id', `cursor') in `creds' 340 * and advance `cursor'. 341 * Return 0 or an error code. 342 */ 343 344 krb5_error_code 345 krb5_cc_next_cred (krb5_context context, 346 const krb5_ccache id, 347 krb5_cc_cursor *cursor, 348 krb5_creds *creds) 349 { 350 return id->ops->get_next(context, id, cursor, creds); 351 } 352 353 /* 354 * Destroy the cursor `cursor'. 355 */ 356 357 krb5_error_code 358 krb5_cc_end_seq_get (krb5_context context, 359 const krb5_ccache id, 360 krb5_cc_cursor *cursor) 361 { 362 return id->ops->end_get(context, id, cursor); 363 } 364 365 /* 366 * Remove the credential identified by `cred', `which' from `id'. 367 */ 368 369 krb5_error_code 370 krb5_cc_remove_cred(krb5_context context, 371 krb5_ccache id, 372 krb5_flags which, 373 krb5_creds *cred) 374 { 375 if(id->ops->remove_cred == NULL) { 376 krb5_set_error_string(context, 377 "ccache %s does not support remove_cred", 378 id->ops->prefix); 379 return EACCES; /* XXX */ 380 } 381 return (*id->ops->remove_cred)(context, id, which, cred); 382 } 383 384 /* 385 * Set the flags of `id' to `flags'. 386 */ 387 388 krb5_error_code 389 krb5_cc_set_flags(krb5_context context, 390 krb5_ccache id, 391 krb5_flags flags) 392 { 393 return id->ops->set_flags(context, id, flags); 394 } 395 396 /* 397 * Copy the contents of `from' to `to'. 398 */ 399 400 krb5_error_code 401 krb5_cc_copy_cache(krb5_context context, 402 const krb5_ccache from, 403 krb5_ccache to) 404 { 405 krb5_error_code ret; 406 krb5_cc_cursor cursor; 407 krb5_creds cred; 408 krb5_principal princ; 409 410 ret = krb5_cc_get_principal(context, from, &princ); 411 if(ret) 412 return ret; 413 ret = krb5_cc_initialize(context, to, princ); 414 if(ret){ 415 krb5_free_principal(context, princ); 416 return ret; 417 } 418 ret = krb5_cc_start_seq_get(context, from, &cursor); 419 if(ret){ 420 krb5_free_principal(context, princ); 421 return ret; 422 } 423 while(ret == 0 && krb5_cc_next_cred(context, from, &cursor, &cred) == 0){ 424 ret = krb5_cc_store_cred(context, to, &cred); 425 krb5_free_creds_contents (context, &cred); 426 } 427 krb5_cc_end_seq_get(context, from, &cursor); 428 krb5_free_principal(context, princ); 429 return ret; 430 } 431 432 /* 433 * Return the version of `id'. 434 */ 435 436 krb5_error_code 437 krb5_cc_get_version(krb5_context context, 438 const krb5_ccache id) 439 { 440 if(id->ops->get_version) 441 return id->ops->get_version(context, id); 442 else 443 return 0; 444 } 445