1 /* 2 * Copyright (c) 1997 - 2000 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: keytab.c,v 1.46 2000/02/07 03:18:05 assar Exp $"); 37 38 /* 39 * Register a new keytab in `ops' 40 * Return 0 or an error. 41 */ 42 43 krb5_error_code 44 krb5_kt_register(krb5_context context, 45 const krb5_kt_ops *ops) 46 { 47 struct krb5_keytab_data *tmp; 48 49 tmp = realloc(context->kt_types, 50 (context->num_kt_types + 1) * sizeof(*context->kt_types)); 51 if(tmp == NULL) 52 return ENOMEM; 53 memcpy(&tmp[context->num_kt_types], ops, 54 sizeof(tmp[context->num_kt_types])); 55 context->kt_types = tmp; 56 context->num_kt_types++; 57 return 0; 58 } 59 60 /* 61 * Resolve the keytab name (of the form `type:residual') in `name' 62 * into a keytab in `id'. 63 * Return 0 or an error 64 */ 65 66 krb5_error_code 67 krb5_kt_resolve(krb5_context context, 68 const char *name, 69 krb5_keytab *id) 70 { 71 krb5_keytab k; 72 int i; 73 const char *type, *residual; 74 size_t type_len; 75 krb5_error_code ret; 76 77 residual = strchr(name, ':'); 78 if(residual == NULL) { 79 type = "FILE"; 80 type_len = strlen(type); 81 residual = name; 82 } else { 83 type = name; 84 type_len = residual - name; 85 residual++; 86 } 87 88 for(i = 0; i < context->num_kt_types; i++) { 89 if(strncmp(type, context->kt_types[i].prefix, type_len) == 0) 90 break; 91 } 92 if(i == context->num_kt_types) 93 return KRB5_KT_UNKNOWN_TYPE; 94 95 k = malloc (sizeof(*k)); 96 if (k == NULL) 97 return ENOMEM; 98 memcpy(k, &context->kt_types[i], sizeof(*k)); 99 k->data = NULL; 100 ret = (*k->resolve)(context, residual, k); 101 if(ret) { 102 free(k); 103 k = NULL; 104 } 105 *id = k; 106 return ret; 107 } 108 109 /* 110 * copy the name of the default keytab into `name'. 111 * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short. 112 */ 113 114 krb5_error_code 115 krb5_kt_default_name(krb5_context context, char *name, size_t namesize) 116 { 117 if (strlcpy (name, context->default_keytab, namesize) >= namesize) 118 return KRB5_CONFIG_NOTENUFSPACE; 119 return 0; 120 } 121 122 /* 123 * Set `id' to the default keytab. 124 * Return 0 or an error. 125 */ 126 127 krb5_error_code 128 krb5_kt_default(krb5_context context, krb5_keytab *id) 129 { 130 return krb5_kt_resolve (context, context->default_keytab, id); 131 } 132 133 /* 134 * Read the key identified by `(principal, vno, enctype)' from the 135 * keytab in `keyprocarg' (the default if == NULL) into `*key'. 136 * Return 0 or an error. 137 */ 138 139 krb5_error_code 140 krb5_kt_read_service_key(krb5_context context, 141 krb5_pointer keyprocarg, 142 krb5_principal principal, 143 krb5_kvno vno, 144 krb5_enctype enctype, 145 krb5_keyblock **key) 146 { 147 krb5_keytab keytab; 148 krb5_keytab_entry entry; 149 krb5_error_code ret; 150 151 if (keyprocarg) 152 ret = krb5_kt_resolve (context, keyprocarg, &keytab); 153 else 154 ret = krb5_kt_default (context, &keytab); 155 156 if (ret) 157 return ret; 158 159 ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); 160 krb5_kt_close (context, keytab); 161 if (ret) 162 return ret; 163 ret = krb5_copy_keyblock (context, &entry.keyblock, key); 164 krb5_kt_free_entry(context, &entry); 165 return ret; 166 } 167 168 /* 169 * Retrieve the name of the keytab `keytab' into `name', `namesize' 170 * Return 0 or an error. 171 */ 172 173 krb5_error_code 174 krb5_kt_get_name(krb5_context context, 175 krb5_keytab keytab, 176 char *name, 177 size_t namesize) 178 { 179 return (*keytab->get_name)(context, keytab, name, namesize); 180 } 181 182 /* 183 * Finish using the keytab in `id'. All resources will be released. 184 * Return 0 or an error. 185 */ 186 187 krb5_error_code 188 krb5_kt_close(krb5_context context, 189 krb5_keytab id) 190 { 191 krb5_error_code ret; 192 193 ret = (*id->close)(context, id); 194 if(ret == 0) 195 free(id); 196 return ret; 197 } 198 199 /* 200 * Compare `entry' against `principal, vno, enctype'. 201 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. 202 * Return TRUE if they compare the same, FALSE otherwise. 203 */ 204 205 krb5_boolean 206 krb5_kt_compare(krb5_context context, 207 krb5_keytab_entry *entry, 208 krb5_const_principal principal, 209 krb5_kvno vno, 210 krb5_enctype enctype) 211 { 212 if(principal != NULL && 213 !krb5_principal_compare(context, entry->principal, principal)) 214 return FALSE; 215 if(vno && vno != entry->vno) 216 return FALSE; 217 if(enctype && enctype != entry->keyblock.keytype) 218 return FALSE; 219 return TRUE; 220 } 221 222 /* 223 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' 224 * from the keytab `id'. 225 * Return 0 or an error. 226 */ 227 228 krb5_error_code 229 krb5_kt_get_entry(krb5_context context, 230 krb5_keytab id, 231 krb5_const_principal principal, 232 krb5_kvno kvno, 233 krb5_enctype enctype, 234 krb5_keytab_entry *entry) 235 { 236 krb5_keytab_entry tmp; 237 krb5_error_code ret; 238 krb5_kt_cursor cursor; 239 240 if(id->get) 241 return (*id->get)(context, id, principal, kvno, enctype, entry); 242 243 ret = krb5_kt_start_seq_get (context, id, &cursor); 244 if (ret) 245 return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */ 246 247 entry->vno = 0; 248 while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { 249 if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { 250 if (kvno == tmp.vno) { 251 krb5_kt_copy_entry_contents (context, &tmp, entry); 252 krb5_kt_free_entry (context, &tmp); 253 krb5_kt_end_seq_get(context, id, &cursor); 254 return 0; 255 } else if (kvno == 0 && tmp.vno > entry->vno) { 256 if (entry->vno) 257 krb5_kt_free_entry (context, entry); 258 krb5_kt_copy_entry_contents (context, &tmp, entry); 259 } 260 } 261 krb5_kt_free_entry(context, &tmp); 262 } 263 krb5_kt_end_seq_get (context, id, &cursor); 264 if (entry->vno) 265 return 0; 266 else 267 return KRB5_KT_NOTFOUND; 268 } 269 270 /* 271 * Copy the contents of `in' into `out'. 272 * Return 0 or an error. 273 */ 274 275 krb5_error_code 276 krb5_kt_copy_entry_contents(krb5_context context, 277 const krb5_keytab_entry *in, 278 krb5_keytab_entry *out) 279 { 280 krb5_error_code ret; 281 282 memset(out, 0, sizeof(*out)); 283 out->vno = in->vno; 284 285 ret = krb5_copy_principal (context, in->principal, &out->principal); 286 if (ret) 287 goto fail; 288 ret = krb5_copy_keyblock_contents (context, 289 &in->keyblock, 290 &out->keyblock); 291 if (ret) 292 goto fail; 293 out->timestamp = in->timestamp; 294 return 0; 295 fail: 296 krb5_kt_free_entry (context, out); 297 return ret; 298 } 299 300 /* 301 * Free the contents of `entry'. 302 */ 303 304 krb5_error_code 305 krb5_kt_free_entry(krb5_context context, 306 krb5_keytab_entry *entry) 307 { 308 krb5_free_principal (context, entry->principal); 309 krb5_free_keyblock_contents (context, &entry->keyblock); 310 return 0; 311 } 312 313 #if 0 314 static int 315 xxxlock(int fd, int write) 316 { 317 if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) { 318 sleep(1); 319 if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) 320 return -1; 321 } 322 return 0; 323 } 324 325 static void 326 xxxunlock(int fd) 327 { 328 flock(fd, LOCK_UN); 329 } 330 #endif 331 332 /* 333 * Set `cursor' to point at the beginning of `id'. 334 * Return 0 or an error. 335 */ 336 337 krb5_error_code 338 krb5_kt_start_seq_get(krb5_context context, 339 krb5_keytab id, 340 krb5_kt_cursor *cursor) 341 { 342 if(id->start_seq_get == NULL) 343 return HEIM_ERR_OPNOTSUPP; 344 return (*id->start_seq_get)(context, id, cursor); 345 } 346 347 /* 348 * Get the next entry from `id' pointed to by `cursor' and advance the 349 * `cursor'. 350 * Return 0 or an error. 351 */ 352 353 krb5_error_code 354 krb5_kt_next_entry(krb5_context context, 355 krb5_keytab id, 356 krb5_keytab_entry *entry, 357 krb5_kt_cursor *cursor) 358 { 359 if(id->next_entry == NULL) 360 return HEIM_ERR_OPNOTSUPP; 361 return (*id->next_entry)(context, id, entry, cursor); 362 } 363 364 /* 365 * Release all resources associated with `cursor'. 366 */ 367 368 krb5_error_code 369 krb5_kt_end_seq_get(krb5_context context, 370 krb5_keytab id, 371 krb5_kt_cursor *cursor) 372 { 373 if(id->end_seq_get == NULL) 374 return HEIM_ERR_OPNOTSUPP; 375 return (*id->end_seq_get)(context, id, cursor); 376 } 377 378 /* 379 * Add the entry in `entry' to the keytab `id'. 380 * Return 0 or an error. 381 */ 382 383 krb5_error_code 384 krb5_kt_add_entry(krb5_context context, 385 krb5_keytab id, 386 krb5_keytab_entry *entry) 387 { 388 if(id->add == NULL) 389 return KRB5_KT_NOWRITE; 390 entry->timestamp = time(NULL); 391 return (*id->add)(context, id,entry); 392 } 393 394 /* 395 * Remove the entry `entry' from the keytab `id'. 396 * Return 0 or an error. 397 */ 398 399 krb5_error_code 400 krb5_kt_remove_entry(krb5_context context, 401 krb5_keytab id, 402 krb5_keytab_entry *entry) 403 { 404 if(id->remove == NULL) 405 return KRB5_KT_NOWRITE; 406 return (*id->remove)(context, id, entry); 407 } 408