keytab.c revision 90926
155682Smarkm/*
278527Sassar * Copyright (c) 1997 - 2001 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
3690926SnectarRCSID("$Id: keytab.c,v 1.52 2002/01/30 10:09:35 joda Exp $");
3755682Smarkm
3855682Smarkm/*
3955682Smarkm * Register a new keytab in `ops'
4055682Smarkm * Return 0 or an error.
4155682Smarkm */
4255682Smarkm
4355682Smarkmkrb5_error_code
4455682Smarkmkrb5_kt_register(krb5_context context,
4555682Smarkm		 const krb5_kt_ops *ops)
4655682Smarkm{
4755682Smarkm    struct krb5_keytab_data *tmp;
4855682Smarkm
4955682Smarkm    tmp = realloc(context->kt_types,
5055682Smarkm		  (context->num_kt_types + 1) * sizeof(*context->kt_types));
5178527Sassar    if(tmp == NULL) {
5278527Sassar	krb5_set_error_string(context, "malloc: out of memory");
5355682Smarkm	return ENOMEM;
5478527Sassar    }
5555682Smarkm    memcpy(&tmp[context->num_kt_types], ops,
5655682Smarkm	   sizeof(tmp[context->num_kt_types]));
5755682Smarkm    context->kt_types = tmp;
5855682Smarkm    context->num_kt_types++;
5955682Smarkm    return 0;
6055682Smarkm}
6155682Smarkm
6255682Smarkm/*
6355682Smarkm * Resolve the keytab name (of the form `type:residual') in `name'
6455682Smarkm * into a keytab in `id'.
6555682Smarkm * Return 0 or an error
6655682Smarkm */
6755682Smarkm
6855682Smarkmkrb5_error_code
6955682Smarkmkrb5_kt_resolve(krb5_context context,
7055682Smarkm		const char *name,
7155682Smarkm		krb5_keytab *id)
7255682Smarkm{
7355682Smarkm    krb5_keytab k;
7455682Smarkm    int i;
7555682Smarkm    const char *type, *residual;
7655682Smarkm    size_t type_len;
7755682Smarkm    krb5_error_code ret;
7855682Smarkm
7955682Smarkm    residual = strchr(name, ':');
8055682Smarkm    if(residual == NULL) {
8155682Smarkm	type = "FILE";
8255682Smarkm	type_len = strlen(type);
8355682Smarkm	residual = name;
8455682Smarkm    } else {
8555682Smarkm	type = name;
8655682Smarkm	type_len = residual - name;
8755682Smarkm	residual++;
8855682Smarkm    }
8955682Smarkm
9055682Smarkm    for(i = 0; i < context->num_kt_types; i++) {
9190926Snectar	if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
9255682Smarkm	    break;
9355682Smarkm    }
9478527Sassar    if(i == context->num_kt_types) {
9578527Sassar	krb5_set_error_string(context, "unknown keytab type %.*s",
9678527Sassar			      (int)type_len, type);
9755682Smarkm	return KRB5_KT_UNKNOWN_TYPE;
9878527Sassar    }
9955682Smarkm
10055682Smarkm    k = malloc (sizeof(*k));
10178527Sassar    if (k == NULL) {
10278527Sassar	krb5_set_error_string(context, "malloc: out of memory");
10355682Smarkm	return ENOMEM;
10478527Sassar    }
10555682Smarkm    memcpy(k, &context->kt_types[i], sizeof(*k));
10655682Smarkm    k->data = NULL;
10755682Smarkm    ret = (*k->resolve)(context, residual, k);
10855682Smarkm    if(ret) {
10955682Smarkm	free(k);
11055682Smarkm	k = NULL;
11155682Smarkm    }
11255682Smarkm    *id = k;
11355682Smarkm    return ret;
11455682Smarkm}
11555682Smarkm
11655682Smarkm/*
11755682Smarkm * copy the name of the default keytab into `name'.
11855682Smarkm * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
11955682Smarkm */
12055682Smarkm
12155682Smarkmkrb5_error_code
12255682Smarkmkrb5_kt_default_name(krb5_context context, char *name, size_t namesize)
12355682Smarkm{
12478527Sassar    if (strlcpy (name, context->default_keytab, namesize) >= namesize) {
12578527Sassar	krb5_clear_error_string (context);
12655682Smarkm	return KRB5_CONFIG_NOTENUFSPACE;
12778527Sassar    }
12855682Smarkm    return 0;
12955682Smarkm}
13055682Smarkm
13155682Smarkm/*
13278527Sassar * copy the name of the default modify keytab into `name'.
13378527Sassar * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
13478527Sassar */
13578527Sassar
13678527Sassarkrb5_error_code
13778527Sassarkrb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
13878527Sassar{
13990926Snectar    const char *kt = NULL;
14090926Snectar    if(context->default_keytab_modify == NULL) {
14190926Snectar	if(strncasecmp(context->default_keytab, "ANY:", 4) != 0)
14290926Snectar	    kt = context->default_keytab;
14390926Snectar	else {
14490926Snectar	    size_t len = strcspn(context->default_keytab + 4, ",");
14590926Snectar	    if(len >= namesize) {
14690926Snectar		krb5_clear_error_string(context);
14790926Snectar		return KRB5_CONFIG_NOTENUFSPACE;
14890926Snectar	    }
14990926Snectar	    strlcpy(name, context->default_keytab + 4, namesize);
15090926Snectar	    name[len] = '\0';
15190926Snectar	    return 0;
15290926Snectar	}
15390926Snectar    } else
15490926Snectar	kt = context->default_keytab_modify;
15590926Snectar    if (strlcpy (name, kt, namesize) >= namesize) {
15678527Sassar	krb5_clear_error_string (context);
15778527Sassar	return KRB5_CONFIG_NOTENUFSPACE;
15878527Sassar    }
15978527Sassar    return 0;
16078527Sassar}
16178527Sassar
16278527Sassar/*
16355682Smarkm * Set `id' to the default keytab.
16455682Smarkm * Return 0 or an error.
16555682Smarkm */
16655682Smarkm
16755682Smarkmkrb5_error_code
16855682Smarkmkrb5_kt_default(krb5_context context, krb5_keytab *id)
16955682Smarkm{
17055682Smarkm    return krb5_kt_resolve (context, context->default_keytab, id);
17155682Smarkm}
17255682Smarkm
17355682Smarkm/*
17455682Smarkm * Read the key identified by `(principal, vno, enctype)' from the
17555682Smarkm * keytab in `keyprocarg' (the default if == NULL) into `*key'.
17655682Smarkm * Return 0 or an error.
17755682Smarkm */
17855682Smarkm
17955682Smarkmkrb5_error_code
18055682Smarkmkrb5_kt_read_service_key(krb5_context context,
18155682Smarkm			 krb5_pointer keyprocarg,
18255682Smarkm			 krb5_principal principal,
18355682Smarkm			 krb5_kvno vno,
18455682Smarkm			 krb5_enctype enctype,
18555682Smarkm			 krb5_keyblock **key)
18655682Smarkm{
18755682Smarkm    krb5_keytab keytab;
18855682Smarkm    krb5_keytab_entry entry;
18955682Smarkm    krb5_error_code ret;
19055682Smarkm
19155682Smarkm    if (keyprocarg)
19255682Smarkm	ret = krb5_kt_resolve (context, keyprocarg, &keytab);
19355682Smarkm    else
19455682Smarkm	ret = krb5_kt_default (context, &keytab);
19555682Smarkm
19655682Smarkm    if (ret)
19755682Smarkm	return ret;
19855682Smarkm
19955682Smarkm    ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
20055682Smarkm    krb5_kt_close (context, keytab);
20155682Smarkm    if (ret)
20255682Smarkm	return ret;
20355682Smarkm    ret = krb5_copy_keyblock (context, &entry.keyblock, key);
20455682Smarkm    krb5_kt_free_entry(context, &entry);
20555682Smarkm    return ret;
20655682Smarkm}
20755682Smarkm
20855682Smarkm/*
20955682Smarkm * Retrieve the name of the keytab `keytab' into `name', `namesize'
21055682Smarkm * Return 0 or an error.
21155682Smarkm */
21255682Smarkm
21355682Smarkmkrb5_error_code
21455682Smarkmkrb5_kt_get_name(krb5_context context,
21555682Smarkm		 krb5_keytab keytab,
21655682Smarkm		 char *name,
21755682Smarkm		 size_t namesize)
21855682Smarkm{
21955682Smarkm    return (*keytab->get_name)(context, keytab, name, namesize);
22055682Smarkm}
22155682Smarkm
22255682Smarkm/*
22355682Smarkm * Finish using the keytab in `id'.  All resources will be released.
22455682Smarkm * Return 0 or an error.
22555682Smarkm */
22655682Smarkm
22755682Smarkmkrb5_error_code
22855682Smarkmkrb5_kt_close(krb5_context context,
22955682Smarkm	      krb5_keytab id)
23055682Smarkm{
23155682Smarkm    krb5_error_code ret;
23255682Smarkm
23355682Smarkm    ret = (*id->close)(context, id);
23455682Smarkm    if(ret == 0)
23555682Smarkm	free(id);
23655682Smarkm    return ret;
23755682Smarkm}
23855682Smarkm
23955682Smarkm/*
24055682Smarkm * Compare `entry' against `principal, vno, enctype'.
24155682Smarkm * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
24255682Smarkm * Return TRUE if they compare the same, FALSE otherwise.
24355682Smarkm */
24455682Smarkm
24555682Smarkmkrb5_boolean
24655682Smarkmkrb5_kt_compare(krb5_context context,
24755682Smarkm		krb5_keytab_entry *entry,
24855682Smarkm		krb5_const_principal principal,
24955682Smarkm		krb5_kvno vno,
25055682Smarkm		krb5_enctype enctype)
25155682Smarkm{
25255682Smarkm    if(principal != NULL &&
25355682Smarkm       !krb5_principal_compare(context, entry->principal, principal))
25455682Smarkm	return FALSE;
25555682Smarkm    if(vno && vno != entry->vno)
25655682Smarkm	return FALSE;
25755682Smarkm    if(enctype && enctype != entry->keyblock.keytype)
25855682Smarkm	return FALSE;
25955682Smarkm    return TRUE;
26055682Smarkm}
26155682Smarkm
26255682Smarkm/*
26355682Smarkm * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
26455682Smarkm * from the keytab `id'.
26555682Smarkm * Return 0 or an error.
26655682Smarkm */
26755682Smarkm
26855682Smarkmkrb5_error_code
26955682Smarkmkrb5_kt_get_entry(krb5_context context,
27055682Smarkm		  krb5_keytab id,
27155682Smarkm		  krb5_const_principal principal,
27255682Smarkm		  krb5_kvno kvno,
27355682Smarkm		  krb5_enctype enctype,
27455682Smarkm		  krb5_keytab_entry *entry)
27555682Smarkm{
27655682Smarkm    krb5_keytab_entry tmp;
27755682Smarkm    krb5_error_code ret;
27855682Smarkm    krb5_kt_cursor cursor;
27955682Smarkm
28055682Smarkm    if(id->get)
28155682Smarkm	return (*id->get)(context, id, principal, kvno, enctype, entry);
28255682Smarkm
28355682Smarkm    ret = krb5_kt_start_seq_get (context, id, &cursor);
28455682Smarkm    if (ret)
28555682Smarkm	return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */
28655682Smarkm
28755682Smarkm    entry->vno = 0;
28855682Smarkm    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
28955682Smarkm	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
29055682Smarkm	    if (kvno == tmp.vno) {
29155682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
29255682Smarkm		krb5_kt_free_entry (context, &tmp);
29355682Smarkm		krb5_kt_end_seq_get(context, id, &cursor);
29455682Smarkm		return 0;
29555682Smarkm	    } else if (kvno == 0 && tmp.vno > entry->vno) {
29655682Smarkm		if (entry->vno)
29755682Smarkm		    krb5_kt_free_entry (context, entry);
29855682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
29955682Smarkm	    }
30055682Smarkm	}
30155682Smarkm	krb5_kt_free_entry(context, &tmp);
30255682Smarkm    }
30355682Smarkm    krb5_kt_end_seq_get (context, id, &cursor);
30478527Sassar    if (entry->vno) {
30555682Smarkm	return 0;
30678527Sassar    } else {
30778527Sassar	char princ[256], kt_name[256];
30878527Sassar
30978527Sassar	krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
31078527Sassar	krb5_kt_get_name (context, id, kt_name, sizeof(kt_name));
31178527Sassar
31278527Sassar	krb5_set_error_string (context,
31378527Sassar 			       "failed to find %s in keytab %s",
31478527Sassar			       princ, kt_name);
31555682Smarkm	return KRB5_KT_NOTFOUND;
31678527Sassar    }
31755682Smarkm}
31855682Smarkm
31955682Smarkm/*
32055682Smarkm * Copy the contents of `in' into `out'.
32155682Smarkm * Return 0 or an error.
32255682Smarkm */
32355682Smarkm
32455682Smarkmkrb5_error_code
32555682Smarkmkrb5_kt_copy_entry_contents(krb5_context context,
32655682Smarkm			    const krb5_keytab_entry *in,
32755682Smarkm			    krb5_keytab_entry *out)
32855682Smarkm{
32955682Smarkm    krb5_error_code ret;
33055682Smarkm
33155682Smarkm    memset(out, 0, sizeof(*out));
33255682Smarkm    out->vno = in->vno;
33355682Smarkm
33455682Smarkm    ret = krb5_copy_principal (context, in->principal, &out->principal);
33555682Smarkm    if (ret)
33655682Smarkm	goto fail;
33755682Smarkm    ret = krb5_copy_keyblock_contents (context,
33855682Smarkm				       &in->keyblock,
33955682Smarkm				       &out->keyblock);
34055682Smarkm    if (ret)
34155682Smarkm	goto fail;
34255682Smarkm    out->timestamp = in->timestamp;
34355682Smarkm    return 0;
34455682Smarkmfail:
34555682Smarkm    krb5_kt_free_entry (context, out);
34655682Smarkm    return ret;
34755682Smarkm}
34855682Smarkm
34955682Smarkm/*
35055682Smarkm * Free the contents of `entry'.
35155682Smarkm */
35255682Smarkm
35355682Smarkmkrb5_error_code
35455682Smarkmkrb5_kt_free_entry(krb5_context context,
35555682Smarkm		   krb5_keytab_entry *entry)
35655682Smarkm{
35755682Smarkm  krb5_free_principal (context, entry->principal);
35855682Smarkm  krb5_free_keyblock_contents (context, &entry->keyblock);
35955682Smarkm  return 0;
36055682Smarkm}
36155682Smarkm
36255682Smarkm#if 0
36355682Smarkmstatic int
36455682Smarkmxxxlock(int fd, int write)
36555682Smarkm{
36655682Smarkm    if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) {
36755682Smarkm	sleep(1);
36855682Smarkm	if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
36955682Smarkm	    return -1;
37055682Smarkm    }
37155682Smarkm    return 0;
37255682Smarkm}
37355682Smarkm
37455682Smarkmstatic void
37555682Smarkmxxxunlock(int fd)
37655682Smarkm{
37755682Smarkm    flock(fd, LOCK_UN);
37855682Smarkm}
37955682Smarkm#endif
38055682Smarkm
38155682Smarkm/*
38255682Smarkm * Set `cursor' to point at the beginning of `id'.
38355682Smarkm * Return 0 or an error.
38455682Smarkm */
38555682Smarkm
38655682Smarkmkrb5_error_code
38755682Smarkmkrb5_kt_start_seq_get(krb5_context context,
38855682Smarkm		      krb5_keytab id,
38955682Smarkm		      krb5_kt_cursor *cursor)
39055682Smarkm{
39178527Sassar    if(id->start_seq_get == NULL) {
39278527Sassar	krb5_set_error_string(context,
39378527Sassar			      "start_seq_get is not supported in the %s "
39478527Sassar			      " keytab", id->prefix);
39555682Smarkm	return HEIM_ERR_OPNOTSUPP;
39678527Sassar    }
39755682Smarkm    return (*id->start_seq_get)(context, id, cursor);
39855682Smarkm}
39955682Smarkm
40055682Smarkm/*
40155682Smarkm * Get the next entry from `id' pointed to by `cursor' and advance the
40255682Smarkm * `cursor'.
40355682Smarkm * Return 0 or an error.
40455682Smarkm */
40555682Smarkm
40655682Smarkmkrb5_error_code
40755682Smarkmkrb5_kt_next_entry(krb5_context context,
40855682Smarkm		   krb5_keytab id,
40955682Smarkm		   krb5_keytab_entry *entry,
41055682Smarkm		   krb5_kt_cursor *cursor)
41155682Smarkm{
41278527Sassar    if(id->next_entry == NULL) {
41378527Sassar	krb5_set_error_string(context,
41478527Sassar			      "next_entry is not supported in the %s "
41578527Sassar			      " keytab", id->prefix);
41655682Smarkm	return HEIM_ERR_OPNOTSUPP;
41778527Sassar    }
41855682Smarkm    return (*id->next_entry)(context, id, entry, cursor);
41955682Smarkm}
42055682Smarkm
42155682Smarkm/*
42255682Smarkm * Release all resources associated with `cursor'.
42355682Smarkm */
42455682Smarkm
42555682Smarkmkrb5_error_code
42655682Smarkmkrb5_kt_end_seq_get(krb5_context context,
42755682Smarkm		    krb5_keytab id,
42855682Smarkm		    krb5_kt_cursor *cursor)
42955682Smarkm{
43078527Sassar    if(id->end_seq_get == NULL) {
43178527Sassar	krb5_set_error_string(context,
43278527Sassar			      "end_seq_get is not supported in the %s "
43378527Sassar			      " keytab", id->prefix);
43455682Smarkm	return HEIM_ERR_OPNOTSUPP;
43578527Sassar    }
43655682Smarkm    return (*id->end_seq_get)(context, id, cursor);
43755682Smarkm}
43855682Smarkm
43955682Smarkm/*
44055682Smarkm * Add the entry in `entry' to the keytab `id'.
44155682Smarkm * Return 0 or an error.
44255682Smarkm */
44355682Smarkm
44455682Smarkmkrb5_error_code
44555682Smarkmkrb5_kt_add_entry(krb5_context context,
44655682Smarkm		  krb5_keytab id,
44755682Smarkm		  krb5_keytab_entry *entry)
44855682Smarkm{
44978527Sassar    if(id->add == NULL) {
45078527Sassar	krb5_set_error_string(context, "Add is not supported in the %s keytab",
45178527Sassar			      id->prefix);
45255682Smarkm	return KRB5_KT_NOWRITE;
45378527Sassar    }
45457416Smarkm    entry->timestamp = time(NULL);
45555682Smarkm    return (*id->add)(context, id,entry);
45655682Smarkm}
45755682Smarkm
45855682Smarkm/*
45955682Smarkm * Remove the entry `entry' from the keytab `id'.
46055682Smarkm * Return 0 or an error.
46155682Smarkm */
46255682Smarkm
46355682Smarkmkrb5_error_code
46455682Smarkmkrb5_kt_remove_entry(krb5_context context,
46555682Smarkm		     krb5_keytab id,
46655682Smarkm		     krb5_keytab_entry *entry)
46755682Smarkm{
46878527Sassar    if(id->remove == NULL) {
46978527Sassar	krb5_set_error_string(context,
47078527Sassar			      "Remove is not supported in the %s keytab",
47178527Sassar			      id->prefix);
47255682Smarkm	return KRB5_KT_NOWRITE;
47378527Sassar    }
47455682Smarkm    return (*id->remove)(context, id, entry);
47555682Smarkm}
476