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