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