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