155682Smarkm/*
290926Snectar * Copyright (c) 1997 - 2002 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_krb4.c 17046 2006-04-10 17:10:53Z lha $");
3755682Smarkm
3855682Smarkmstruct krb4_kt_data {
3955682Smarkm    char *filename;
4055682Smarkm};
4155682Smarkm
4255682Smarkmstatic krb5_error_code
4355682Smarkmkrb4_kt_resolve(krb5_context context, const char *name, krb5_keytab id)
4455682Smarkm{
4555682Smarkm    struct krb4_kt_data *d;
4655682Smarkm
4755682Smarkm    d = malloc (sizeof(*d));
4878527Sassar    if (d == NULL) {
4978527Sassar	krb5_set_error_string (context, "malloc: out of memory");
5055682Smarkm	return ENOMEM;
5178527Sassar    }
5255682Smarkm    d->filename = strdup (name);
5355682Smarkm    if (d->filename == NULL) {
5455682Smarkm	free(d);
5578527Sassar	krb5_set_error_string (context, "malloc: out of memory");
5655682Smarkm	return ENOMEM;
5755682Smarkm    }
5855682Smarkm    id->data = d;
5955682Smarkm    return 0;
6055682Smarkm}
6155682Smarkm
6255682Smarkmstatic krb5_error_code
6355682Smarkmkrb4_kt_get_name (krb5_context context,
6455682Smarkm		  krb5_keytab id,
6555682Smarkm		  char *name,
6655682Smarkm		  size_t name_sz)
6755682Smarkm{
6855682Smarkm    struct krb4_kt_data *d = id->data;
6955682Smarkm
7055682Smarkm    strlcpy (name, d->filename, name_sz);
7155682Smarkm    return 0;
7255682Smarkm}
7355682Smarkm
7455682Smarkmstatic krb5_error_code
7555682Smarkmkrb4_kt_close (krb5_context context,
7655682Smarkm	       krb5_keytab id)
7755682Smarkm{
7855682Smarkm    struct krb4_kt_data *d = id->data;
7955682Smarkm
8055682Smarkm    free (d->filename);
8155682Smarkm    free (d);
8255682Smarkm    return 0;
8355682Smarkm}
8455682Smarkm
8555682Smarkmstruct krb4_cursor_extra_data {
8655682Smarkm    krb5_keytab_entry entry;
8755682Smarkm    int num;
8855682Smarkm};
8955682Smarkm
9090926Snectarstatic int
9190926Snectaropen_flock(const char *filename, int flags, int mode)
9290926Snectar{
9390926Snectar    int lock_mode;
9490926Snectar    int tries = 0;
9590926Snectar    int fd = open(filename, flags, mode);
9690926Snectar    if(fd < 0)
9790926Snectar	return fd;
9890926Snectar    if((flags & O_ACCMODE) == O_RDONLY)
9990926Snectar	lock_mode = LOCK_SH | LOCK_NB;
10090926Snectar    else
10190926Snectar	lock_mode = LOCK_EX | LOCK_NB;
10290926Snectar    while(flock(fd, lock_mode) < 0) {
10390926Snectar	if(++tries < 5) {
10490926Snectar	    sleep(1);
10590926Snectar	} else {
10690926Snectar	    close(fd);
10790926Snectar	    return -1;
10890926Snectar	}
10990926Snectar    }
11090926Snectar    return fd;
11190926Snectar}
11290926Snectar
11390926Snectar
11490926Snectar
11555682Smarkmstatic krb5_error_code
11655682Smarkmkrb4_kt_start_seq_get_int (krb5_context context,
11755682Smarkm			   krb5_keytab id,
11855682Smarkm			   int flags,
11955682Smarkm			   krb5_kt_cursor *c)
12055682Smarkm{
12155682Smarkm    struct krb4_kt_data *d = id->data;
12255682Smarkm    struct krb4_cursor_extra_data *ed;
12378527Sassar    int ret;
12455682Smarkm
12555682Smarkm    ed = malloc (sizeof(*ed));
12678527Sassar    if (ed == NULL) {
12778527Sassar	krb5_set_error_string (context, "malloc: out of memory");
12855682Smarkm	return ENOMEM;
12978527Sassar    }
13055682Smarkm    ed->entry.principal = NULL;
13155682Smarkm    ed->num = -1;
13255682Smarkm    c->data = ed;
13390926Snectar    c->fd = open_flock (d->filename, flags, 0);
13455682Smarkm    if (c->fd < 0) {
13578527Sassar	ret = errno;
13655682Smarkm	free (ed);
13778527Sassar	krb5_set_error_string(context, "open(%s): %s", d->filename,
13878527Sassar			      strerror(ret));
13978527Sassar	return ret;
14055682Smarkm    }
14155682Smarkm    c->sp = krb5_storage_from_fd(c->fd);
142178825Sdfr    if(c->sp == NULL) {
143178825Sdfr	close(c->fd);
144178825Sdfr	free(ed);
145178825Sdfr	return ENOMEM;
146178825Sdfr    }
147102644Snectar    krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
14855682Smarkm    return 0;
14955682Smarkm}
15055682Smarkm
15155682Smarkmstatic krb5_error_code
15255682Smarkmkrb4_kt_start_seq_get (krb5_context context,
15355682Smarkm		       krb5_keytab id,
15455682Smarkm		       krb5_kt_cursor *c)
15555682Smarkm{
15655682Smarkm    return krb4_kt_start_seq_get_int (context, id, O_BINARY | O_RDONLY, c);
15755682Smarkm}
15855682Smarkm
15955682Smarkmstatic krb5_error_code
16055682Smarkmread_v4_entry (krb5_context context,
16155682Smarkm	       struct krb4_kt_data *d,
16255682Smarkm	       krb5_kt_cursor *c,
16355682Smarkm	       struct krb4_cursor_extra_data *ed)
16455682Smarkm{
165178825Sdfr    unsigned char des_key[8];
16655682Smarkm    krb5_error_code ret;
16755682Smarkm    char *service, *instance, *realm;
16855682Smarkm    int8_t kvno;
16955682Smarkm
17055682Smarkm    ret = krb5_ret_stringz(c->sp, &service);
17155682Smarkm    if (ret)
17255682Smarkm	return ret;
17355682Smarkm    ret = krb5_ret_stringz(c->sp, &instance);
17455682Smarkm    if (ret) {
17555682Smarkm	free (service);
17655682Smarkm	return ret;
17755682Smarkm    }
17855682Smarkm    ret = krb5_ret_stringz(c->sp, &realm);
17955682Smarkm    if (ret) {
18055682Smarkm	free (service);
18155682Smarkm	free (instance);
18255682Smarkm	return ret;
18355682Smarkm    }
18455682Smarkm    ret = krb5_425_conv_principal (context, service, instance, realm,
18555682Smarkm				   &ed->entry.principal);
18655682Smarkm    free (service);
18755682Smarkm    free (instance);
18855682Smarkm    free (realm);
18955682Smarkm    if (ret)
19055682Smarkm	return ret;
19155682Smarkm    ret = krb5_ret_int8(c->sp, &kvno);
19255682Smarkm    if (ret) {
19355682Smarkm	krb5_free_principal (context, ed->entry.principal);
19455682Smarkm	return ret;
19555682Smarkm    }
196178825Sdfr    ret = krb5_storage_read(c->sp, des_key, sizeof(des_key));
19755682Smarkm    if (ret < 0) {
19855682Smarkm	krb5_free_principal(context, ed->entry.principal);
19955682Smarkm	return ret;
20055682Smarkm    }
20155682Smarkm    if (ret < 8) {
20255682Smarkm	krb5_free_principal(context, ed->entry.principal);
20355682Smarkm	return EINVAL;
20455682Smarkm    }
20555682Smarkm    ed->entry.vno = kvno;
20655682Smarkm    ret = krb5_data_copy (&ed->entry.keyblock.keyvalue,
207178825Sdfr			  des_key, sizeof(des_key));
20855682Smarkm    if (ret)
20955682Smarkm	return ret;
21055682Smarkm    ed->entry.timestamp = time(NULL);
21155682Smarkm    ed->num = 0;
21255682Smarkm    return 0;
21355682Smarkm}
21455682Smarkm
21555682Smarkmstatic krb5_error_code
21655682Smarkmkrb4_kt_next_entry (krb5_context context,
21755682Smarkm		    krb5_keytab id,
21855682Smarkm		    krb5_keytab_entry *entry,
21955682Smarkm		    krb5_kt_cursor *c)
22055682Smarkm{
22155682Smarkm    krb5_error_code ret;
22255682Smarkm    struct krb4_kt_data *d = id->data;
22355682Smarkm    struct krb4_cursor_extra_data *ed = c->data;
22455682Smarkm    const krb5_enctype keytypes[] = {ETYPE_DES_CBC_MD5,
22555682Smarkm				     ETYPE_DES_CBC_MD4,
22655682Smarkm				     ETYPE_DES_CBC_CRC};
22755682Smarkm
22855682Smarkm    if (ed->num == -1) {
22955682Smarkm	ret = read_v4_entry (context, d, c, ed);
23055682Smarkm	if (ret)
23155682Smarkm	    return ret;
23255682Smarkm    }
23355682Smarkm    ret = krb5_kt_copy_entry_contents (context,
23455682Smarkm				       &ed->entry,
23555682Smarkm				       entry);
23655682Smarkm    if (ret)
23755682Smarkm	return ret;
23855682Smarkm    entry->keyblock.keytype = keytypes[ed->num];
23955682Smarkm    if (++ed->num == 3) {
24055682Smarkm	krb5_kt_free_entry (context, &ed->entry);
24155682Smarkm	ed->num = -1;
24255682Smarkm    }
24355682Smarkm    return 0;
24455682Smarkm}
24555682Smarkm
24655682Smarkmstatic krb5_error_code
24755682Smarkmkrb4_kt_end_seq_get (krb5_context context,
24855682Smarkm		     krb5_keytab id,
24955682Smarkm		     krb5_kt_cursor *c)
25055682Smarkm{
25155682Smarkm    struct krb4_cursor_extra_data *ed = c->data;
25255682Smarkm
25355682Smarkm    krb5_storage_free (c->sp);
25455682Smarkm    if (ed->num != -1)
25555682Smarkm	krb5_kt_free_entry (context, &ed->entry);
25655682Smarkm    free (c->data);
25755682Smarkm    close (c->fd);
25855682Smarkm    return 0;
25955682Smarkm}
26055682Smarkm
26155682Smarkmstatic krb5_error_code
26290926Snectarkrb4_store_keytab_entry(krb5_context context,
26390926Snectar			krb5_keytab_entry *entry,
26490926Snectar			krb5_storage *sp)
26555682Smarkm{
26655682Smarkm    krb5_error_code ret;
26772445Sassar#define ANAME_SZ 40
26872445Sassar#define INST_SZ 40
26972445Sassar#define REALM_SZ 40
27055682Smarkm    char service[ANAME_SZ];
27155682Smarkm    char instance[INST_SZ];
27255682Smarkm    char realm[REALM_SZ];
27390926Snectar    ret = krb5_524_conv_principal (context, entry->principal,
27490926Snectar				   service, instance, realm);
27590926Snectar    if (ret)
27690926Snectar	return ret;
27790926Snectar    if (entry->keyblock.keyvalue.length == 8
27890926Snectar	&& entry->keyblock.keytype == ETYPE_DES_CBC_MD5) {
27990926Snectar	ret = krb5_store_stringz(sp, service);
28090926Snectar	ret = krb5_store_stringz(sp, instance);
28190926Snectar	ret = krb5_store_stringz(sp, realm);
28290926Snectar	ret = krb5_store_int8(sp, entry->vno);
283102644Snectar	ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, 8);
28490926Snectar    }
28590926Snectar    return 0;
28690926Snectar}
28755682Smarkm
28890926Snectarstatic krb5_error_code
28990926Snectarkrb4_kt_add_entry (krb5_context context,
29090926Snectar		   krb5_keytab id,
29190926Snectar		   krb5_keytab_entry *entry)
29290926Snectar{
29390926Snectar    struct krb4_kt_data *d = id->data;
29490926Snectar    krb5_storage *sp;
29590926Snectar    krb5_error_code ret;
29690926Snectar    int fd;
29790926Snectar
29890926Snectar    fd = open_flock (d->filename, O_WRONLY | O_APPEND | O_BINARY, 0);
29955682Smarkm    if (fd < 0) {
30090926Snectar	fd = open_flock (d->filename,
30155682Smarkm		   O_WRONLY | O_APPEND | O_BINARY | O_CREAT, 0600);
30278527Sassar	if (fd < 0) {
30378527Sassar	    ret = errno;
30478527Sassar	    krb5_set_error_string(context, "open(%s): %s", d->filename,
30578527Sassar				  strerror(ret));
30678527Sassar	    return ret;
30778527Sassar	}
30855682Smarkm    }
30990926Snectar    sp = krb5_storage_from_fd(fd);
31090926Snectar    if(sp == NULL) {
31190926Snectar	close(fd);
31290926Snectar	return ENOMEM;
31355682Smarkm    }
314178825Sdfr    krb5_storage_set_eof_code(sp, KRB5_KT_END);
31590926Snectar    ret = krb4_store_keytab_entry(context, entry, sp);
31690926Snectar    krb5_storage_free(sp);
31790926Snectar    if(close (fd) < 0)
31890926Snectar	return errno;
31990926Snectar    return ret;
32090926Snectar}
32190926Snectar
32290926Snectarstatic krb5_error_code
32390926Snectarkrb4_kt_remove_entry(krb5_context context,
324178825Sdfr		     krb5_keytab id,
325178825Sdfr		     krb5_keytab_entry *entry)
32690926Snectar{
32790926Snectar    struct krb4_kt_data *d = id->data;
32890926Snectar    krb5_error_code ret;
32990926Snectar    krb5_keytab_entry e;
33090926Snectar    krb5_kt_cursor cursor;
33190926Snectar    krb5_storage *sp;
33290926Snectar    int remove_flag = 0;
33390926Snectar
33490926Snectar    sp = krb5_storage_emem();
335178825Sdfr    if (sp == NULL) {
336178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
337178825Sdfr	return ENOMEM;
338178825Sdfr    }
33990926Snectar    ret = krb5_kt_start_seq_get(context, id, &cursor);
340178825Sdfr    if (ret) {
341178825Sdfr	krb5_storage_free(sp);
342178825Sdfr	return ret;
343178825Sdfr    }
34490926Snectar    while(krb5_kt_next_entry(context, id, &e, &cursor) == 0) {
34590926Snectar	if(!krb5_kt_compare(context, &e, entry->principal,
34690926Snectar			    entry->vno, entry->keyblock.keytype)) {
34790926Snectar	    ret = krb4_store_keytab_entry(context, &e, sp);
34890926Snectar	    if(ret) {
349178825Sdfr		krb5_kt_free_entry(context, &e);
35090926Snectar		krb5_storage_free(sp);
35190926Snectar		return ret;
35290926Snectar	    }
35390926Snectar	} else
35490926Snectar	    remove_flag = 1;
355178825Sdfr	krb5_kt_free_entry(context, &e);
35655682Smarkm    }
35790926Snectar    krb5_kt_end_seq_get(context, id, &cursor);
35890926Snectar    if(remove_flag) {
35990926Snectar	int fd;
36090926Snectar	unsigned char buf[1024];
36190926Snectar	ssize_t n;
36290926Snectar	krb5_data data;
36390926Snectar	struct stat st;
36490926Snectar
36590926Snectar	krb5_storage_to_data(sp, &data);
36690926Snectar	krb5_storage_free(sp);
36790926Snectar
36890926Snectar	fd = open_flock (d->filename, O_RDWR | O_BINARY, 0);
36990926Snectar	if(fd < 0) {
37090926Snectar	    memset(data.data, 0, data.length);
37190926Snectar	    krb5_data_free(&data);
37290926Snectar	    if(errno == EACCES || errno == EROFS)
37390926Snectar		return KRB5_KT_NOWRITE;
37490926Snectar	    return errno;
37590926Snectar	}
37690926Snectar
37790926Snectar	if(write(fd, data.data, data.length) != data.length) {
37890926Snectar	    memset(data.data, 0, data.length);
379178825Sdfr	    krb5_data_free(&data);
38090926Snectar	    close(fd);
38190926Snectar	    krb5_set_error_string(context, "failed writing to \"%s\"", d->filename);
38290926Snectar	    return errno;
38390926Snectar	}
38490926Snectar	memset(data.data, 0, data.length);
38590926Snectar	if(fstat(fd, &st) < 0) {
386178825Sdfr	    krb5_data_free(&data);
38790926Snectar	    close(fd);
38890926Snectar	    krb5_set_error_string(context, "failed getting size of \"%s\"", d->filename);
38990926Snectar	    return errno;
39090926Snectar	}
39190926Snectar	st.st_size -= data.length;
39290926Snectar	memset(buf, 0, sizeof(buf));
39390926Snectar	while(st.st_size > 0) {
39490926Snectar	    n = min(st.st_size, sizeof(buf));
39590926Snectar	    n = write(fd, buf, n);
39690926Snectar	    if(n <= 0) {
397178825Sdfr		krb5_data_free(&data);
39890926Snectar		close(fd);
39990926Snectar		krb5_set_error_string(context, "failed writing to \"%s\"", d->filename);
40090926Snectar		return errno;
40190926Snectar
40290926Snectar	    }
40390926Snectar	    st.st_size -= n;
40490926Snectar	}
40590926Snectar	if(ftruncate(fd, data.length) < 0) {
406178825Sdfr	    krb5_data_free(&data);
40790926Snectar	    close(fd);
40890926Snectar	    krb5_set_error_string(context, "failed truncating \"%s\"", d->filename);
40990926Snectar	    return errno;
41090926Snectar	}
41190926Snectar	krb5_data_free(&data);
41290926Snectar	if(close(fd) < 0) {
41390926Snectar	    krb5_set_error_string(context, "error closing \"%s\"", d->filename);
41490926Snectar	    return errno;
41590926Snectar	}
41690926Snectar	return 0;
417178825Sdfr    } else {
418178825Sdfr	krb5_storage_free(sp);
41990926Snectar	return KRB5_KT_NOTFOUND;
420178825Sdfr    }
42155682Smarkm}
42255682Smarkm
42390926Snectar
42472445Sassarconst krb5_kt_ops krb4_fkt_ops = {
42555682Smarkm    "krb4",
42655682Smarkm    krb4_kt_resolve,
42755682Smarkm    krb4_kt_get_name,
42855682Smarkm    krb4_kt_close,
42955682Smarkm    NULL,			/* get */
43055682Smarkm    krb4_kt_start_seq_get,
43155682Smarkm    krb4_kt_next_entry,
43255682Smarkm    krb4_kt_end_seq_get,
43355682Smarkm    krb4_kt_add_entry,		/* add_entry */
43490926Snectar    krb4_kt_remove_entry	/* remove_entry */
43555682Smarkm};
43678527Sassar
43778527Sassarconst krb5_kt_ops krb5_srvtab_fkt_ops = {
43878527Sassar    "SRVTAB",
43978527Sassar    krb4_kt_resolve,
44078527Sassar    krb4_kt_get_name,
44178527Sassar    krb4_kt_close,
44278527Sassar    NULL,			/* get */
44378527Sassar    krb4_kt_start_seq_get,
44478527Sassar    krb4_kt_next_entry,
44578527Sassar    krb4_kt_end_seq_get,
44678527Sassar    krb4_kt_add_entry,		/* add_entry */
44790926Snectar    krb4_kt_remove_entry	/* remove_entry */
44878527Sassar};
449