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 54struct 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 65static int 66get_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 131static krb5_error_code 132akf_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 167static krb5_error_code 168akf_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 182static krb5_error_code 183akf_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 198static krb5_error_code 199akf_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->sp = krb5_storage_from_fd(c->fd); 216 ret = krb5_ret_uint32(c->sp, &d->num_entries); 217 if(ret) { 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 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 entry->keyblock.keytype = ETYPE_DES_CBC_MD5; 259 entry->keyblock.keyvalue.length = 8; 260 entry->keyblock.keyvalue.data = malloc (8); 261 if (entry->keyblock.keyvalue.data == NULL) { 262 krb5_free_principal (context, entry->principal); 263 krb5_set_error_message(context, ENOMEM, 264 N_("malloc: out of memory", "")); 265 ret = ENOMEM; 266 goto out; 267 } 268 269 ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8); 270 if(ret != 8) 271 ret = (ret < 0) ? errno : KRB5_KT_END; 272 else 273 ret = 0; 274 275 entry->timestamp = time(NULL); 276 entry->flags = 0; 277 entry->aliases = NULL; 278 279 out: 280 krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET); 281 return ret; 282} 283 284static krb5_error_code 285akf_end_seq_get(krb5_context context, 286 krb5_keytab id, 287 krb5_kt_cursor *cursor) 288{ 289 krb5_storage_free(cursor->sp); 290 close(cursor->fd); 291 return 0; 292} 293 294static krb5_error_code 295akf_add_entry(krb5_context context, 296 krb5_keytab id, 297 krb5_keytab_entry *entry) 298{ 299 struct akf_data *d = id->data; 300 int fd, created = 0; 301 krb5_error_code ret; 302 int32_t len; 303 krb5_storage *sp; 304 305 306 if (entry->keyblock.keyvalue.length != 8) 307 return 0; 308 switch(entry->keyblock.keytype) { 309 case ETYPE_DES_CBC_CRC: 310 case ETYPE_DES_CBC_MD4: 311 case ETYPE_DES_CBC_MD5: 312 break; 313 default: 314 return 0; 315 } 316 317 fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC); 318 if (fd < 0) { 319 fd = open (d->filename, 320 O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); 321 if (fd < 0) { 322 ret = errno; 323 krb5_set_error_message(context, ret, 324 N_("open keyfile(%s): %s", ""), 325 d->filename, 326 strerror(ret)); 327 return ret; 328 } 329 created = 1; 330 } 331 332 sp = krb5_storage_from_fd(fd); 333 if(sp == NULL) { 334 close(fd); 335 krb5_set_error_message(context, ENOMEM, 336 N_("malloc: out of memory", "")); 337 return ENOMEM; 338 } 339 if (created) 340 len = 0; 341 else { 342 if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { 343 ret = errno; 344 krb5_storage_free(sp); 345 close(fd); 346 krb5_set_error_message(context, ret, 347 N_("seeking in keyfile: %s", ""), 348 strerror(ret)); 349 return ret; 350 } 351 352 ret = krb5_ret_int32(sp, &len); 353 if(ret) { 354 krb5_storage_free(sp); 355 close(fd); 356 return ret; 357 } 358 } 359 360 /* 361 * Make sure we don't add the entry twice, assumes the DES 362 * encryption types are all the same key. 363 */ 364 if (len > 0) { 365 int32_t kvno; 366 int i; 367 368 for (i = 0; i < len; i++) { 369 ret = krb5_ret_int32(sp, &kvno); 370 if (ret) { 371 krb5_set_error_message (context, ret, 372 N_("Failed getting kvno from keyfile", "")); 373 goto out; 374 } 375 if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) { 376 ret = errno; 377 krb5_set_error_message (context, ret, 378 N_("Failed seeing in keyfile: %s", ""), 379 strerror(ret)); 380 goto out; 381 } 382 if (kvno == entry->vno) { 383 ret = 0; 384 goto out; 385 } 386 } 387 } 388 389 len++; 390 391 if(krb5_storage_seek(sp, 0, SEEK_SET) < 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 399 ret = krb5_store_int32(sp, len); 400 if(ret) { 401 ret = errno; 402 krb5_set_error_message (context, ret, 403 N_("keytab keyfile failed new length", "")); 404 return ret; 405 } 406 407 if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { 408 ret = errno; 409 krb5_set_error_message (context, ret, 410 N_("seek to end: %s", ""), strerror(ret)); 411 goto out; 412 } 413 414 ret = krb5_store_int32(sp, entry->vno); 415 if(ret) { 416 krb5_set_error_message(context, ret, 417 N_("keytab keyfile failed store kvno", "")); 418 goto out; 419 } 420 ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 421 entry->keyblock.keyvalue.length); 422 if(ret != entry->keyblock.keyvalue.length) { 423 if (ret < 0) 424 ret = errno; 425 else 426 ret = ENOTTY; 427 krb5_set_error_message(context, ret, 428 N_("keytab keyfile failed to add key", "")); 429 goto out; 430 } 431 ret = 0; 432out: 433 krb5_storage_free(sp); 434 close (fd); 435 return ret; 436} 437 438const krb5_kt_ops krb5_akf_ops = { 439 "AFSKEYFILE", 440 akf_resolve, 441 akf_get_name, 442 akf_close, 443 NULL, /* destroy */ 444 NULL, /* get */ 445 akf_start_seq_get, 446 akf_next_entry, 447 akf_end_seq_get, 448 akf_add_entry, 449 NULL /* remove */ 450}; 451 452#endif /* HEIMDAL_SMALLER */ 453