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