1 /* 2 * Copyright (c) 1997 - 2002 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,v 1.12 2002/02/15 14:32:52 joda Exp $"); 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, 67 struct akf_data *d) 68 { 69 FILE *f; 70 char buf[BUFSIZ], *cp; 71 int ret; 72 73 f = fopen (AFS_SERVERTHISCELL, "r"); 74 if (f == NULL) { 75 ret = errno; 76 krb5_set_error_string (context, "open %s: %s", AFS_SERVERTHISCELL, 77 strerror(ret)); 78 return ret; 79 } 80 if (fgets (buf, sizeof(buf), f) == NULL) { 81 fclose (f); 82 krb5_set_error_string (context, "no cell in %s", AFS_SERVERTHISCELL); 83 return EINVAL; 84 } 85 if (buf[strlen(buf) - 1] == '\n') 86 buf[strlen(buf) - 1] = '\0'; 87 fclose(f); 88 89 d->cell = strdup (buf); 90 if (d->cell == NULL) { 91 krb5_set_error_string (context, "malloc: out of memory"); 92 return ENOMEM; 93 } 94 95 f = fopen (AFS_SERVERMAGICKRBCONF, "r"); 96 if (f != NULL) { 97 if (fgets (buf, sizeof(buf), f) == NULL) { 98 fclose (f); 99 krb5_set_error_string (context, "no realm in %s", 100 AFS_SERVERMAGICKRBCONF); 101 return EINVAL; 102 } 103 if (buf[strlen(buf)-1] == '\n') 104 buf[strlen(buf)-1] = '\0'; 105 fclose(f); 106 } 107 /* uppercase */ 108 for (cp = buf; *cp != '\0'; cp++) 109 *cp = toupper(*cp); 110 111 d->realm = strdup (buf); 112 if (d->realm == NULL) { 113 free (d->cell); 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_CC_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 = cursor->sp->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 = cursor->sp->fetch(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 cursor->sp->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 || entry->keyblock.keytype != ETYPE_DES_CBC_MD5) 295 return 0; 296 297 fd = open (d->filename, O_RDWR | O_BINARY); 298 if (fd < 0) { 299 fd = open (d->filename, 300 O_RDWR | O_BINARY | O_CREAT, 0600); 301 if (fd < 0) { 302 ret = errno; 303 krb5_set_error_string(context, "open(%s): %s", d->filename, 304 strerror(ret)); 305 return ret; 306 } 307 created = 1; 308 } 309 310 sp = krb5_storage_from_fd(fd); 311 if(sp == NULL) { 312 close(fd); 313 krb5_set_error_string (context, "malloc: out of memory"); 314 return ENOMEM; 315 } 316 if (created) 317 len = 0; 318 else { 319 if((*sp->seek)(sp, 0, SEEK_SET) < 0) { 320 ret = errno; 321 krb5_storage_free(sp); 322 close(fd); 323 krb5_set_error_string (context, "seek: %s", strerror(ret)); 324 return ret; 325 } 326 327 ret = krb5_ret_int32(sp, &len); 328 if(ret) { 329 krb5_storage_free(sp); 330 close(fd); 331 return ret; 332 } 333 } 334 len++; 335 336 if((*sp->seek)(sp, 0, SEEK_SET) < 0) { 337 ret = errno; 338 krb5_storage_free(sp); 339 close(fd); 340 krb5_set_error_string (context, "seek: %s", strerror(ret)); 341 return ret; 342 } 343 344 ret = krb5_store_int32(sp, len); 345 if(ret) { 346 krb5_storage_free(sp); 347 close(fd); 348 return ret; 349 } 350 351 352 if((*sp->seek)(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { 353 ret = errno; 354 krb5_storage_free(sp); 355 close(fd); 356 krb5_set_error_string (context, "seek: %s", strerror(ret)); 357 return ret; 358 } 359 360 ret = krb5_store_int32(sp, entry->vno); 361 if(ret) { 362 krb5_storage_free(sp); 363 close(fd); 364 return ret; 365 } 366 ret = sp->store(sp, entry->keyblock.keyvalue.data, 367 entry->keyblock.keyvalue.length); 368 if(ret != entry->keyblock.keyvalue.length) { 369 krb5_storage_free(sp); 370 close(fd); 371 if(ret < 0) 372 return errno; 373 return ENOTTY; 374 } 375 krb5_storage_free(sp); 376 close (fd); 377 return 0; 378 } 379 380 const krb5_kt_ops krb5_akf_ops = { 381 "AFSKEYFILE", 382 akf_resolve, 383 akf_get_name, 384 akf_close, 385 NULL, /* get */ 386 akf_start_seq_get, 387 akf_next_entry, 388 akf_end_seq_get, 389 akf_add_entry, 390 NULL /* remove */ 391 }; 392