155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
36178825SdfrRCSID("$Id: keytab_keyfile.c 20695 2007-05-30 14:09:09Z lha $");
3755682Smarkm
3855682Smarkm/* afs keyfile operations --------------------------------------- */
3955682Smarkm
4055682Smarkm/*
4155682Smarkm * Minimum tools to handle the AFS KeyFile.
4255682Smarkm *
4355682Smarkm * Format of the KeyFile is:
4455682Smarkm * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
4555682Smarkm *
4655682Smarkm * It just adds to the end of the keyfile, deleting isn't implemented.
4755682Smarkm * Use your favorite text/hex editor to delete keys.
4855682Smarkm *
4955682Smarkm */
5055682Smarkm
5155682Smarkm#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
5255682Smarkm#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
5355682Smarkm
5455682Smarkmstruct akf_data {
5555682Smarkm    int num_entries;
5655682Smarkm    char *filename;
5755682Smarkm    char *cell;
5855682Smarkm    char *realm;
5955682Smarkm};
6055682Smarkm
6155682Smarkm/*
6255682Smarkm * set `d->cell' and `d->realm'
6355682Smarkm */
6455682Smarkm
6555682Smarkmstatic int
66178825Sdfrget_cell_and_realm (krb5_context context, struct akf_data *d)
6755682Smarkm{
6855682Smarkm    FILE *f;
6955682Smarkm    char buf[BUFSIZ], *cp;
7078527Sassar    int ret;
7155682Smarkm
7255682Smarkm    f = fopen (AFS_SERVERTHISCELL, "r");
7378527Sassar    if (f == NULL) {
7478527Sassar	ret = errno;
7578527Sassar	krb5_set_error_string (context, "open %s: %s", AFS_SERVERTHISCELL,
7678527Sassar			       strerror(ret));
7778527Sassar	return ret;
7878527Sassar    }
7955682Smarkm    if (fgets (buf, sizeof(buf), f) == NULL) {
8055682Smarkm	fclose (f);
8178527Sassar	krb5_set_error_string (context, "no cell in %s", AFS_SERVERTHISCELL);
8255682Smarkm	return EINVAL;
8355682Smarkm    }
84107207Snectar    buf[strcspn(buf, "\n")] = '\0';
8555682Smarkm    fclose(f);
8655682Smarkm
8755682Smarkm    d->cell = strdup (buf);
8878527Sassar    if (d->cell == NULL) {
8978527Sassar	krb5_set_error_string (context, "malloc: out of memory");
9078527Sassar	return ENOMEM;
9178527Sassar    }
9255682Smarkm
9355682Smarkm    f = fopen (AFS_SERVERMAGICKRBCONF, "r");
9455682Smarkm    if (f != NULL) {
9555682Smarkm	if (fgets (buf, sizeof(buf), f) == NULL) {
96178825Sdfr	    free (d->cell);
97178825Sdfr	    d->cell = NULL;
9855682Smarkm	    fclose (f);
9978527Sassar	    krb5_set_error_string (context, "no realm in %s",
10078527Sassar				   AFS_SERVERMAGICKRBCONF);
10155682Smarkm	    return EINVAL;
10255682Smarkm	}
103107207Snectar	buf[strcspn(buf, "\n")] = '\0';
10455682Smarkm	fclose(f);
10555682Smarkm    }
10655682Smarkm    /* uppercase */
10755682Smarkm    for (cp = buf; *cp != '\0'; cp++)
108178825Sdfr	*cp = toupper((unsigned char)*cp);
10955682Smarkm
11055682Smarkm    d->realm = strdup (buf);
11155682Smarkm    if (d->realm == NULL) {
11255682Smarkm	free (d->cell);
113178825Sdfr	d->cell = NULL;
11478527Sassar	krb5_set_error_string (context, "malloc: out of memory");
11578527Sassar	return ENOMEM;
11655682Smarkm    }
11755682Smarkm    return 0;
11855682Smarkm}
11955682Smarkm
12055682Smarkm/*
12155682Smarkm * init and get filename
12255682Smarkm */
12355682Smarkm
12455682Smarkmstatic krb5_error_code
12555682Smarkmakf_resolve(krb5_context context, const char *name, krb5_keytab id)
12655682Smarkm{
12755682Smarkm    int ret;
12855682Smarkm    struct akf_data *d = malloc(sizeof (struct akf_data));
12955682Smarkm
13078527Sassar    if (d == NULL) {
13178527Sassar	krb5_set_error_string (context, "malloc: out of memory");
13278527Sassar	return ENOMEM;
13378527Sassar    }
13455682Smarkm
13555682Smarkm    d->num_entries = 0;
13678527Sassar    ret = get_cell_and_realm (context, d);
13755682Smarkm    if (ret) {
13855682Smarkm	free (d);
13955682Smarkm	return ret;
14055682Smarkm    }
14155682Smarkm    d->filename = strdup (name);
14255682Smarkm    if (d->filename == NULL) {
14355682Smarkm	free (d->cell);
14455682Smarkm	free (d->realm);
14555682Smarkm	free (d);
14678527Sassar	krb5_set_error_string (context, "malloc: out of memory");
14755682Smarkm	return ENOMEM;
14855682Smarkm    }
14955682Smarkm    id->data = d;
15055682Smarkm
15155682Smarkm    return 0;
15255682Smarkm}
15355682Smarkm
15455682Smarkm/*
15555682Smarkm * cleanup
15655682Smarkm */
15755682Smarkm
15855682Smarkmstatic krb5_error_code
15955682Smarkmakf_close(krb5_context context, krb5_keytab id)
16055682Smarkm{
16155682Smarkm    struct akf_data *d = id->data;
16255682Smarkm
16355682Smarkm    free (d->filename);
16455682Smarkm    free (d->cell);
16555682Smarkm    free (d);
16655682Smarkm    return 0;
16755682Smarkm}
16855682Smarkm
16955682Smarkm/*
17055682Smarkm * Return filename
17155682Smarkm */
17255682Smarkm
17355682Smarkmstatic krb5_error_code
17455682Smarkmakf_get_name(krb5_context context,
17555682Smarkm	     krb5_keytab id,
17655682Smarkm	     char *name,
17755682Smarkm	     size_t name_sz)
17855682Smarkm{
17955682Smarkm    struct akf_data *d = id->data;
18055682Smarkm
18155682Smarkm    strlcpy (name, d->filename, name_sz);
18255682Smarkm    return 0;
18355682Smarkm}
18455682Smarkm
18555682Smarkm/*
18655682Smarkm * Init
18755682Smarkm */
18855682Smarkm
18955682Smarkmstatic krb5_error_code
19055682Smarkmakf_start_seq_get(krb5_context context,
19155682Smarkm		  krb5_keytab id,
19255682Smarkm		  krb5_kt_cursor *c)
19355682Smarkm{
19455682Smarkm    int32_t ret;
19555682Smarkm    struct akf_data *d = id->data;
19655682Smarkm
19755682Smarkm    c->fd = open (d->filename, O_RDONLY|O_BINARY, 0600);
19878527Sassar    if (c->fd < 0) {
19978527Sassar	ret = errno;
20078527Sassar	krb5_set_error_string(context, "open(%s): %s", d->filename,
20178527Sassar			      strerror(ret));
20278527Sassar	return ret;
20378527Sassar    }
20455682Smarkm
20555682Smarkm    c->sp = krb5_storage_from_fd(c->fd);
20655682Smarkm    ret = krb5_ret_int32(c->sp, &d->num_entries);
20755682Smarkm    if(ret) {
20855682Smarkm	krb5_storage_free(c->sp);
20955682Smarkm	close(c->fd);
21078527Sassar	krb5_clear_error_string (context);
211102644Snectar	if(ret == KRB5_KT_END)
21278527Sassar	    return KRB5_KT_NOTFOUND;
21355682Smarkm	return ret;
21455682Smarkm    }
21555682Smarkm
21655682Smarkm    return 0;
21755682Smarkm}
21855682Smarkm
21955682Smarkmstatic krb5_error_code
22055682Smarkmakf_next_entry(krb5_context context,
22155682Smarkm	       krb5_keytab id,
22255682Smarkm	       krb5_keytab_entry *entry,
22355682Smarkm	       krb5_kt_cursor *cursor)
22455682Smarkm{
22555682Smarkm    struct akf_data *d = id->data;
22655682Smarkm    int32_t kvno;
22755682Smarkm    off_t pos;
22855682Smarkm    int ret;
22955682Smarkm
230102644Snectar    pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
23155682Smarkm
23255682Smarkm    if ((pos - 4) / (4 + 8) >= d->num_entries)
23355682Smarkm	return KRB5_KT_END;
23455682Smarkm
23555682Smarkm    ret = krb5_make_principal (context, &entry->principal,
23655682Smarkm			       d->realm, "afs", d->cell, NULL);
23755682Smarkm    if (ret)
23855682Smarkm	goto out;
23955682Smarkm
24055682Smarkm    ret = krb5_ret_int32(cursor->sp, &kvno);
24155682Smarkm    if (ret) {
24255682Smarkm	krb5_free_principal (context, entry->principal);
24355682Smarkm	goto out;
24455682Smarkm    }
24555682Smarkm
24672445Sassar    entry->vno = kvno;
24755682Smarkm
24855682Smarkm    entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
24955682Smarkm    entry->keyblock.keyvalue.length = 8;
25055682Smarkm    entry->keyblock.keyvalue.data   = malloc (8);
25155682Smarkm    if (entry->keyblock.keyvalue.data == NULL) {
25255682Smarkm	krb5_free_principal (context, entry->principal);
25378527Sassar	krb5_set_error_string (context, "malloc: out of memory");
25455682Smarkm	ret = ENOMEM;
25555682Smarkm	goto out;
25655682Smarkm    }
25755682Smarkm
258102644Snectar    ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
25955682Smarkm    if(ret != 8)
26055682Smarkm	ret = (ret < 0) ? errno : KRB5_KT_END;
26172445Sassar    else
26272445Sassar	ret = 0;
26355682Smarkm
26455682Smarkm    entry->timestamp = time(NULL);
26555682Smarkm
26655682Smarkm out:
267102644Snectar    krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
26855682Smarkm    return ret;
26955682Smarkm}
27055682Smarkm
27155682Smarkmstatic krb5_error_code
27255682Smarkmakf_end_seq_get(krb5_context context,
27355682Smarkm		krb5_keytab id,
27455682Smarkm		krb5_kt_cursor *cursor)
27555682Smarkm{
27655682Smarkm    krb5_storage_free(cursor->sp);
27755682Smarkm    close(cursor->fd);
27855682Smarkm    return 0;
27955682Smarkm}
28055682Smarkm
28155682Smarkmstatic krb5_error_code
28255682Smarkmakf_add_entry(krb5_context context,
28355682Smarkm	      krb5_keytab id,
28455682Smarkm	      krb5_keytab_entry *entry)
28555682Smarkm{
28655682Smarkm    struct akf_data *d = id->data;
28755682Smarkm    int fd, created = 0;
28872445Sassar    krb5_error_code ret;
28990926Snectar    int32_t len;
29090926Snectar    krb5_storage *sp;
29155682Smarkm
29290926Snectar
293178825Sdfr    if (entry->keyblock.keyvalue.length != 8)
29490926Snectar	return 0;
295178825Sdfr    switch(entry->keyblock.keytype) {
296178825Sdfr    case ETYPE_DES_CBC_CRC:
297178825Sdfr    case ETYPE_DES_CBC_MD4:
298178825Sdfr    case ETYPE_DES_CBC_MD5:
299178825Sdfr	break;
300178825Sdfr    default:
301178825Sdfr	return 0;
302178825Sdfr    }
30390926Snectar
30455682Smarkm    fd = open (d->filename, O_RDWR | O_BINARY);
30555682Smarkm    if (fd < 0) {
30655682Smarkm	fd = open (d->filename,
307103423Snectar		   O_RDWR | O_BINARY | O_CREAT | O_EXCL, 0600);
30878527Sassar	if (fd < 0) {
30978527Sassar	    ret = errno;
31078527Sassar	    krb5_set_error_string(context, "open(%s): %s", d->filename,
31178527Sassar				  strerror(ret));
31278527Sassar	    return ret;
31378527Sassar	}
31455682Smarkm	created = 1;
31555682Smarkm    }
31655682Smarkm
31790926Snectar    sp = krb5_storage_from_fd(fd);
31890926Snectar    if(sp == NULL) {
31990926Snectar	close(fd);
32090926Snectar	krb5_set_error_string (context, "malloc: out of memory");
32190926Snectar	return ENOMEM;
32290926Snectar    }
32390926Snectar    if (created)
32490926Snectar	len = 0;
32590926Snectar    else {
326102644Snectar	if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
32778527Sassar	    ret = errno;
32872445Sassar	    krb5_storage_free(sp);
32972445Sassar	    close(fd);
33078527Sassar	    krb5_set_error_string (context, "seek: %s", strerror(ret));
33178527Sassar	    return ret;
33272445Sassar	}
33390926Snectar
33490926Snectar	ret = krb5_ret_int32(sp, &len);
33572445Sassar	if(ret) {
33672445Sassar	    krb5_storage_free(sp);
33772445Sassar	    close(fd);
33872445Sassar	    return ret;
33972445Sassar	}
34090926Snectar    }
341178825Sdfr
342178825Sdfr    /*
343178825Sdfr     * Make sure we don't add the entry twice, assumes the DES
344178825Sdfr     * encryption types are all the same key.
345178825Sdfr     */
346178825Sdfr    if (len > 0) {
347178825Sdfr	int32_t kvno;
348178825Sdfr	int i;
349178825Sdfr
350178825Sdfr	for (i = 0; i < len; i++) {
351178825Sdfr	    ret = krb5_ret_int32(sp, &kvno);
352178825Sdfr	    if (ret) {
353178825Sdfr		krb5_set_error_string (context, "Failed to get kvno ");
354178825Sdfr		goto out;
355178825Sdfr	    }
356178825Sdfr	    if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
357178825Sdfr		krb5_set_error_string (context, "seek: %s", strerror(ret));
358178825Sdfr		goto out;
359178825Sdfr	    }
360178825Sdfr	    if (kvno == entry->vno) {
361178825Sdfr		ret = 0;
362178825Sdfr		goto out;
363178825Sdfr	    }
364178825Sdfr	}
365178825Sdfr    }
366178825Sdfr
36790926Snectar    len++;
36890926Snectar
369102644Snectar    if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
37090926Snectar	ret = errno;
37190926Snectar	krb5_set_error_string (context, "seek: %s", strerror(ret));
372178825Sdfr	goto out;
37390926Snectar    }
37490926Snectar
37590926Snectar    ret = krb5_store_int32(sp, len);
37690926Snectar    if(ret) {
377178825Sdfr	krb5_set_error_string(context, "keytab keyfile failed new length");
37890926Snectar	return ret;
37990926Snectar    }
38055682Smarkm
381102644Snectar    if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
38290926Snectar	ret = errno;
383178825Sdfr	krb5_set_error_string (context, "seek to end: %s", strerror(ret));
384178825Sdfr	goto out;
38590926Snectar    }
38672445Sassar
38790926Snectar    ret = krb5_store_int32(sp, entry->vno);
38890926Snectar    if(ret) {
389178825Sdfr	krb5_set_error_string(context, "keytab keyfile failed store kvno");
390178825Sdfr	goto out;
39155682Smarkm    }
392102644Snectar    ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
393102644Snectar			     entry->keyblock.keyvalue.length);
39490926Snectar    if(ret != entry->keyblock.keyvalue.length) {
395178825Sdfr	if (ret < 0)
396178825Sdfr	    ret = errno;
397178825Sdfr	else
398178825Sdfr	    ret = ENOTTY;
399178825Sdfr	krb5_set_error_string(context, "keytab keyfile failed to add key");
400178825Sdfr	goto out;
40190926Snectar    }
402178825Sdfr    ret = 0;
403178825Sdfrout:
40490926Snectar    krb5_storage_free(sp);
40555682Smarkm    close (fd);
406178825Sdfr    return ret;
40755682Smarkm}
40855682Smarkm
40955682Smarkmconst krb5_kt_ops krb5_akf_ops = {
41055682Smarkm    "AFSKEYFILE",
41155682Smarkm    akf_resolve,
41255682Smarkm    akf_get_name,
41355682Smarkm    akf_close,
41455682Smarkm    NULL, /* get */
41555682Smarkm    akf_start_seq_get,
41655682Smarkm    akf_next_entry,
41755682Smarkm    akf_end_seq_get,
41855682Smarkm    akf_add_entry,
41955682Smarkm    NULL /* remove */
42055682Smarkm};
421