1 /* 2 * Copyright (c) 1997-2003 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.52 2003/03/16 18:23:59 lha 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 int i; 50 51 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 52 if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) { 53 if(!override) { 54 krb5_set_error_string(context, 55 "ccache type %s already exists", 56 ops->prefix); 57 return KRB5_CC_TYPE_EXISTS; 58 } 59 break; 60 } 61 } 62 if(i == context->num_cc_ops) { 63 krb5_cc_ops *o = realloc(context->cc_ops, 64 (context->num_cc_ops + 1) * 65 sizeof(*context->cc_ops)); 66 if(o == NULL) { 67 krb5_set_error_string(context, "malloc: out of memory"); 68 return KRB5_CC_NOMEM; 69 } 70 context->num_cc_ops++; 71 context->cc_ops = o; 72 memset(context->cc_ops + i, 0, 73 (context->num_cc_ops - i) * sizeof(*context->cc_ops)); 74 } 75 memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i])); 76 return 0; 77 } 78 79 /* 80 * Allocate memory for a new ccache in `id' with operations `ops' 81 * and name `residual'. 82 * Return 0 or an error code. 83 */ 84 85 static krb5_error_code 86 allocate_ccache (krb5_context context, 87 const krb5_cc_ops *ops, 88 const char *residual, 89 krb5_ccache *id) 90 { 91 krb5_error_code ret; 92 krb5_ccache p; 93 94 p = malloc(sizeof(*p)); 95 if(p == NULL) { 96 krb5_set_error_string(context, "malloc: out of memory"); 97 return KRB5_CC_NOMEM; 98 } 99 p->ops = ops; 100 *id = p; 101 ret = p->ops->resolve(context, id, residual); 102 if(ret) 103 free(p); 104 return ret; 105 } 106 107 /* 108 * Find and allocate a ccache in `id' from the specification in `residual'. 109 * If the ccache name doesn't contain any colon, interpret it as a file name. 110 * Return 0 or an error code. 111 */ 112 113 krb5_error_code 114 krb5_cc_resolve(krb5_context context, 115 const char *name, 116 krb5_ccache *id) 117 { 118 int i; 119 120 for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) { 121 size_t prefix_len = strlen(context->cc_ops[i].prefix); 122 123 if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0 124 && name[prefix_len] == ':') { 125 return allocate_ccache (context, &context->cc_ops[i], 126 name + prefix_len + 1, 127 id); 128 } 129 } 130 if (strchr (name, ':') == NULL) 131 return allocate_ccache (context, &krb5_fcc_ops, name, id); 132 else { 133 krb5_set_error_string(context, "unknown ccache type %s", name); 134 return KRB5_CC_UNKNOWN_TYPE; 135 } 136 } 137 138 /* 139 * Generate a new ccache of type `ops' in `id'. 140 * Return 0 or an error code. 141 */ 142 143 krb5_error_code 144 krb5_cc_gen_new(krb5_context context, 145 const krb5_cc_ops *ops, 146 krb5_ccache *id) 147 { 148 krb5_ccache p; 149 150 p = malloc (sizeof(*p)); 151 if (p == NULL) { 152 krb5_set_error_string(context, "malloc: out of memory"); 153 return KRB5_CC_NOMEM; 154 } 155 p->ops = ops; 156 *id = p; 157 return p->ops->gen_new(context, id); 158 } 159 160 /* 161 * Return the name of the ccache `id' 162 */ 163 164 const char* 165 krb5_cc_get_name(krb5_context context, 166 krb5_ccache id) 167 { 168 return id->ops->get_name(context, id); 169 } 170 171 /* 172 * Return the type of the ccache `id'. 173 */ 174 175 const char* 176 krb5_cc_get_type(krb5_context context, 177 krb5_ccache id) 178 { 179 return id->ops->prefix; 180 } 181 182 /* 183 * Return krb5_cc_ops of a the ccache `id'. 184 */ 185 186 const krb5_cc_ops * 187 krb5_cc_get_ops(krb5_context context, krb5_ccache id) 188 { 189 return id->ops; 190 } 191 192 /* 193 * Set the default cc name for `context' to `name'. 194 */ 195 196 krb5_error_code 197 krb5_cc_set_default_name(krb5_context context, const char *name) 198 { 199 krb5_error_code ret = 0; 200 char *p; 201 202 if (name == NULL) { 203 char *e; 204 e = getenv("KRB5CCNAME"); 205 if (e) 206 p = strdup(e); 207 else 208 asprintf(&p,"FILE:/tmp/krb5cc_%u", (unsigned)getuid()); 209 } else 210 p = strdup(name); 211 212 if (p == NULL) 213 return ENOMEM; 214 215 if (context->default_cc_name) 216 free(context->default_cc_name); 217 218 context->default_cc_name = p; 219 220 return ret; 221 } 222 223 /* 224 * Return a pointer to a context static string containing the default ccache name. 225 */ 226 227 const char* 228 krb5_cc_default_name(krb5_context context) 229 { 230 if (context->default_cc_name == NULL) 231 krb5_cc_set_default_name(context, NULL); 232 233 return context->default_cc_name; 234 } 235 236 /* 237 * Open the default ccache in `id'. 238 * Return 0 or an error code. 239 */ 240 241 krb5_error_code 242 krb5_cc_default(krb5_context context, 243 krb5_ccache *id) 244 { 245 const char *p = krb5_cc_default_name(context); 246 247 if (p == NULL) 248 return ENOMEM; 249 return krb5_cc_resolve(context, p, id); 250 } 251 252 /* 253 * Create a new ccache in `id' for `primary_principal'. 254 * Return 0 or an error code. 255 */ 256 257 krb5_error_code 258 krb5_cc_initialize(krb5_context context, 259 krb5_ccache id, 260 krb5_principal primary_principal) 261 { 262 return id->ops->init(context, id, primary_principal); 263 } 264 265 266 /* 267 * Remove the ccache `id'. 268 * Return 0 or an error code. 269 */ 270 271 krb5_error_code 272 krb5_cc_destroy(krb5_context context, 273 krb5_ccache id) 274 { 275 krb5_error_code ret; 276 277 ret = id->ops->destroy(context, id); 278 krb5_cc_close (context, id); 279 return ret; 280 } 281 282 /* 283 * Stop using the ccache `id' and free the related resources. 284 * Return 0 or an error code. 285 */ 286 287 krb5_error_code 288 krb5_cc_close(krb5_context context, 289 krb5_ccache id) 290 { 291 krb5_error_code ret; 292 ret = id->ops->close(context, id); 293 free(id); 294 return ret; 295 } 296 297 /* 298 * Store `creds' in the ccache `id'. 299 * Return 0 or an error code. 300 */ 301 302 krb5_error_code 303 krb5_cc_store_cred(krb5_context context, 304 krb5_ccache id, 305 krb5_creds *creds) 306 { 307 return id->ops->store(context, id, creds); 308 } 309 310 /* 311 * Retrieve the credential identified by `mcreds' (and `whichfields') 312 * from `id' in `creds'. 313 * Return 0 or an error code. 314 */ 315 316 krb5_error_code 317 krb5_cc_retrieve_cred(krb5_context context, 318 krb5_ccache id, 319 krb5_flags whichfields, 320 const krb5_creds *mcreds, 321 krb5_creds *creds) 322 { 323 krb5_error_code ret; 324 krb5_cc_cursor cursor; 325 krb5_cc_start_seq_get(context, id, &cursor); 326 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){ 327 if(krb5_compare_creds(context, whichfields, mcreds, creds)){ 328 ret = 0; 329 break; 330 } 331 krb5_free_creds_contents (context, creds); 332 } 333 krb5_cc_end_seq_get(context, id, &cursor); 334 return ret; 335 } 336 337 /* 338 * Return the principal of `id' in `principal'. 339 * Return 0 or an error code. 340 */ 341 342 krb5_error_code 343 krb5_cc_get_principal(krb5_context context, 344 krb5_ccache id, 345 krb5_principal *principal) 346 { 347 return id->ops->get_princ(context, id, principal); 348 } 349 350 /* 351 * Start iterating over `id', `cursor' is initialized to the 352 * beginning. 353 * Return 0 or an error code. 354 */ 355 356 krb5_error_code 357 krb5_cc_start_seq_get (krb5_context context, 358 const krb5_ccache id, 359 krb5_cc_cursor *cursor) 360 { 361 return id->ops->get_first(context, id, cursor); 362 } 363 364 /* 365 * Retrieve the next cred pointed to by (`id', `cursor') in `creds' 366 * and advance `cursor'. 367 * Return 0 or an error code. 368 */ 369 370 krb5_error_code 371 krb5_cc_next_cred (krb5_context context, 372 const krb5_ccache id, 373 krb5_cc_cursor *cursor, 374 krb5_creds *creds) 375 { 376 return id->ops->get_next(context, id, cursor, creds); 377 } 378 379 /* 380 * Destroy the cursor `cursor'. 381 */ 382 383 krb5_error_code 384 krb5_cc_end_seq_get (krb5_context context, 385 const krb5_ccache id, 386 krb5_cc_cursor *cursor) 387 { 388 return id->ops->end_get(context, id, cursor); 389 } 390 391 /* 392 * Remove the credential identified by `cred', `which' from `id'. 393 */ 394 395 krb5_error_code 396 krb5_cc_remove_cred(krb5_context context, 397 krb5_ccache id, 398 krb5_flags which, 399 krb5_creds *cred) 400 { 401 if(id->ops->remove_cred == NULL) { 402 krb5_set_error_string(context, 403 "ccache %s does not support remove_cred", 404 id->ops->prefix); 405 return EACCES; /* XXX */ 406 } 407 return (*id->ops->remove_cred)(context, id, which, cred); 408 } 409 410 /* 411 * Set the flags of `id' to `flags'. 412 */ 413 414 krb5_error_code 415 krb5_cc_set_flags(krb5_context context, 416 krb5_ccache id, 417 krb5_flags flags) 418 { 419 return id->ops->set_flags(context, id, flags); 420 } 421 422 /* 423 * Copy the contents of `from' to `to'. 424 */ 425 426 krb5_error_code 427 krb5_cc_copy_cache(krb5_context context, 428 const krb5_ccache from, 429 krb5_ccache to) 430 { 431 krb5_error_code ret; 432 krb5_cc_cursor cursor; 433 krb5_creds cred; 434 krb5_principal princ; 435 436 ret = krb5_cc_get_principal(context, from, &princ); 437 if(ret) 438 return ret; 439 ret = krb5_cc_initialize(context, to, princ); 440 if(ret){ 441 krb5_free_principal(context, princ); 442 return ret; 443 } 444 ret = krb5_cc_start_seq_get(context, from, &cursor); 445 if(ret){ 446 krb5_free_principal(context, princ); 447 return ret; 448 } 449 while(ret == 0 && krb5_cc_next_cred(context, from, &cursor, &cred) == 0){ 450 ret = krb5_cc_store_cred(context, to, &cred); 451 krb5_free_creds_contents (context, &cred); 452 } 453 krb5_cc_end_seq_get(context, from, &cursor); 454 krb5_free_principal(context, princ); 455 return ret; 456 } 457 458 /* 459 * Return the version of `id'. 460 */ 461 462 krb5_error_code 463 krb5_cc_get_version(krb5_context context, 464 const krb5_ccache id) 465 { 466 if(id->ops->get_version) 467 return id->ops->get_version(context, id); 468 else 469 return 0; 470 } 471