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