Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: keytab_keyfile.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2007 Kungliga Tekniska Hgskolan
      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 	return krb5_enomem(context);
     96 
     97     f = fopen (AFS_SERVERMAGICKRBCONF, "r");
     98     if (f != NULL) {
     99 	if (fgets (buf, sizeof(buf), f) == NULL) {
    100 	    free (d->cell);
    101 	    d->cell = NULL;
    102 	    fclose (f);
    103 	    krb5_set_error_message (context, EINVAL,
    104 				    N_("No realm in ThisCell file %s", ""),
    105 				    AFS_SERVERMAGICKRBCONF);
    106 	    return EINVAL;
    107 	}
    108 	buf[strcspn(buf, "\n")] = '\0';
    109 	fclose(f);
    110     }
    111     /* uppercase */
    112     for (cp = buf; *cp != '\0'; cp++)
    113 	*cp = toupper((unsigned char)*cp);
    114 
    115     d->realm = strdup (buf);
    116     if (d->realm == NULL) {
    117 	free (d->cell);
    118 	d->cell = NULL;
    119 	return krb5_enomem(context);
    120     }
    121     return 0;
    122 }
    123 
    124 /*
    125  * init and get filename
    126  */
    127 
    128 static krb5_error_code KRB5_CALLCONV
    129 akf_resolve(krb5_context context, const char *name, krb5_keytab id)
    130 {
    131     int ret;
    132     struct akf_data *d = calloc(1, sizeof (struct akf_data));
    133 
    134     if (d == NULL)
    135 	return krb5_enomem(context);
    136 
    137     d->num_entries = 0;
    138     ret = get_cell_and_realm (context, d);
    139     if (ret) {
    140 	free (d);
    141 	return ret;
    142     }
    143     d->filename = strdup (name);
    144     if (d->filename == NULL) {
    145 	free (d->cell);
    146 	free (d->realm);
    147 	free (d);
    148 	return krb5_enomem(context);
    149     }
    150     id->data = d;
    151 
    152     return 0;
    153 }
    154 
    155 /*
    156  * cleanup
    157  */
    158 
    159 static krb5_error_code KRB5_CALLCONV
    160 akf_close(krb5_context context, krb5_keytab id)
    161 {
    162     struct akf_data *d = id->data;
    163 
    164     free (d->filename);
    165     free (d->cell);
    166     free (d);
    167     return 0;
    168 }
    169 
    170 /*
    171  * Return filename
    172  */
    173 
    174 static krb5_error_code KRB5_CALLCONV
    175 akf_get_name(krb5_context context,
    176 	     krb5_keytab id,
    177 	     char *name,
    178 	     size_t name_sz)
    179 {
    180     struct akf_data *d = id->data;
    181 
    182     strlcpy (name, d->filename, name_sz);
    183     return 0;
    184 }
    185 
    186 /*
    187  * Init
    188  */
    189 
    190 static krb5_error_code KRB5_CALLCONV
    191 akf_start_seq_get(krb5_context context,
    192 		  krb5_keytab id,
    193 		  krb5_kt_cursor *c)
    194 {
    195     int32_t ret;
    196     struct akf_data *d = id->data;
    197 
    198     c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
    199     if (c->fd < 0) {
    200 	ret = errno;
    201 	krb5_set_error_message(context, ret,
    202 			       N_("keytab afs keyfile open %s failed: %s", ""),
    203 			       d->filename, strerror(ret));
    204 	return ret;
    205     }
    206 
    207     c->data = NULL;
    208     c->sp = krb5_storage_from_fd(c->fd);
    209     if (c->sp == NULL) {
    210 	close(c->fd);
    211 	krb5_clear_error_message (context);
    212 	return KRB5_KT_NOTFOUND;
    213     }
    214     krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
    215 
    216     ret = krb5_ret_uint32(c->sp, &d->num_entries);
    217     if(ret || d->num_entries > INT_MAX / 8) {
    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 
    229 static krb5_error_code KRB5_CALLCONV
    230 akf_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     if (cursor->data)
    259 	entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
    260     else
    261 	entry->keyblock.keytype         = ETYPE_DES_CBC_CRC;
    262     entry->keyblock.keyvalue.length = 8;
    263     entry->keyblock.keyvalue.data   = malloc (8);
    264     if (entry->keyblock.keyvalue.data == NULL) {
    265 	krb5_free_principal (context, entry->principal);
    266 	ret = krb5_enomem(context);
    267 	goto out;
    268     }
    269 
    270     ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
    271     if(ret != 8)
    272 	ret = (ret < 0) ? errno : KRB5_KT_END;
    273     else
    274 	ret = 0;
    275 
    276     entry->timestamp = time(NULL);
    277     entry->flags = 0;
    278     entry->aliases = NULL;
    279 
    280  out:
    281     if (cursor->data) {
    282 	krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
    283 	cursor->data = NULL;
    284     } else
    285 	cursor->data = cursor;
    286     return ret;
    287 }
    288 
    289 static krb5_error_code KRB5_CALLCONV
    290 akf_end_seq_get(krb5_context context,
    291 		krb5_keytab id,
    292 		krb5_kt_cursor *cursor)
    293 {
    294     krb5_storage_free(cursor->sp);
    295     close(cursor->fd);
    296     cursor->data = NULL;
    297     return 0;
    298 }
    299 
    300 static krb5_error_code KRB5_CALLCONV
    301 akf_add_entry(krb5_context context,
    302 	      krb5_keytab id,
    303 	      krb5_keytab_entry *entry)
    304 {
    305     struct akf_data *d = id->data;
    306     int fd, created = 0;
    307     krb5_error_code ret;
    308     int32_t len;
    309     krb5_storage *sp;
    310 
    311 
    312     if (entry->keyblock.keyvalue.length != 8)
    313 	return 0;
    314     switch(entry->keyblock.keytype) {
    315     case ETYPE_DES_CBC_CRC:
    316     case ETYPE_DES_CBC_MD4:
    317     case ETYPE_DES_CBC_MD5:
    318 	break;
    319     default:
    320 	return 0;
    321     }
    322 
    323     fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
    324     if (fd < 0) {
    325 	fd = open (d->filename,
    326 		   O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
    327 	if (fd < 0) {
    328 	    ret = errno;
    329 	    krb5_set_error_message(context, ret,
    330 				   N_("open keyfile(%s): %s", ""),
    331 				   d->filename,
    332 				   strerror(ret));
    333 	    return ret;
    334 	}
    335 	created = 1;
    336     }
    337 
    338     sp = krb5_storage_from_fd(fd);
    339     if(sp == NULL) {
    340 	close(fd);
    341 	return krb5_enomem(context);
    342     }
    343     if (created)
    344 	len = 0;
    345     else {
    346 	if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
    347 	    ret = errno;
    348 	    krb5_storage_free(sp);
    349 	    close(fd);
    350 	    krb5_set_error_message(context, ret,
    351 				   N_("seeking in keyfile: %s", ""),
    352 				   strerror(ret));
    353 	    return ret;
    354 	}
    355 
    356 	ret = krb5_ret_int32(sp, &len);
    357 	if(ret) {
    358 	    krb5_storage_free(sp);
    359 	    close(fd);
    360 	    return ret;
    361 	}
    362     }
    363 
    364     /*
    365      * Make sure we don't add the entry twice, assumes the DES
    366      * encryption types are all the same key.
    367      */
    368     if (len > 0) {
    369 	int32_t kvno;
    370 	int i;
    371 
    372 	for (i = 0; i < len; i++) {
    373 	    ret = krb5_ret_int32(sp, &kvno);
    374 	    if (ret) {
    375 		krb5_set_error_message (context, ret,
    376 					N_("Failed getting kvno from keyfile", ""));
    377 		goto out;
    378 	    }
    379 	    if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
    380 		ret = errno;
    381 		krb5_set_error_message (context, ret,
    382 					N_("Failed seeing in keyfile: %s", ""),
    383 					strerror(ret));
    384 		goto out;
    385 	    }
    386 	    if (kvno == entry->vno) {
    387 		ret = 0;
    388 		goto out;
    389 	    }
    390 	}
    391     }
    392 
    393     len++;
    394 
    395     if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
    396 	ret = errno;
    397 	krb5_set_error_message (context, ret,
    398 				N_("Failed seeing in keyfile: %s", ""),
    399 				strerror(ret));
    400 	goto out;
    401     }
    402 
    403     ret = krb5_store_int32(sp, len);
    404     if(ret) {
    405 	ret = errno;
    406 	krb5_set_error_message (context, ret,
    407 				N_("keytab keyfile failed new length", ""));
    408 	return ret;
    409     }
    410 
    411     if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
    412 	ret = errno;
    413 	krb5_set_error_message (context, ret,
    414 				N_("seek to end: %s", ""), strerror(ret));
    415 	goto out;
    416     }
    417 
    418     ret = krb5_store_int32(sp, entry->vno);
    419     if(ret) {
    420 	krb5_set_error_message(context, ret,
    421 			       N_("keytab keyfile failed store kvno", ""));
    422 	goto out;
    423     }
    424     ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
    425 			     entry->keyblock.keyvalue.length);
    426     if(ret != entry->keyblock.keyvalue.length) {
    427 	if (ret < 0)
    428 	    ret = errno;
    429 	else
    430 	    ret = ENOTTY;
    431 	krb5_set_error_message(context, ret,
    432 			       N_("keytab keyfile failed to add key", ""));
    433 	goto out;
    434     }
    435     ret = 0;
    436 out:
    437     krb5_storage_free(sp);
    438     close (fd);
    439     return ret;
    440 }
    441 
    442 const krb5_kt_ops krb5_akf_ops = {
    443     "AFSKEYFILE",
    444     akf_resolve,
    445     akf_get_name,
    446     akf_close,
    447     NULL, /* destroy */
    448     NULL, /* get */
    449     akf_start_seq_get,
    450     akf_next_entry,
    451     akf_end_seq_get,
    452     akf_add_entry,
    453     NULL, /* remove */
    454     NULL,
    455     0
    456 };
    457 
    458 #endif /* HEIMDAL_SMALLER */
    459