Home | History | Annotate | Line # | Download | only in krb5
      1  1.1     elric /*	$NetBSD: keytab_keyfile.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
      2  1.1     elric 
      3  1.1     elric /*
      4  1.1     elric  * Copyright (c) 1997 - 2007 Kungliga Tekniska Hgskolan
      5  1.1     elric  * (Royal Institute of Technology, Stockholm, Sweden).
      6  1.1     elric  * All rights reserved.
      7  1.1     elric  *
      8  1.1     elric  * Redistribution and use in source and binary forms, with or without
      9  1.1     elric  * modification, are permitted provided that the following conditions
     10  1.1     elric  * are met:
     11  1.1     elric  *
     12  1.1     elric  * 1. Redistributions of source code must retain the above copyright
     13  1.1     elric  *    notice, this list of conditions and the following disclaimer.
     14  1.1     elric  *
     15  1.1     elric  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1     elric  *    notice, this list of conditions and the following disclaimer in the
     17  1.1     elric  *    documentation and/or other materials provided with the distribution.
     18  1.1     elric  *
     19  1.1     elric  * 3. Neither the name of the Institute nor the names of its contributors
     20  1.1     elric  *    may be used to endorse or promote products derived from this software
     21  1.1     elric  *    without specific prior written permission.
     22  1.1     elric  *
     23  1.1     elric  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  1.1     elric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  1.1     elric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  1.1     elric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  1.1     elric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  1.1     elric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  1.1     elric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  1.1     elric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  1.1     elric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  1.1     elric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  1.1     elric  * SUCH DAMAGE.
     34  1.1     elric  */
     35  1.1     elric 
     36  1.1     elric #include "krb5_locl.h"
     37  1.1     elric 
     38  1.1     elric #ifndef HEIMDAL_SMALLER
     39  1.1     elric 
     40  1.1     elric /* afs keyfile operations --------------------------------------- */
     41  1.1     elric 
     42  1.1     elric /*
     43  1.1     elric  * Minimum tools to handle the AFS KeyFile.
     44  1.1     elric  *
     45  1.1     elric  * Format of the KeyFile is:
     46  1.1     elric  * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
     47  1.1     elric  *
     48  1.1     elric  * It just adds to the end of the keyfile, deleting isn't implemented.
     49  1.1     elric  * Use your favorite text/hex editor to delete keys.
     50  1.1     elric  *
     51  1.1     elric  */
     52  1.1     elric 
     53  1.1     elric #define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
     54  1.1     elric #define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
     55  1.1     elric 
     56  1.1     elric struct akf_data {
     57  1.1     elric     uint32_t num_entries;
     58  1.1     elric     char *filename;
     59  1.1     elric     char *cell;
     60  1.1     elric     char *realm;
     61  1.1     elric };
     62  1.1     elric 
     63  1.1     elric /*
     64  1.1     elric  * set `d->cell' and `d->realm'
     65  1.1     elric  */
     66  1.1     elric 
     67  1.1     elric static int
     68  1.1     elric get_cell_and_realm (krb5_context context, struct akf_data *d)
     69  1.1     elric {
     70  1.1     elric     FILE *f;
     71  1.1     elric     char buf[BUFSIZ], *cp;
     72  1.1     elric     int ret;
     73  1.1     elric 
     74  1.1     elric     f = fopen (AFS_SERVERTHISCELL, "r");
     75  1.1     elric     if (f == NULL) {
     76  1.1     elric 	ret = errno;
     77  1.1     elric 	krb5_set_error_message (context, ret,
     78  1.1     elric 				N_("Open ThisCell %s: %s", ""),
     79  1.1     elric 				AFS_SERVERTHISCELL,
     80  1.1     elric 				strerror(ret));
     81  1.1     elric 	return ret;
     82  1.1     elric     }
     83  1.1     elric     if (fgets (buf, sizeof(buf), f) == NULL) {
     84  1.1     elric 	fclose (f);
     85  1.1     elric 	krb5_set_error_message (context, EINVAL,
     86  1.1     elric 				N_("No cell in ThisCell file %s", ""),
     87  1.1     elric 				AFS_SERVERTHISCELL);
     88  1.1     elric 	return EINVAL;
     89  1.1     elric     }
     90  1.1     elric     buf[strcspn(buf, "\n")] = '\0';
     91  1.1     elric     fclose(f);
     92  1.1     elric 
     93  1.1     elric     d->cell = strdup (buf);
     94  1.2  christos     if (d->cell == NULL)
     95  1.2  christos 	return krb5_enomem(context);
     96  1.1     elric 
     97  1.1     elric     f = fopen (AFS_SERVERMAGICKRBCONF, "r");
     98  1.1     elric     if (f != NULL) {
     99  1.1     elric 	if (fgets (buf, sizeof(buf), f) == NULL) {
    100  1.1     elric 	    free (d->cell);
    101  1.1     elric 	    d->cell = NULL;
    102  1.1     elric 	    fclose (f);
    103  1.1     elric 	    krb5_set_error_message (context, EINVAL,
    104  1.1     elric 				    N_("No realm in ThisCell file %s", ""),
    105  1.1     elric 				    AFS_SERVERMAGICKRBCONF);
    106  1.1     elric 	    return EINVAL;
    107  1.1     elric 	}
    108  1.1     elric 	buf[strcspn(buf, "\n")] = '\0';
    109  1.1     elric 	fclose(f);
    110  1.1     elric     }
    111  1.1     elric     /* uppercase */
    112  1.1     elric     for (cp = buf; *cp != '\0'; cp++)
    113  1.1     elric 	*cp = toupper((unsigned char)*cp);
    114  1.1     elric 
    115  1.1     elric     d->realm = strdup (buf);
    116  1.1     elric     if (d->realm == NULL) {
    117  1.1     elric 	free (d->cell);
    118  1.1     elric 	d->cell = NULL;
    119  1.2  christos 	return krb5_enomem(context);
    120  1.1     elric     }
    121  1.1     elric     return 0;
    122  1.1     elric }
    123  1.1     elric 
    124  1.1     elric /*
    125  1.1     elric  * init and get filename
    126  1.1     elric  */
    127  1.1     elric 
    128  1.1     elric static krb5_error_code KRB5_CALLCONV
    129  1.1     elric akf_resolve(krb5_context context, const char *name, krb5_keytab id)
    130  1.1     elric {
    131  1.1     elric     int ret;
    132  1.2  christos     struct akf_data *d = calloc(1, sizeof (struct akf_data));
    133  1.1     elric 
    134  1.2  christos     if (d == NULL)
    135  1.2  christos 	return krb5_enomem(context);
    136  1.1     elric 
    137  1.1     elric     d->num_entries = 0;
    138  1.1     elric     ret = get_cell_and_realm (context, d);
    139  1.1     elric     if (ret) {
    140  1.1     elric 	free (d);
    141  1.1     elric 	return ret;
    142  1.1     elric     }
    143  1.1     elric     d->filename = strdup (name);
    144  1.1     elric     if (d->filename == NULL) {
    145  1.1     elric 	free (d->cell);
    146  1.1     elric 	free (d->realm);
    147  1.1     elric 	free (d);
    148  1.2  christos 	return krb5_enomem(context);
    149  1.1     elric     }
    150  1.1     elric     id->data = d;
    151  1.1     elric 
    152  1.1     elric     return 0;
    153  1.1     elric }
    154  1.1     elric 
    155  1.1     elric /*
    156  1.1     elric  * cleanup
    157  1.1     elric  */
    158  1.1     elric 
    159  1.1     elric static krb5_error_code KRB5_CALLCONV
    160  1.1     elric akf_close(krb5_context context, krb5_keytab id)
    161  1.1     elric {
    162  1.1     elric     struct akf_data *d = id->data;
    163  1.1     elric 
    164  1.1     elric     free (d->filename);
    165  1.1     elric     free (d->cell);
    166  1.1     elric     free (d);
    167  1.1     elric     return 0;
    168  1.1     elric }
    169  1.1     elric 
    170  1.1     elric /*
    171  1.1     elric  * Return filename
    172  1.1     elric  */
    173  1.1     elric 
    174  1.1     elric static krb5_error_code KRB5_CALLCONV
    175  1.1     elric akf_get_name(krb5_context context,
    176  1.1     elric 	     krb5_keytab id,
    177  1.1     elric 	     char *name,
    178  1.1     elric 	     size_t name_sz)
    179  1.1     elric {
    180  1.1     elric     struct akf_data *d = id->data;
    181  1.1     elric 
    182  1.1     elric     strlcpy (name, d->filename, name_sz);
    183  1.1     elric     return 0;
    184  1.1     elric }
    185  1.1     elric 
    186  1.1     elric /*
    187  1.1     elric  * Init
    188  1.1     elric  */
    189  1.1     elric 
    190  1.1     elric static krb5_error_code KRB5_CALLCONV
    191  1.1     elric akf_start_seq_get(krb5_context context,
    192  1.1     elric 		  krb5_keytab id,
    193  1.1     elric 		  krb5_kt_cursor *c)
    194  1.1     elric {
    195  1.1     elric     int32_t ret;
    196  1.1     elric     struct akf_data *d = id->data;
    197  1.1     elric 
    198  1.1     elric     c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
    199  1.1     elric     if (c->fd < 0) {
    200  1.1     elric 	ret = errno;
    201  1.1     elric 	krb5_set_error_message(context, ret,
    202  1.1     elric 			       N_("keytab afs keyfile open %s failed: %s", ""),
    203  1.1     elric 			       d->filename, strerror(ret));
    204  1.1     elric 	return ret;
    205  1.1     elric     }
    206  1.1     elric 
    207  1.2  christos     c->data = NULL;
    208  1.1     elric     c->sp = krb5_storage_from_fd(c->fd);
    209  1.2  christos     if (c->sp == NULL) {
    210  1.2  christos 	close(c->fd);
    211  1.2  christos 	krb5_clear_error_message (context);
    212  1.2  christos 	return KRB5_KT_NOTFOUND;
    213  1.2  christos     }
    214  1.2  christos     krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
    215  1.2  christos 
    216  1.1     elric     ret = krb5_ret_uint32(c->sp, &d->num_entries);
    217  1.2  christos     if(ret || d->num_entries > INT_MAX / 8) {
    218  1.1     elric 	krb5_storage_free(c->sp);
    219  1.1     elric 	close(c->fd);
    220  1.1     elric 	krb5_clear_error_message (context);
    221  1.1     elric 	if(ret == KRB5_KT_END)
    222  1.1     elric 	    return KRB5_KT_NOTFOUND;
    223  1.1     elric 	return ret;
    224  1.1     elric     }
    225  1.1     elric 
    226  1.1     elric     return 0;
    227  1.1     elric }
    228  1.1     elric 
    229  1.1     elric static krb5_error_code KRB5_CALLCONV
    230  1.1     elric akf_next_entry(krb5_context context,
    231  1.1     elric 	       krb5_keytab id,
    232  1.1     elric 	       krb5_keytab_entry *entry,
    233  1.1     elric 	       krb5_kt_cursor *cursor)
    234  1.1     elric {
    235  1.1     elric     struct akf_data *d = id->data;
    236  1.1     elric     int32_t kvno;
    237  1.1     elric     off_t pos;
    238  1.1     elric     int ret;
    239  1.1     elric 
    240  1.1     elric     pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
    241  1.1     elric 
    242  1.1     elric     if ((pos - 4) / (4 + 8) >= d->num_entries)
    243  1.1     elric 	return KRB5_KT_END;
    244  1.1     elric 
    245  1.1     elric     ret = krb5_make_principal (context, &entry->principal,
    246  1.1     elric 			       d->realm, "afs", d->cell, NULL);
    247  1.1     elric     if (ret)
    248  1.1     elric 	goto out;
    249  1.1     elric 
    250  1.1     elric     ret = krb5_ret_int32(cursor->sp, &kvno);
    251  1.1     elric     if (ret) {
    252  1.1     elric 	krb5_free_principal (context, entry->principal);
    253  1.1     elric 	goto out;
    254  1.1     elric     }
    255  1.1     elric 
    256  1.1     elric     entry->vno = kvno;
    257  1.1     elric 
    258  1.2  christos     if (cursor->data)
    259  1.2  christos 	entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
    260  1.2  christos     else
    261  1.2  christos 	entry->keyblock.keytype         = ETYPE_DES_CBC_CRC;
    262  1.1     elric     entry->keyblock.keyvalue.length = 8;
    263  1.1     elric     entry->keyblock.keyvalue.data   = malloc (8);
    264  1.1     elric     if (entry->keyblock.keyvalue.data == NULL) {
    265  1.1     elric 	krb5_free_principal (context, entry->principal);
    266  1.2  christos 	ret = krb5_enomem(context);
    267  1.1     elric 	goto out;
    268  1.1     elric     }
    269  1.1     elric 
    270  1.1     elric     ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
    271  1.1     elric     if(ret != 8)
    272  1.1     elric 	ret = (ret < 0) ? errno : KRB5_KT_END;
    273  1.1     elric     else
    274  1.1     elric 	ret = 0;
    275  1.1     elric 
    276  1.1     elric     entry->timestamp = time(NULL);
    277  1.1     elric     entry->flags = 0;
    278  1.1     elric     entry->aliases = NULL;
    279  1.1     elric 
    280  1.1     elric  out:
    281  1.2  christos     if (cursor->data) {
    282  1.2  christos 	krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
    283  1.2  christos 	cursor->data = NULL;
    284  1.2  christos     } else
    285  1.2  christos 	cursor->data = cursor;
    286  1.1     elric     return ret;
    287  1.1     elric }
    288  1.1     elric 
    289  1.1     elric static krb5_error_code KRB5_CALLCONV
    290  1.1     elric akf_end_seq_get(krb5_context context,
    291  1.1     elric 		krb5_keytab id,
    292  1.1     elric 		krb5_kt_cursor *cursor)
    293  1.1     elric {
    294  1.1     elric     krb5_storage_free(cursor->sp);
    295  1.1     elric     close(cursor->fd);
    296  1.2  christos     cursor->data = NULL;
    297  1.1     elric     return 0;
    298  1.1     elric }
    299  1.1     elric 
    300  1.1     elric static krb5_error_code KRB5_CALLCONV
    301  1.1     elric akf_add_entry(krb5_context context,
    302  1.1     elric 	      krb5_keytab id,
    303  1.1     elric 	      krb5_keytab_entry *entry)
    304  1.1     elric {
    305  1.1     elric     struct akf_data *d = id->data;
    306  1.1     elric     int fd, created = 0;
    307  1.1     elric     krb5_error_code ret;
    308  1.1     elric     int32_t len;
    309  1.1     elric     krb5_storage *sp;
    310  1.1     elric 
    311  1.1     elric 
    312  1.1     elric     if (entry->keyblock.keyvalue.length != 8)
    313  1.1     elric 	return 0;
    314  1.1     elric     switch(entry->keyblock.keytype) {
    315  1.1     elric     case ETYPE_DES_CBC_CRC:
    316  1.1     elric     case ETYPE_DES_CBC_MD4:
    317  1.1     elric     case ETYPE_DES_CBC_MD5:
    318  1.1     elric 	break;
    319  1.1     elric     default:
    320  1.1     elric 	return 0;
    321  1.1     elric     }
    322  1.1     elric 
    323  1.1     elric     fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
    324  1.1     elric     if (fd < 0) {
    325  1.1     elric 	fd = open (d->filename,
    326  1.1     elric 		   O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
    327  1.1     elric 	if (fd < 0) {
    328  1.1     elric 	    ret = errno;
    329  1.1     elric 	    krb5_set_error_message(context, ret,
    330  1.1     elric 				   N_("open keyfile(%s): %s", ""),
    331  1.1     elric 				   d->filename,
    332  1.1     elric 				   strerror(ret));
    333  1.1     elric 	    return ret;
    334  1.1     elric 	}
    335  1.1     elric 	created = 1;
    336  1.1     elric     }
    337  1.1     elric 
    338  1.1     elric     sp = krb5_storage_from_fd(fd);
    339  1.1     elric     if(sp == NULL) {
    340  1.1     elric 	close(fd);
    341  1.2  christos 	return krb5_enomem(context);
    342  1.1     elric     }
    343  1.1     elric     if (created)
    344  1.1     elric 	len = 0;
    345  1.1     elric     else {
    346  1.1     elric 	if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
    347  1.1     elric 	    ret = errno;
    348  1.1     elric 	    krb5_storage_free(sp);
    349  1.1     elric 	    close(fd);
    350  1.1     elric 	    krb5_set_error_message(context, ret,
    351  1.1     elric 				   N_("seeking in keyfile: %s", ""),
    352  1.1     elric 				   strerror(ret));
    353  1.1     elric 	    return ret;
    354  1.1     elric 	}
    355  1.2  christos 
    356  1.1     elric 	ret = krb5_ret_int32(sp, &len);
    357  1.1     elric 	if(ret) {
    358  1.1     elric 	    krb5_storage_free(sp);
    359  1.1     elric 	    close(fd);
    360  1.1     elric 	    return ret;
    361  1.1     elric 	}
    362  1.1     elric     }
    363  1.1     elric 
    364  1.1     elric     /*
    365  1.1     elric      * Make sure we don't add the entry twice, assumes the DES
    366  1.1     elric      * encryption types are all the same key.
    367  1.1     elric      */
    368  1.1     elric     if (len > 0) {
    369  1.1     elric 	int32_t kvno;
    370  1.1     elric 	int i;
    371  1.1     elric 
    372  1.1     elric 	for (i = 0; i < len; i++) {
    373  1.1     elric 	    ret = krb5_ret_int32(sp, &kvno);
    374  1.1     elric 	    if (ret) {
    375  1.1     elric 		krb5_set_error_message (context, ret,
    376  1.1     elric 					N_("Failed getting kvno from keyfile", ""));
    377  1.1     elric 		goto out;
    378  1.1     elric 	    }
    379  1.1     elric 	    if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
    380  1.1     elric 		ret = errno;
    381  1.1     elric 		krb5_set_error_message (context, ret,
    382  1.1     elric 					N_("Failed seeing in keyfile: %s", ""),
    383  1.1     elric 					strerror(ret));
    384  1.1     elric 		goto out;
    385  1.1     elric 	    }
    386  1.1     elric 	    if (kvno == entry->vno) {
    387  1.1     elric 		ret = 0;
    388  1.1     elric 		goto out;
    389  1.1     elric 	    }
    390  1.1     elric 	}
    391  1.1     elric     }
    392  1.1     elric 
    393  1.1     elric     len++;
    394  1.2  christos 
    395  1.1     elric     if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
    396  1.1     elric 	ret = errno;
    397  1.1     elric 	krb5_set_error_message (context, ret,
    398  1.1     elric 				N_("Failed seeing in keyfile: %s", ""),
    399  1.1     elric 				strerror(ret));
    400  1.1     elric 	goto out;
    401  1.1     elric     }
    402  1.2  christos 
    403  1.1     elric     ret = krb5_store_int32(sp, len);
    404  1.1     elric     if(ret) {
    405  1.1     elric 	ret = errno;
    406  1.1     elric 	krb5_set_error_message (context, ret,
    407  1.1     elric 				N_("keytab keyfile failed new length", ""));
    408  1.1     elric 	return ret;
    409  1.1     elric     }
    410  1.1     elric 
    411  1.1     elric     if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
    412  1.1     elric 	ret = errno;
    413  1.1     elric 	krb5_set_error_message (context, ret,
    414  1.1     elric 				N_("seek to end: %s", ""), strerror(ret));
    415  1.1     elric 	goto out;
    416  1.1     elric     }
    417  1.2  christos 
    418  1.1     elric     ret = krb5_store_int32(sp, entry->vno);
    419  1.1     elric     if(ret) {
    420  1.1     elric 	krb5_set_error_message(context, ret,
    421  1.1     elric 			       N_("keytab keyfile failed store kvno", ""));
    422  1.1     elric 	goto out;
    423  1.1     elric     }
    424  1.1     elric     ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
    425  1.1     elric 			     entry->keyblock.keyvalue.length);
    426  1.1     elric     if(ret != entry->keyblock.keyvalue.length) {
    427  1.1     elric 	if (ret < 0)
    428  1.1     elric 	    ret = errno;
    429  1.1     elric 	else
    430  1.1     elric 	    ret = ENOTTY;
    431  1.1     elric 	krb5_set_error_message(context, ret,
    432  1.1     elric 			       N_("keytab keyfile failed to add key", ""));
    433  1.1     elric 	goto out;
    434  1.1     elric     }
    435  1.1     elric     ret = 0;
    436  1.1     elric out:
    437  1.1     elric     krb5_storage_free(sp);
    438  1.1     elric     close (fd);
    439  1.1     elric     return ret;
    440  1.1     elric }
    441  1.1     elric 
    442  1.1     elric const krb5_kt_ops krb5_akf_ops = {
    443  1.1     elric     "AFSKEYFILE",
    444  1.1     elric     akf_resolve,
    445  1.1     elric     akf_get_name,
    446  1.1     elric     akf_close,
    447  1.1     elric     NULL, /* destroy */
    448  1.1     elric     NULL, /* get */
    449  1.1     elric     akf_start_seq_get,
    450  1.1     elric     akf_next_entry,
    451  1.1     elric     akf_end_seq_get,
    452  1.1     elric     akf_add_entry,
    453  1.2  christos     NULL, /* remove */
    454  1.2  christos     NULL,
    455  1.2  christos     0
    456  1.1     elric };
    457  1.1     elric 
    458  1.1     elric #endif /* HEIMDAL_SMALLER */
    459