1 /* $NetBSD: keytab_keyfile.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "krb5_locl.h" 37 38 #ifndef HEIMDAL_SMALLER 39 40 /* afs keyfile operations --------------------------------------- */ 41 42 /* 43 * Minimum tools to handle the AFS KeyFile. 44 * 45 * Format of the KeyFile is: 46 * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys} 47 * 48 * It just adds to the end of the keyfile, deleting isn't implemented. 49 * Use your favorite text/hex editor to delete keys. 50 * 51 */ 52 53 #define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell" 54 #define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf" 55 56 struct akf_data { 57 uint32_t num_entries; 58 char *filename; 59 char *cell; 60 char *realm; 61 }; 62 63 /* 64 * set `d->cell' and `d->realm' 65 */ 66 67 static int 68 get_cell_and_realm (krb5_context context, struct akf_data *d) 69 { 70 FILE *f; 71 char buf[BUFSIZ], *cp; 72 int ret; 73 74 f = fopen (AFS_SERVERTHISCELL, "r"); 75 if (f == NULL) { 76 ret = errno; 77 krb5_set_error_message (context, ret, 78 N_("Open ThisCell %s: %s", ""), 79 AFS_SERVERTHISCELL, 80 strerror(ret)); 81 return ret; 82 } 83 if (fgets (buf, sizeof(buf), f) == NULL) { 84 fclose (f); 85 krb5_set_error_message (context, EINVAL, 86 N_("No cell in ThisCell file %s", ""), 87 AFS_SERVERTHISCELL); 88 return EINVAL; 89 } 90 buf[strcspn(buf, "\n")] = '\0'; 91 fclose(f); 92 93 d->cell = strdup (buf); 94 if (d->cell == NULL) { 95 krb5_set_error_message(context, ENOMEM, 96 N_("malloc: out of memory", "")); 97 return ENOMEM; 98 } 99 100 f = fopen (AFS_SERVERMAGICKRBCONF, "r"); 101 if (f != NULL) { 102 if (fgets (buf, sizeof(buf), f) == NULL) { 103 free (d->cell); 104 d->cell = NULL; 105 fclose (f); 106 krb5_set_error_message (context, EINVAL, 107 N_("No realm in ThisCell file %s", ""), 108 AFS_SERVERMAGICKRBCONF); 109 return EINVAL; 110 } 111 buf[strcspn(buf, "\n")] = '\0'; 112 fclose(f); 113 } 114 /* uppercase */ 115 for (cp = buf; *cp != '\0'; cp++) 116 *cp = toupper((unsigned char)*cp); 117 118 d->realm = strdup (buf); 119 if (d->realm == NULL) { 120 free (d->cell); 121 d->cell = NULL; 122 krb5_set_error_message(context, ENOMEM, 123 N_("malloc: out of memory", "")); 124 return ENOMEM; 125 } 126 return 0; 127 } 128 129 /* 130 * init and get filename 131 */ 132 133 static krb5_error_code KRB5_CALLCONV 134 akf_resolve(krb5_context context, const char *name, krb5_keytab id) 135 { 136 int ret; 137 struct akf_data *d = malloc(sizeof (struct akf_data)); 138 139 if (d == NULL) { 140 krb5_set_error_message(context, ENOMEM, 141 N_("malloc: out of memory", "")); 142 return ENOMEM; 143 } 144 145 d->num_entries = 0; 146 ret = get_cell_and_realm (context, d); 147 if (ret) { 148 free (d); 149 return ret; 150 } 151 d->filename = strdup (name); 152 if (d->filename == NULL) { 153 free (d->cell); 154 free (d->realm); 155 free (d); 156 krb5_set_error_message(context, ENOMEM, 157 N_("malloc: out of memory", "")); 158 return ENOMEM; 159 } 160 id->data = d; 161 162 return 0; 163 } 164 165 /* 166 * cleanup 167 */ 168 169 static krb5_error_code KRB5_CALLCONV 170 akf_close(krb5_context context, krb5_keytab id) 171 { 172 struct akf_data *d = id->data; 173 174 free (d->filename); 175 free (d->cell); 176 free (d); 177 return 0; 178 } 179 180 /* 181 * Return filename 182 */ 183 184 static krb5_error_code KRB5_CALLCONV 185 akf_get_name(krb5_context context, 186 krb5_keytab id, 187 char *name, 188 size_t name_sz) 189 { 190 struct akf_data *d = id->data; 191 192 strlcpy (name, d->filename, name_sz); 193 return 0; 194 } 195 196 /* 197 * Init 198 */ 199 200 static krb5_error_code KRB5_CALLCONV 201 akf_start_seq_get(krb5_context context, 202 krb5_keytab id, 203 krb5_kt_cursor *c) 204 { 205 int32_t ret; 206 struct akf_data *d = id->data; 207 208 c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600); 209 if (c->fd < 0) { 210 ret = errno; 211 krb5_set_error_message(context, ret, 212 N_("keytab afs keyfile open %s failed: %s", ""), 213 d->filename, strerror(ret)); 214 return ret; 215 } 216 217 c->data = NULL; 218 c->sp = krb5_storage_from_fd(c->fd); 219 if (c->sp == NULL) { 220 close(c->fd); 221 krb5_clear_error_message (context); 222 return KRB5_KT_NOTFOUND; 223 } 224 krb5_storage_set_eof_code(c->sp, KRB5_KT_END); 225 226 ret = krb5_ret_uint32(c->sp, &d->num_entries); 227 if(ret || d->num_entries > INT_MAX / 8) { 228 krb5_storage_free(c->sp); 229 close(c->fd); 230 krb5_clear_error_message (context); 231 if(ret == KRB5_KT_END) 232 return KRB5_KT_NOTFOUND; 233 return ret; 234 } 235 236 return 0; 237 } 238 239 static krb5_error_code KRB5_CALLCONV 240 akf_next_entry(krb5_context context, 241 krb5_keytab id, 242 krb5_keytab_entry *entry, 243 krb5_kt_cursor *cursor) 244 { 245 struct akf_data *d = id->data; 246 int32_t kvno; 247 off_t pos; 248 int ret; 249 250 pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); 251 252 if ((pos - 4) / (4 + 8) >= d->num_entries) 253 return KRB5_KT_END; 254 255 ret = krb5_make_principal (context, &entry->principal, 256 d->realm, "afs", d->cell, NULL); 257 if (ret) 258 goto out; 259 260 ret = krb5_ret_int32(cursor->sp, &kvno); 261 if (ret) { 262 krb5_free_principal (context, entry->principal); 263 goto out; 264 } 265 266 entry->vno = kvno; 267 268 if (cursor->data) 269 entry->keyblock.keytype = ETYPE_DES_CBC_MD5; 270 else 271 entry->keyblock.keytype = ETYPE_DES_CBC_CRC; 272 entry->keyblock.keyvalue.length = 8; 273 entry->keyblock.keyvalue.data = malloc (8); 274 if (entry->keyblock.keyvalue.data == NULL) { 275 krb5_free_principal (context, entry->principal); 276 krb5_set_error_message(context, ENOMEM, 277 N_("malloc: out of memory", "")); 278 ret = ENOMEM; 279 goto out; 280 } 281 282 ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8); 283 if(ret != 8) 284 ret = (ret < 0) ? errno : KRB5_KT_END; 285 else 286 ret = 0; 287 288 entry->timestamp = time(NULL); 289 entry->flags = 0; 290 entry->aliases = NULL; 291 292 out: 293 if (cursor->data) { 294 krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET); 295 cursor->data = NULL; 296 } else 297 cursor->data = cursor; 298 return ret; 299 } 300 301 static krb5_error_code KRB5_CALLCONV 302 akf_end_seq_get(krb5_context context, 303 krb5_keytab id, 304 krb5_kt_cursor *cursor) 305 { 306 krb5_storage_free(cursor->sp); 307 close(cursor->fd); 308 cursor->data = NULL; 309 return 0; 310 } 311 312 static krb5_error_code KRB5_CALLCONV 313 akf_add_entry(krb5_context context, 314 krb5_keytab id, 315 krb5_keytab_entry *entry) 316 { 317 struct akf_data *d = id->data; 318 int fd, created = 0; 319 krb5_error_code ret; 320 int32_t len; 321 krb5_storage *sp; 322 323 324 if (entry->keyblock.keyvalue.length != 8) 325 return 0; 326 switch(entry->keyblock.keytype) { 327 case ETYPE_DES_CBC_CRC: 328 case ETYPE_DES_CBC_MD4: 329 case ETYPE_DES_CBC_MD5: 330 break; 331 default: 332 return 0; 333 } 334 335 fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC); 336 if (fd < 0) { 337 fd = open (d->filename, 338 O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); 339 if (fd < 0) { 340 ret = errno; 341 krb5_set_error_message(context, ret, 342 N_("open keyfile(%s): %s", ""), 343 d->filename, 344 strerror(ret)); 345 return ret; 346 } 347 created = 1; 348 } 349 350 sp = krb5_storage_from_fd(fd); 351 if(sp == NULL) { 352 close(fd); 353 krb5_set_error_message(context, ENOMEM, 354 N_("malloc: out of memory", "")); 355 return ENOMEM; 356 } 357 if (created) 358 len = 0; 359 else { 360 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 361 ret = errno; 362 krb5_storage_free(sp); 363 close(fd); 364 krb5_set_error_message(context, ret, 365 N_("seeking in keyfile: %s", ""), 366 strerror(ret)); 367 return ret; 368 } 369 370 ret = krb5_ret_int32(sp, &len); 371 if(ret) { 372 krb5_storage_free(sp); 373 close(fd); 374 return ret; 375 } 376 } 377 378 /* 379 * Make sure we don't add the entry twice, assumes the DES 380 * encryption types are all the same key. 381 */ 382 if (len > 0) { 383 int32_t kvno; 384 int i; 385 386 for (i = 0; i < len; i++) { 387 ret = krb5_ret_int32(sp, &kvno); 388 if (ret) { 389 krb5_set_error_message (context, ret, 390 N_("Failed getting kvno from keyfile", "")); 391 goto out; 392 } 393 if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) { 394 ret = errno; 395 krb5_set_error_message (context, ret, 396 N_("Failed seeing in keyfile: %s", ""), 397 strerror(ret)); 398 goto out; 399 } 400 if (kvno == entry->vno) { 401 ret = 0; 402 goto out; 403 } 404 } 405 } 406 407 len++; 408 409 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 410 ret = errno; 411 krb5_set_error_message (context, ret, 412 N_("Failed seeing in keyfile: %s", ""), 413 strerror(ret)); 414 goto out; 415 } 416 417 ret = krb5_store_int32(sp, len); 418 if(ret) { 419 ret = errno; 420 krb5_set_error_message (context, ret, 421 N_("keytab keyfile failed new length", "")); 422 return ret; 423 } 424 425 if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { 426 ret = errno; 427 krb5_set_error_message (context, ret, 428 N_("seek to end: %s", ""), strerror(ret)); 429 goto out; 430 } 431 432 ret = krb5_store_int32(sp, entry->vno); 433 if(ret) { 434 krb5_set_error_message(context, ret, 435 N_("keytab keyfile failed store kvno", "")); 436 goto out; 437 } 438 ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 439 entry->keyblock.keyvalue.length); 440 if(ret != entry->keyblock.keyvalue.length) { 441 if (ret < 0) 442 ret = errno; 443 else 444 ret = ENOTTY; 445 krb5_set_error_message(context, ret, 446 N_("keytab keyfile failed to add key", "")); 447 goto out; 448 } 449 ret = 0; 450 out: 451 krb5_storage_free(sp); 452 close (fd); 453 return ret; 454 } 455 456 const krb5_kt_ops krb5_akf_ops = { 457 "AFSKEYFILE", 458 akf_resolve, 459 akf_get_name, 460 akf_close, 461 NULL, /* destroy */ 462 NULL, /* get */ 463 akf_start_seq_get, 464 akf_next_entry, 465 akf_end_seq_get, 466 akf_add_entry, 467 NULL /* remove */ 468 }; 469 470 #endif /* HEIMDAL_SMALLER */ 471