1 /* 2 * Copyright (c) 1997 - 2007 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 #ifndef HEIMDAL_SMALLER 37 38 /* afs keyfile operations --------------------------------------- */ 39 40 /* 41 * Minimum tools to handle the AFS KeyFile. 42 * 43 * Format of the KeyFile is: 44 * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys} 45 * 46 * It just adds to the end of the keyfile, deleting isn't implemented. 47 * Use your favorite text/hex editor to delete keys. 48 * 49 */ 50 51 #define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell" 52 #define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf" 53 54 struct akf_data { 55 uint32_t num_entries; 56 char *filename; 57 char *cell; 58 char *realm; 59 }; 60 61 /* 62 * set `d->cell' and `d->realm' 63 */ 64 65 static int 66 get_cell_and_realm (krb5_context context, struct akf_data *d) 67 { 68 FILE *f; 69 char buf[BUFSIZ], *cp; 70 int ret; 71 72 f = fopen (AFS_SERVERTHISCELL, "r"); 73 if (f == NULL) { 74 ret = errno; 75 krb5_set_error_message (context, ret, 76 N_("Open ThisCell %s: %s", ""), 77 AFS_SERVERTHISCELL, 78 strerror(ret)); 79 return ret; 80 } 81 if (fgets (buf, sizeof(buf), f) == NULL) { 82 fclose (f); 83 krb5_set_error_message (context, EINVAL, 84 N_("No cell in ThisCell file %s", ""), 85 AFS_SERVERTHISCELL); 86 return EINVAL; 87 } 88 buf[strcspn(buf, "\n")] = '\0'; 89 fclose(f); 90 91 d->cell = strdup (buf); 92 if (d->cell == NULL) { 93 krb5_set_error_message(context, ENOMEM, 94 N_("malloc: out of memory", "")); 95 return ENOMEM; 96 } 97 98 f = fopen (AFS_SERVERMAGICKRBCONF, "r"); 99 if (f != NULL) { 100 if (fgets (buf, sizeof(buf), f) == NULL) { 101 free (d->cell); 102 d->cell = NULL; 103 fclose (f); 104 krb5_set_error_message (context, EINVAL, 105 N_("No realm in ThisCell file %s", ""), 106 AFS_SERVERMAGICKRBCONF); 107 return EINVAL; 108 } 109 buf[strcspn(buf, "\n")] = '\0'; 110 fclose(f); 111 } 112 /* uppercase */ 113 for (cp = buf; *cp != '\0'; cp++) 114 *cp = toupper((unsigned char)*cp); 115 116 d->realm = strdup (buf); 117 if (d->realm == NULL) { 118 free (d->cell); 119 d->cell = NULL; 120 krb5_set_error_message(context, ENOMEM, 121 N_("malloc: out of memory", "")); 122 return ENOMEM; 123 } 124 return 0; 125 } 126 127 /* 128 * init and get filename 129 */ 130 131 static krb5_error_code KRB5_CALLCONV 132 akf_resolve(krb5_context context, const char *name, krb5_keytab id) 133 { 134 int ret; 135 struct akf_data *d = malloc(sizeof (struct akf_data)); 136 137 if (d == NULL) { 138 krb5_set_error_message(context, ENOMEM, 139 N_("malloc: out of memory", "")); 140 return ENOMEM; 141 } 142 143 d->num_entries = 0; 144 ret = get_cell_and_realm (context, d); 145 if (ret) { 146 free (d); 147 return ret; 148 } 149 d->filename = strdup (name); 150 if (d->filename == NULL) { 151 free (d->cell); 152 free (d->realm); 153 free (d); 154 krb5_set_error_message(context, ENOMEM, 155 N_("malloc: out of memory", "")); 156 return ENOMEM; 157 } 158 id->data = d; 159 160 return 0; 161 } 162 163 /* 164 * cleanup 165 */ 166 167 static krb5_error_code KRB5_CALLCONV 168 akf_close(krb5_context context, krb5_keytab id) 169 { 170 struct akf_data *d = id->data; 171 172 free (d->filename); 173 free (d->cell); 174 free (d); 175 return 0; 176 } 177 178 /* 179 * Return filename 180 */ 181 182 static krb5_error_code KRB5_CALLCONV 183 akf_get_name(krb5_context context, 184 krb5_keytab id, 185 char *name, 186 size_t name_sz) 187 { 188 struct akf_data *d = id->data; 189 190 strlcpy (name, d->filename, name_sz); 191 return 0; 192 } 193 194 /* 195 * Init 196 */ 197 198 static krb5_error_code KRB5_CALLCONV 199 akf_start_seq_get(krb5_context context, 200 krb5_keytab id, 201 krb5_kt_cursor *c) 202 { 203 int32_t ret; 204 struct akf_data *d = id->data; 205 206 c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600); 207 if (c->fd < 0) { 208 ret = errno; 209 krb5_set_error_message(context, ret, 210 N_("keytab afs keyfile open %s failed: %s", ""), 211 d->filename, strerror(ret)); 212 return ret; 213 } 214 215 c->data = NULL; 216 c->sp = krb5_storage_from_fd(c->fd); 217 if (c->sp == NULL) { 218 close(c->fd); 219 krb5_clear_error_message (context); 220 return KRB5_KT_NOTFOUND; 221 } 222 krb5_storage_set_eof_code(c->sp, KRB5_KT_END); 223 224 ret = krb5_ret_uint32(c->sp, &d->num_entries); 225 if(ret || d->num_entries > INT_MAX / 8) { 226 krb5_storage_free(c->sp); 227 close(c->fd); 228 krb5_clear_error_message (context); 229 if(ret == KRB5_KT_END) 230 return KRB5_KT_NOTFOUND; 231 return ret; 232 } 233 234 return 0; 235 } 236 237 static krb5_error_code KRB5_CALLCONV 238 akf_next_entry(krb5_context context, 239 krb5_keytab id, 240 krb5_keytab_entry *entry, 241 krb5_kt_cursor *cursor) 242 { 243 struct akf_data *d = id->data; 244 int32_t kvno; 245 off_t pos; 246 int ret; 247 248 pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); 249 250 if ((pos - 4) / (4 + 8) >= d->num_entries) 251 return KRB5_KT_END; 252 253 ret = krb5_make_principal (context, &entry->principal, 254 d->realm, "afs", d->cell, NULL); 255 if (ret) 256 goto out; 257 258 ret = krb5_ret_int32(cursor->sp, &kvno); 259 if (ret) { 260 krb5_free_principal (context, entry->principal); 261 goto out; 262 } 263 264 entry->vno = kvno; 265 266 if (cursor->data) 267 entry->keyblock.keytype = ETYPE_DES_CBC_MD5; 268 else 269 entry->keyblock.keytype = ETYPE_DES_CBC_CRC; 270 entry->keyblock.keyvalue.length = 8; 271 entry->keyblock.keyvalue.data = malloc (8); 272 if (entry->keyblock.keyvalue.data == NULL) { 273 krb5_free_principal (context, entry->principal); 274 krb5_set_error_message(context, ENOMEM, 275 N_("malloc: out of memory", "")); 276 ret = ENOMEM; 277 goto out; 278 } 279 280 ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8); 281 if(ret != 8) 282 ret = (ret < 0) ? errno : KRB5_KT_END; 283 else 284 ret = 0; 285 286 entry->timestamp = time(NULL); 287 entry->flags = 0; 288 entry->aliases = NULL; 289 290 out: 291 if (cursor->data) { 292 krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET); 293 cursor->data = NULL; 294 } else 295 cursor->data = cursor; 296 return ret; 297 } 298 299 static krb5_error_code KRB5_CALLCONV 300 akf_end_seq_get(krb5_context context, 301 krb5_keytab id, 302 krb5_kt_cursor *cursor) 303 { 304 krb5_storage_free(cursor->sp); 305 close(cursor->fd); 306 cursor->data = NULL; 307 return 0; 308 } 309 310 static krb5_error_code KRB5_CALLCONV 311 akf_add_entry(krb5_context context, 312 krb5_keytab id, 313 krb5_keytab_entry *entry) 314 { 315 struct akf_data *d = id->data; 316 int fd, created = 0; 317 krb5_error_code ret; 318 int32_t len; 319 krb5_storage *sp; 320 321 322 if (entry->keyblock.keyvalue.length != 8) 323 return 0; 324 switch(entry->keyblock.keytype) { 325 case ETYPE_DES_CBC_CRC: 326 case ETYPE_DES_CBC_MD4: 327 case ETYPE_DES_CBC_MD5: 328 break; 329 default: 330 return 0; 331 } 332 333 fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC); 334 if (fd < 0) { 335 fd = open (d->filename, 336 O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); 337 if (fd < 0) { 338 ret = errno; 339 krb5_set_error_message(context, ret, 340 N_("open keyfile(%s): %s", ""), 341 d->filename, 342 strerror(ret)); 343 return ret; 344 } 345 created = 1; 346 } 347 348 sp = krb5_storage_from_fd(fd); 349 if(sp == NULL) { 350 close(fd); 351 krb5_set_error_message(context, ENOMEM, 352 N_("malloc: out of memory", "")); 353 return ENOMEM; 354 } 355 if (created) 356 len = 0; 357 else { 358 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 359 ret = errno; 360 krb5_storage_free(sp); 361 close(fd); 362 krb5_set_error_message(context, ret, 363 N_("seeking in keyfile: %s", ""), 364 strerror(ret)); 365 return ret; 366 } 367 368 ret = krb5_ret_int32(sp, &len); 369 if(ret) { 370 krb5_storage_free(sp); 371 close(fd); 372 return ret; 373 } 374 } 375 376 /* 377 * Make sure we don't add the entry twice, assumes the DES 378 * encryption types are all the same key. 379 */ 380 if (len > 0) { 381 int32_t kvno; 382 int i; 383 384 for (i = 0; i < len; i++) { 385 ret = krb5_ret_int32(sp, &kvno); 386 if (ret) { 387 krb5_set_error_message (context, ret, 388 N_("Failed getting kvno from keyfile", "")); 389 goto out; 390 } 391 if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) { 392 ret = errno; 393 krb5_set_error_message (context, ret, 394 N_("Failed seeing in keyfile: %s", ""), 395 strerror(ret)); 396 goto out; 397 } 398 if (kvno == entry->vno) { 399 ret = 0; 400 goto out; 401 } 402 } 403 } 404 405 len++; 406 407 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 408 ret = errno; 409 krb5_set_error_message (context, ret, 410 N_("Failed seeing in keyfile: %s", ""), 411 strerror(ret)); 412 goto out; 413 } 414 415 ret = krb5_store_int32(sp, len); 416 if(ret) { 417 ret = errno; 418 krb5_set_error_message (context, ret, 419 N_("keytab keyfile failed new length", "")); 420 return ret; 421 } 422 423 if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { 424 ret = errno; 425 krb5_set_error_message (context, ret, 426 N_("seek to end: %s", ""), strerror(ret)); 427 goto out; 428 } 429 430 ret = krb5_store_int32(sp, entry->vno); 431 if(ret) { 432 krb5_set_error_message(context, ret, 433 N_("keytab keyfile failed store kvno", "")); 434 goto out; 435 } 436 ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 437 entry->keyblock.keyvalue.length); 438 if(ret != entry->keyblock.keyvalue.length) { 439 if (ret < 0) 440 ret = errno; 441 else 442 ret = ENOTTY; 443 krb5_set_error_message(context, ret, 444 N_("keytab keyfile failed to add key", "")); 445 goto out; 446 } 447 ret = 0; 448 out: 449 krb5_storage_free(sp); 450 close (fd); 451 return ret; 452 } 453 454 const krb5_kt_ops krb5_akf_ops = { 455 "AFSKEYFILE", 456 akf_resolve, 457 akf_get_name, 458 akf_close, 459 NULL, /* destroy */ 460 NULL, /* get */ 461 akf_start_seq_get, 462 akf_next_entry, 463 akf_end_seq_get, 464 akf_add_entry, 465 NULL /* remove */ 466 }; 467 468 #endif /* HEIMDAL_SMALLER */ 469