1 /* 2 * Copyright (c) 1997-2002 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.49 2002/05/29 16:08:23 joda 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 a pointer to a static string containing the default ccache name. 184 */ 185 186 const char* 187 krb5_cc_default_name(krb5_context context) 188 { 189 static char name[1024]; 190 char *p; 191 192 p = getenv("KRB5CCNAME"); 193 if(p) 194 strlcpy (name, p, sizeof(name)); 195 else 196 snprintf(name, 197 sizeof(name), 198 "FILE:/tmp/krb5cc_%u", 199 (unsigned)getuid()); 200 return name; 201 } 202 203 /* 204 * Open the default ccache in `id'. 205 * Return 0 or an error code. 206 */ 207 208 krb5_error_code 209 krb5_cc_default(krb5_context context, 210 krb5_ccache *id) 211 { 212 return krb5_cc_resolve(context, 213 krb5_cc_default_name(context), 214 id); 215 } 216 217 /* 218 * Create a new ccache in `id' for `primary_principal'. 219 * Return 0 or an error code. 220 */ 221 222 krb5_error_code 223 krb5_cc_initialize(krb5_context context, 224 krb5_ccache id, 225 krb5_principal primary_principal) 226 { 227 return id->ops->init(context, id, primary_principal); 228 } 229 230 231 /* 232 * Remove the ccache `id'. 233 * Return 0 or an error code. 234 */ 235 236 krb5_error_code 237 krb5_cc_destroy(krb5_context context, 238 krb5_ccache id) 239 { 240 krb5_error_code ret; 241 242 ret = id->ops->destroy(context, id); 243 krb5_cc_close (context, id); 244 return ret; 245 } 246 247 /* 248 * Stop using the ccache `id' and free the related resources. 249 * Return 0 or an error code. 250 */ 251 252 krb5_error_code 253 krb5_cc_close(krb5_context context, 254 krb5_ccache id) 255 { 256 krb5_error_code ret; 257 ret = id->ops->close(context, id); 258 free(id); 259 return ret; 260 } 261 262 /* 263 * Store `creds' in the ccache `id'. 264 * Return 0 or an error code. 265 */ 266 267 krb5_error_code 268 krb5_cc_store_cred(krb5_context context, 269 krb5_ccache id, 270 krb5_creds *creds) 271 { 272 return id->ops->store(context, id, creds); 273 } 274 275 /* 276 * Retrieve the credential identified by `mcreds' (and `whichfields') 277 * from `id' in `creds'. 278 * Return 0 or an error code. 279 */ 280 281 krb5_error_code 282 krb5_cc_retrieve_cred(krb5_context context, 283 krb5_ccache id, 284 krb5_flags whichfields, 285 const krb5_creds *mcreds, 286 krb5_creds *creds) 287 { 288 krb5_error_code ret; 289 krb5_cc_cursor cursor; 290 krb5_cc_start_seq_get(context, id, &cursor); 291 while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){ 292 if(krb5_compare_creds(context, whichfields, mcreds, creds)){ 293 ret = 0; 294 break; 295 } 296 krb5_free_creds_contents (context, creds); 297 } 298 krb5_cc_end_seq_get(context, id, &cursor); 299 return ret; 300 } 301 302 /* 303 * Return the principal of `id' in `principal'. 304 * Return 0 or an error code. 305 */ 306 307 krb5_error_code 308 krb5_cc_get_principal(krb5_context context, 309 krb5_ccache id, 310 krb5_principal *principal) 311 { 312 return id->ops->get_princ(context, id, principal); 313 } 314 315 /* 316 * Start iterating over `id', `cursor' is initialized to the 317 * beginning. 318 * Return 0 or an error code. 319 */ 320 321 krb5_error_code 322 krb5_cc_start_seq_get (krb5_context context, 323 const krb5_ccache id, 324 krb5_cc_cursor *cursor) 325 { 326 return id->ops->get_first(context, id, cursor); 327 } 328 329 /* 330 * Retrieve the next cred pointed to by (`id', `cursor') in `creds' 331 * and advance `cursor'. 332 * Return 0 or an error code. 333 */ 334 335 krb5_error_code 336 krb5_cc_next_cred (krb5_context context, 337 const krb5_ccache id, 338 krb5_cc_cursor *cursor, 339 krb5_creds *creds) 340 { 341 return id->ops->get_next(context, id, cursor, creds); 342 } 343 344 /* 345 * Destroy the cursor `cursor'. 346 */ 347 348 krb5_error_code 349 krb5_cc_end_seq_get (krb5_context context, 350 const krb5_ccache id, 351 krb5_cc_cursor *cursor) 352 { 353 return id->ops->end_get(context, id, cursor); 354 } 355 356 /* 357 * Remove the credential identified by `cred', `which' from `id'. 358 */ 359 360 krb5_error_code 361 krb5_cc_remove_cred(krb5_context context, 362 krb5_ccache id, 363 krb5_flags which, 364 krb5_creds *cred) 365 { 366 if(id->ops->remove_cred == NULL) { 367 krb5_set_error_string(context, 368 "ccache %s does not support remove_cred", 369 id->ops->prefix); 370 return EACCES; /* XXX */ 371 } 372 return (*id->ops->remove_cred)(context, id, which, cred); 373 } 374 375 /* 376 * Set the flags of `id' to `flags'. 377 */ 378 379 krb5_error_code 380 krb5_cc_set_flags(krb5_context context, 381 krb5_ccache id, 382 krb5_flags flags) 383 { 384 return id->ops->set_flags(context, id, flags); 385 } 386 387 /* 388 * Copy the contents of `from' to `to'. 389 */ 390 391 krb5_error_code 392 krb5_cc_copy_cache(krb5_context context, 393 const krb5_ccache from, 394 krb5_ccache to) 395 { 396 krb5_error_code ret; 397 krb5_cc_cursor cursor; 398 krb5_creds cred; 399 krb5_principal princ; 400 401 ret = krb5_cc_get_principal(context, from, &princ); 402 if(ret) 403 return ret; 404 ret = krb5_cc_initialize(context, to, princ); 405 if(ret){ 406 krb5_free_principal(context, princ); 407 return ret; 408 } 409 ret = krb5_cc_start_seq_get(context, from, &cursor); 410 if(ret){ 411 krb5_free_principal(context, princ); 412 return ret; 413 } 414 while(ret == 0 && krb5_cc_next_cred(context, from, &cursor, &cred) == 0){ 415 ret = krb5_cc_store_cred(context, to, &cred); 416 krb5_free_creds_contents (context, &cred); 417 } 418 krb5_cc_end_seq_get(context, from, &cursor); 419 krb5_free_principal(context, princ); 420 return ret; 421 } 422 423 /* 424 * Return the version of `id'. 425 */ 426 427 krb5_error_code 428 krb5_cc_get_version(krb5_context context, 429 const krb5_ccache id) 430 { 431 if(id->ops->get_version) 432 return id->ops->get_version(context, id); 433 else 434 return 0; 435 } 436