keytab.c revision 102644
155682Smarkm/*
2102644Snectar * 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
36102644SnectarRCSID("$Id: keytab.c,v 1.53 2002/03/10 23:14:12 assar 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'.
265102644Snectar * kvno == 0 is a wildcard and gives the keytab with the highest vno.
26655682Smarkm * Return 0 or an error.
26755682Smarkm */
26855682Smarkm
26955682Smarkmkrb5_error_code
27055682Smarkmkrb5_kt_get_entry(krb5_context context,
27155682Smarkm		  krb5_keytab id,
27255682Smarkm		  krb5_const_principal principal,
27355682Smarkm		  krb5_kvno kvno,
27455682Smarkm		  krb5_enctype enctype,
27555682Smarkm		  krb5_keytab_entry *entry)
27655682Smarkm{
27755682Smarkm    krb5_keytab_entry tmp;
27855682Smarkm    krb5_error_code ret;
27955682Smarkm    krb5_kt_cursor cursor;
28055682Smarkm
28155682Smarkm    if(id->get)
28255682Smarkm	return (*id->get)(context, id, principal, kvno, enctype, entry);
28355682Smarkm
28455682Smarkm    ret = krb5_kt_start_seq_get (context, id, &cursor);
28555682Smarkm    if (ret)
28655682Smarkm	return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */
28755682Smarkm
28855682Smarkm    entry->vno = 0;
28955682Smarkm    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
29055682Smarkm	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
291102644Snectar	    /* the file keytab might only store the lower 8 bits of
292102644Snectar	       the kvno, so only compare those bits */
293102644Snectar	    if (kvno == tmp.vno
294102644Snectar		|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
29555682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
29655682Smarkm		krb5_kt_free_entry (context, &tmp);
29755682Smarkm		krb5_kt_end_seq_get(context, id, &cursor);
29855682Smarkm		return 0;
29955682Smarkm	    } else if (kvno == 0 && tmp.vno > entry->vno) {
30055682Smarkm		if (entry->vno)
30155682Smarkm		    krb5_kt_free_entry (context, entry);
30255682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
30355682Smarkm	    }
30455682Smarkm	}
30555682Smarkm	krb5_kt_free_entry(context, &tmp);
30655682Smarkm    }
30755682Smarkm    krb5_kt_end_seq_get (context, id, &cursor);
30878527Sassar    if (entry->vno) {
30955682Smarkm	return 0;
31078527Sassar    } else {
31178527Sassar	char princ[256], kt_name[256];
31278527Sassar
31378527Sassar	krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
31478527Sassar	krb5_kt_get_name (context, id, kt_name, sizeof(kt_name));
31578527Sassar
31678527Sassar	krb5_set_error_string (context,
317102644Snectar 			       "failed to find %s%s%d%s in keytab %s",
318102644Snectar			       princ,
319102644Snectar			       kvno ? "(" : "",
320102644Snectar			       kvno,
321102644Snectar			       kvno ? ")" : "",
322102644Snectar			       kt_name);
32355682Smarkm	return KRB5_KT_NOTFOUND;
32478527Sassar    }
32555682Smarkm}
32655682Smarkm
32755682Smarkm/*
32855682Smarkm * Copy the contents of `in' into `out'.
329102644Snectar * Return 0 or an error.  */
33055682Smarkm
33155682Smarkmkrb5_error_code
33255682Smarkmkrb5_kt_copy_entry_contents(krb5_context context,
33355682Smarkm			    const krb5_keytab_entry *in,
33455682Smarkm			    krb5_keytab_entry *out)
33555682Smarkm{
33655682Smarkm    krb5_error_code ret;
33755682Smarkm
33855682Smarkm    memset(out, 0, sizeof(*out));
33955682Smarkm    out->vno = in->vno;
34055682Smarkm
34155682Smarkm    ret = krb5_copy_principal (context, in->principal, &out->principal);
34255682Smarkm    if (ret)
34355682Smarkm	goto fail;
34455682Smarkm    ret = krb5_copy_keyblock_contents (context,
34555682Smarkm				       &in->keyblock,
34655682Smarkm				       &out->keyblock);
34755682Smarkm    if (ret)
34855682Smarkm	goto fail;
34955682Smarkm    out->timestamp = in->timestamp;
35055682Smarkm    return 0;
35155682Smarkmfail:
35255682Smarkm    krb5_kt_free_entry (context, out);
35355682Smarkm    return ret;
35455682Smarkm}
35555682Smarkm
35655682Smarkm/*
35755682Smarkm * Free the contents of `entry'.
35855682Smarkm */
35955682Smarkm
36055682Smarkmkrb5_error_code
36155682Smarkmkrb5_kt_free_entry(krb5_context context,
36255682Smarkm		   krb5_keytab_entry *entry)
36355682Smarkm{
36455682Smarkm  krb5_free_principal (context, entry->principal);
36555682Smarkm  krb5_free_keyblock_contents (context, &entry->keyblock);
36655682Smarkm  return 0;
36755682Smarkm}
36855682Smarkm
36955682Smarkm#if 0
37055682Smarkmstatic int
37155682Smarkmxxxlock(int fd, int write)
37255682Smarkm{
37355682Smarkm    if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) {
37455682Smarkm	sleep(1);
37555682Smarkm	if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
37655682Smarkm	    return -1;
37755682Smarkm    }
37855682Smarkm    return 0;
37955682Smarkm}
38055682Smarkm
38155682Smarkmstatic void
38255682Smarkmxxxunlock(int fd)
38355682Smarkm{
38455682Smarkm    flock(fd, LOCK_UN);
38555682Smarkm}
38655682Smarkm#endif
38755682Smarkm
38855682Smarkm/*
38955682Smarkm * Set `cursor' to point at the beginning of `id'.
39055682Smarkm * Return 0 or an error.
39155682Smarkm */
39255682Smarkm
39355682Smarkmkrb5_error_code
39455682Smarkmkrb5_kt_start_seq_get(krb5_context context,
39555682Smarkm		      krb5_keytab id,
39655682Smarkm		      krb5_kt_cursor *cursor)
39755682Smarkm{
39878527Sassar    if(id->start_seq_get == NULL) {
39978527Sassar	krb5_set_error_string(context,
40078527Sassar			      "start_seq_get is not supported in the %s "
40178527Sassar			      " keytab", id->prefix);
40255682Smarkm	return HEIM_ERR_OPNOTSUPP;
40378527Sassar    }
40455682Smarkm    return (*id->start_seq_get)(context, id, cursor);
40555682Smarkm}
40655682Smarkm
40755682Smarkm/*
40855682Smarkm * Get the next entry from `id' pointed to by `cursor' and advance the
40955682Smarkm * `cursor'.
41055682Smarkm * Return 0 or an error.
41155682Smarkm */
41255682Smarkm
41355682Smarkmkrb5_error_code
41455682Smarkmkrb5_kt_next_entry(krb5_context context,
41555682Smarkm		   krb5_keytab id,
41655682Smarkm		   krb5_keytab_entry *entry,
41755682Smarkm		   krb5_kt_cursor *cursor)
41855682Smarkm{
41978527Sassar    if(id->next_entry == NULL) {
42078527Sassar	krb5_set_error_string(context,
42178527Sassar			      "next_entry is not supported in the %s "
42278527Sassar			      " keytab", id->prefix);
42355682Smarkm	return HEIM_ERR_OPNOTSUPP;
42478527Sassar    }
42555682Smarkm    return (*id->next_entry)(context, id, entry, cursor);
42655682Smarkm}
42755682Smarkm
42855682Smarkm/*
42955682Smarkm * Release all resources associated with `cursor'.
43055682Smarkm */
43155682Smarkm
43255682Smarkmkrb5_error_code
43355682Smarkmkrb5_kt_end_seq_get(krb5_context context,
43455682Smarkm		    krb5_keytab id,
43555682Smarkm		    krb5_kt_cursor *cursor)
43655682Smarkm{
43778527Sassar    if(id->end_seq_get == NULL) {
43878527Sassar	krb5_set_error_string(context,
43978527Sassar			      "end_seq_get is not supported in the %s "
44078527Sassar			      " keytab", id->prefix);
44155682Smarkm	return HEIM_ERR_OPNOTSUPP;
44278527Sassar    }
44355682Smarkm    return (*id->end_seq_get)(context, id, cursor);
44455682Smarkm}
44555682Smarkm
44655682Smarkm/*
44755682Smarkm * Add the entry in `entry' to the keytab `id'.
44855682Smarkm * Return 0 or an error.
44955682Smarkm */
45055682Smarkm
45155682Smarkmkrb5_error_code
45255682Smarkmkrb5_kt_add_entry(krb5_context context,
45355682Smarkm		  krb5_keytab id,
45455682Smarkm		  krb5_keytab_entry *entry)
45555682Smarkm{
45678527Sassar    if(id->add == NULL) {
45778527Sassar	krb5_set_error_string(context, "Add is not supported in the %s keytab",
45878527Sassar			      id->prefix);
45955682Smarkm	return KRB5_KT_NOWRITE;
46078527Sassar    }
46157416Smarkm    entry->timestamp = time(NULL);
46255682Smarkm    return (*id->add)(context, id,entry);
46355682Smarkm}
46455682Smarkm
46555682Smarkm/*
46655682Smarkm * Remove the entry `entry' from the keytab `id'.
46755682Smarkm * Return 0 or an error.
46855682Smarkm */
46955682Smarkm
47055682Smarkmkrb5_error_code
47155682Smarkmkrb5_kt_remove_entry(krb5_context context,
47255682Smarkm		     krb5_keytab id,
47355682Smarkm		     krb5_keytab_entry *entry)
47455682Smarkm{
47578527Sassar    if(id->remove == NULL) {
47678527Sassar	krb5_set_error_string(context,
47778527Sassar			      "Remove is not supported in the %s keytab",
47878527Sassar			      id->prefix);
47955682Smarkm	return KRB5_KT_NOWRITE;
48078527Sassar    }
48155682Smarkm    return (*id->remove)(context, id, entry);
48255682Smarkm}
483