keytab.c revision 120945
155682Smarkm/*
2120945Snectar * Copyright (c) 1997 - 2003 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
36120945SnectarRCSID("$Id: keytab.c,v 1.55 2003/03/27 03:45:01 lha 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
49120945Snectar    if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) {
50120945Snectar	krb5_set_error_string(context, "krb5_kt_register; prefix too long");
51120945Snectar	return KRB5_KT_NAME_TOOLONG;
52120945Snectar    }
53120945Snectar
5455682Smarkm    tmp = realloc(context->kt_types,
5555682Smarkm		  (context->num_kt_types + 1) * sizeof(*context->kt_types));
5678527Sassar    if(tmp == NULL) {
5778527Sassar	krb5_set_error_string(context, "malloc: out of memory");
5855682Smarkm	return ENOMEM;
5978527Sassar    }
6055682Smarkm    memcpy(&tmp[context->num_kt_types], ops,
6155682Smarkm	   sizeof(tmp[context->num_kt_types]));
6255682Smarkm    context->kt_types = tmp;
6355682Smarkm    context->num_kt_types++;
6455682Smarkm    return 0;
6555682Smarkm}
6655682Smarkm
6755682Smarkm/*
6855682Smarkm * Resolve the keytab name (of the form `type:residual') in `name'
6955682Smarkm * into a keytab in `id'.
7055682Smarkm * Return 0 or an error
7155682Smarkm */
7255682Smarkm
7355682Smarkmkrb5_error_code
7455682Smarkmkrb5_kt_resolve(krb5_context context,
7555682Smarkm		const char *name,
7655682Smarkm		krb5_keytab *id)
7755682Smarkm{
7855682Smarkm    krb5_keytab k;
7955682Smarkm    int i;
8055682Smarkm    const char *type, *residual;
8155682Smarkm    size_t type_len;
8255682Smarkm    krb5_error_code ret;
8355682Smarkm
8455682Smarkm    residual = strchr(name, ':');
8555682Smarkm    if(residual == NULL) {
8655682Smarkm	type = "FILE";
8755682Smarkm	type_len = strlen(type);
8855682Smarkm	residual = name;
8955682Smarkm    } else {
9055682Smarkm	type = name;
9155682Smarkm	type_len = residual - name;
9255682Smarkm	residual++;
9355682Smarkm    }
9455682Smarkm
9555682Smarkm    for(i = 0; i < context->num_kt_types; i++) {
9690926Snectar	if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
9755682Smarkm	    break;
9855682Smarkm    }
9978527Sassar    if(i == context->num_kt_types) {
10078527Sassar	krb5_set_error_string(context, "unknown keytab type %.*s",
10178527Sassar			      (int)type_len, type);
10255682Smarkm	return KRB5_KT_UNKNOWN_TYPE;
10378527Sassar    }
10455682Smarkm
10555682Smarkm    k = malloc (sizeof(*k));
10678527Sassar    if (k == NULL) {
10778527Sassar	krb5_set_error_string(context, "malloc: out of memory");
10855682Smarkm	return ENOMEM;
10978527Sassar    }
11055682Smarkm    memcpy(k, &context->kt_types[i], sizeof(*k));
11155682Smarkm    k->data = NULL;
11255682Smarkm    ret = (*k->resolve)(context, residual, k);
11355682Smarkm    if(ret) {
11455682Smarkm	free(k);
11555682Smarkm	k = NULL;
11655682Smarkm    }
11755682Smarkm    *id = k;
11855682Smarkm    return ret;
11955682Smarkm}
12055682Smarkm
12155682Smarkm/*
12255682Smarkm * copy the name of the default keytab into `name'.
12355682Smarkm * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
12455682Smarkm */
12555682Smarkm
12655682Smarkmkrb5_error_code
12755682Smarkmkrb5_kt_default_name(krb5_context context, char *name, size_t namesize)
12855682Smarkm{
12978527Sassar    if (strlcpy (name, context->default_keytab, namesize) >= namesize) {
13078527Sassar	krb5_clear_error_string (context);
13155682Smarkm	return KRB5_CONFIG_NOTENUFSPACE;
13278527Sassar    }
13355682Smarkm    return 0;
13455682Smarkm}
13555682Smarkm
13655682Smarkm/*
13778527Sassar * copy the name of the default modify keytab into `name'.
13878527Sassar * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
13978527Sassar */
14078527Sassar
14178527Sassarkrb5_error_code
14278527Sassarkrb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
14378527Sassar{
14490926Snectar    const char *kt = NULL;
14590926Snectar    if(context->default_keytab_modify == NULL) {
14690926Snectar	if(strncasecmp(context->default_keytab, "ANY:", 4) != 0)
14790926Snectar	    kt = context->default_keytab;
14890926Snectar	else {
14990926Snectar	    size_t len = strcspn(context->default_keytab + 4, ",");
15090926Snectar	    if(len >= namesize) {
15190926Snectar		krb5_clear_error_string(context);
15290926Snectar		return KRB5_CONFIG_NOTENUFSPACE;
15390926Snectar	    }
15490926Snectar	    strlcpy(name, context->default_keytab + 4, namesize);
15590926Snectar	    name[len] = '\0';
15690926Snectar	    return 0;
15790926Snectar	}
15890926Snectar    } else
15990926Snectar	kt = context->default_keytab_modify;
16090926Snectar    if (strlcpy (name, kt, namesize) >= namesize) {
16178527Sassar	krb5_clear_error_string (context);
16278527Sassar	return KRB5_CONFIG_NOTENUFSPACE;
16378527Sassar    }
16478527Sassar    return 0;
16578527Sassar}
16678527Sassar
16778527Sassar/*
16855682Smarkm * Set `id' to the default keytab.
16955682Smarkm * Return 0 or an error.
17055682Smarkm */
17155682Smarkm
17255682Smarkmkrb5_error_code
17355682Smarkmkrb5_kt_default(krb5_context context, krb5_keytab *id)
17455682Smarkm{
17555682Smarkm    return krb5_kt_resolve (context, context->default_keytab, id);
17655682Smarkm}
17755682Smarkm
17855682Smarkm/*
17955682Smarkm * Read the key identified by `(principal, vno, enctype)' from the
18055682Smarkm * keytab in `keyprocarg' (the default if == NULL) into `*key'.
18155682Smarkm * Return 0 or an error.
18255682Smarkm */
18355682Smarkm
18455682Smarkmkrb5_error_code
18555682Smarkmkrb5_kt_read_service_key(krb5_context context,
18655682Smarkm			 krb5_pointer keyprocarg,
18755682Smarkm			 krb5_principal principal,
18855682Smarkm			 krb5_kvno vno,
18955682Smarkm			 krb5_enctype enctype,
19055682Smarkm			 krb5_keyblock **key)
19155682Smarkm{
19255682Smarkm    krb5_keytab keytab;
19355682Smarkm    krb5_keytab_entry entry;
19455682Smarkm    krb5_error_code ret;
19555682Smarkm
19655682Smarkm    if (keyprocarg)
19755682Smarkm	ret = krb5_kt_resolve (context, keyprocarg, &keytab);
19855682Smarkm    else
19955682Smarkm	ret = krb5_kt_default (context, &keytab);
20055682Smarkm
20155682Smarkm    if (ret)
20255682Smarkm	return ret;
20355682Smarkm
20455682Smarkm    ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
20555682Smarkm    krb5_kt_close (context, keytab);
20655682Smarkm    if (ret)
20755682Smarkm	return ret;
20855682Smarkm    ret = krb5_copy_keyblock (context, &entry.keyblock, key);
20955682Smarkm    krb5_kt_free_entry(context, &entry);
21055682Smarkm    return ret;
21155682Smarkm}
21255682Smarkm
21355682Smarkm/*
214120945Snectar * Return the type of the `keytab' in the string `prefix of length
215120945Snectar * `prefixsize'.
216120945Snectar */
217120945Snectar
218120945Snectarkrb5_error_code
219120945Snectarkrb5_kt_get_type(krb5_context context,
220120945Snectar		 krb5_keytab keytab,
221120945Snectar		 char *prefix,
222120945Snectar		 size_t prefixsize)
223120945Snectar{
224120945Snectar    strlcpy(prefix, keytab->prefix, prefixsize);
225120945Snectar    return 0;
226120945Snectar}
227120945Snectar
228120945Snectar/*
22955682Smarkm * Retrieve the name of the keytab `keytab' into `name', `namesize'
23055682Smarkm * Return 0 or an error.
23155682Smarkm */
23255682Smarkm
23355682Smarkmkrb5_error_code
23455682Smarkmkrb5_kt_get_name(krb5_context context,
23555682Smarkm		 krb5_keytab keytab,
23655682Smarkm		 char *name,
23755682Smarkm		 size_t namesize)
23855682Smarkm{
23955682Smarkm    return (*keytab->get_name)(context, keytab, name, namesize);
24055682Smarkm}
24155682Smarkm
24255682Smarkm/*
24355682Smarkm * Finish using the keytab in `id'.  All resources will be released.
24455682Smarkm * Return 0 or an error.
24555682Smarkm */
24655682Smarkm
24755682Smarkmkrb5_error_code
24855682Smarkmkrb5_kt_close(krb5_context context,
24955682Smarkm	      krb5_keytab id)
25055682Smarkm{
25155682Smarkm    krb5_error_code ret;
25255682Smarkm
25355682Smarkm    ret = (*id->close)(context, id);
25455682Smarkm    if(ret == 0)
25555682Smarkm	free(id);
25655682Smarkm    return ret;
25755682Smarkm}
25855682Smarkm
25955682Smarkm/*
26055682Smarkm * Compare `entry' against `principal, vno, enctype'.
26155682Smarkm * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
26255682Smarkm * Return TRUE if they compare the same, FALSE otherwise.
26355682Smarkm */
26455682Smarkm
26555682Smarkmkrb5_boolean
26655682Smarkmkrb5_kt_compare(krb5_context context,
26755682Smarkm		krb5_keytab_entry *entry,
26855682Smarkm		krb5_const_principal principal,
26955682Smarkm		krb5_kvno vno,
27055682Smarkm		krb5_enctype enctype)
27155682Smarkm{
27255682Smarkm    if(principal != NULL &&
27355682Smarkm       !krb5_principal_compare(context, entry->principal, principal))
27455682Smarkm	return FALSE;
27555682Smarkm    if(vno && vno != entry->vno)
27655682Smarkm	return FALSE;
27755682Smarkm    if(enctype && enctype != entry->keyblock.keytype)
27855682Smarkm	return FALSE;
27955682Smarkm    return TRUE;
28055682Smarkm}
28155682Smarkm
28255682Smarkm/*
28355682Smarkm * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
28455682Smarkm * from the keytab `id'.
285102644Snectar * kvno == 0 is a wildcard and gives the keytab with the highest vno.
28655682Smarkm * Return 0 or an error.
28755682Smarkm */
28855682Smarkm
28955682Smarkmkrb5_error_code
29055682Smarkmkrb5_kt_get_entry(krb5_context context,
29155682Smarkm		  krb5_keytab id,
29255682Smarkm		  krb5_const_principal principal,
29355682Smarkm		  krb5_kvno kvno,
29455682Smarkm		  krb5_enctype enctype,
29555682Smarkm		  krb5_keytab_entry *entry)
29655682Smarkm{
29755682Smarkm    krb5_keytab_entry tmp;
29855682Smarkm    krb5_error_code ret;
29955682Smarkm    krb5_kt_cursor cursor;
30055682Smarkm
30155682Smarkm    if(id->get)
30255682Smarkm	return (*id->get)(context, id, principal, kvno, enctype, entry);
30355682Smarkm
30455682Smarkm    ret = krb5_kt_start_seq_get (context, id, &cursor);
30555682Smarkm    if (ret)
30655682Smarkm	return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */
30755682Smarkm
30855682Smarkm    entry->vno = 0;
30955682Smarkm    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
31055682Smarkm	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
311102644Snectar	    /* the file keytab might only store the lower 8 bits of
312102644Snectar	       the kvno, so only compare those bits */
313102644Snectar	    if (kvno == tmp.vno
314102644Snectar		|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
31555682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
31655682Smarkm		krb5_kt_free_entry (context, &tmp);
31755682Smarkm		krb5_kt_end_seq_get(context, id, &cursor);
31855682Smarkm		return 0;
31955682Smarkm	    } else if (kvno == 0 && tmp.vno > entry->vno) {
32055682Smarkm		if (entry->vno)
32155682Smarkm		    krb5_kt_free_entry (context, entry);
32255682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
32355682Smarkm	    }
32455682Smarkm	}
32555682Smarkm	krb5_kt_free_entry(context, &tmp);
32655682Smarkm    }
32755682Smarkm    krb5_kt_end_seq_get (context, id, &cursor);
32878527Sassar    if (entry->vno) {
32955682Smarkm	return 0;
33078527Sassar    } else {
331120945Snectar	char princ[256], kt_name[256], kvno_str[25];
33278527Sassar
33378527Sassar	krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
33478527Sassar	krb5_kt_get_name (context, id, kt_name, sizeof(kt_name));
33578527Sassar
336120945Snectar	if (kvno)
337120945Snectar	    snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
338120945Snectar	else
339120945Snectar	    kvno_str[0] = '\0';
340120945Snectar
34178527Sassar	krb5_set_error_string (context,
342120945Snectar 			       "failed to find %s%s in keytab %s",
343102644Snectar			       princ,
344120945Snectar			       kvno_str,
345102644Snectar			       kt_name);
34655682Smarkm	return KRB5_KT_NOTFOUND;
34778527Sassar    }
34855682Smarkm}
34955682Smarkm
35055682Smarkm/*
35155682Smarkm * Copy the contents of `in' into `out'.
352102644Snectar * Return 0 or an error.  */
35355682Smarkm
35455682Smarkmkrb5_error_code
35555682Smarkmkrb5_kt_copy_entry_contents(krb5_context context,
35655682Smarkm			    const krb5_keytab_entry *in,
35755682Smarkm			    krb5_keytab_entry *out)
35855682Smarkm{
35955682Smarkm    krb5_error_code ret;
36055682Smarkm
36155682Smarkm    memset(out, 0, sizeof(*out));
36255682Smarkm    out->vno = in->vno;
36355682Smarkm
36455682Smarkm    ret = krb5_copy_principal (context, in->principal, &out->principal);
36555682Smarkm    if (ret)
36655682Smarkm	goto fail;
36755682Smarkm    ret = krb5_copy_keyblock_contents (context,
36855682Smarkm				       &in->keyblock,
36955682Smarkm				       &out->keyblock);
37055682Smarkm    if (ret)
37155682Smarkm	goto fail;
37255682Smarkm    out->timestamp = in->timestamp;
37355682Smarkm    return 0;
37455682Smarkmfail:
37555682Smarkm    krb5_kt_free_entry (context, out);
37655682Smarkm    return ret;
37755682Smarkm}
37855682Smarkm
37955682Smarkm/*
38055682Smarkm * Free the contents of `entry'.
38155682Smarkm */
38255682Smarkm
38355682Smarkmkrb5_error_code
38455682Smarkmkrb5_kt_free_entry(krb5_context context,
38555682Smarkm		   krb5_keytab_entry *entry)
38655682Smarkm{
38755682Smarkm  krb5_free_principal (context, entry->principal);
38855682Smarkm  krb5_free_keyblock_contents (context, &entry->keyblock);
38955682Smarkm  return 0;
39055682Smarkm}
39155682Smarkm
39255682Smarkm#if 0
39355682Smarkmstatic int
39455682Smarkmxxxlock(int fd, int write)
39555682Smarkm{
39655682Smarkm    if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) {
39755682Smarkm	sleep(1);
39855682Smarkm	if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
39955682Smarkm	    return -1;
40055682Smarkm    }
40155682Smarkm    return 0;
40255682Smarkm}
40355682Smarkm
40455682Smarkmstatic void
40555682Smarkmxxxunlock(int fd)
40655682Smarkm{
40755682Smarkm    flock(fd, LOCK_UN);
40855682Smarkm}
40955682Smarkm#endif
41055682Smarkm
41155682Smarkm/*
41255682Smarkm * Set `cursor' to point at the beginning of `id'.
41355682Smarkm * Return 0 or an error.
41455682Smarkm */
41555682Smarkm
41655682Smarkmkrb5_error_code
41755682Smarkmkrb5_kt_start_seq_get(krb5_context context,
41855682Smarkm		      krb5_keytab id,
41955682Smarkm		      krb5_kt_cursor *cursor)
42055682Smarkm{
42178527Sassar    if(id->start_seq_get == NULL) {
42278527Sassar	krb5_set_error_string(context,
42378527Sassar			      "start_seq_get is not supported in the %s "
42478527Sassar			      " keytab", id->prefix);
42555682Smarkm	return HEIM_ERR_OPNOTSUPP;
42678527Sassar    }
42755682Smarkm    return (*id->start_seq_get)(context, id, cursor);
42855682Smarkm}
42955682Smarkm
43055682Smarkm/*
43155682Smarkm * Get the next entry from `id' pointed to by `cursor' and advance the
43255682Smarkm * `cursor'.
43355682Smarkm * Return 0 or an error.
43455682Smarkm */
43555682Smarkm
43655682Smarkmkrb5_error_code
43755682Smarkmkrb5_kt_next_entry(krb5_context context,
43855682Smarkm		   krb5_keytab id,
43955682Smarkm		   krb5_keytab_entry *entry,
44055682Smarkm		   krb5_kt_cursor *cursor)
44155682Smarkm{
44278527Sassar    if(id->next_entry == NULL) {
44378527Sassar	krb5_set_error_string(context,
44478527Sassar			      "next_entry is not supported in the %s "
44578527Sassar			      " keytab", id->prefix);
44655682Smarkm	return HEIM_ERR_OPNOTSUPP;
44778527Sassar    }
44855682Smarkm    return (*id->next_entry)(context, id, entry, cursor);
44955682Smarkm}
45055682Smarkm
45155682Smarkm/*
45255682Smarkm * Release all resources associated with `cursor'.
45355682Smarkm */
45455682Smarkm
45555682Smarkmkrb5_error_code
45655682Smarkmkrb5_kt_end_seq_get(krb5_context context,
45755682Smarkm		    krb5_keytab id,
45855682Smarkm		    krb5_kt_cursor *cursor)
45955682Smarkm{
46078527Sassar    if(id->end_seq_get == NULL) {
46178527Sassar	krb5_set_error_string(context,
46278527Sassar			      "end_seq_get is not supported in the %s "
46378527Sassar			      " keytab", id->prefix);
46455682Smarkm	return HEIM_ERR_OPNOTSUPP;
46578527Sassar    }
46655682Smarkm    return (*id->end_seq_get)(context, id, cursor);
46755682Smarkm}
46855682Smarkm
46955682Smarkm/*
47055682Smarkm * Add the entry in `entry' to the keytab `id'.
47155682Smarkm * Return 0 or an error.
47255682Smarkm */
47355682Smarkm
47455682Smarkmkrb5_error_code
47555682Smarkmkrb5_kt_add_entry(krb5_context context,
47655682Smarkm		  krb5_keytab id,
47755682Smarkm		  krb5_keytab_entry *entry)
47855682Smarkm{
47978527Sassar    if(id->add == NULL) {
48078527Sassar	krb5_set_error_string(context, "Add is not supported in the %s keytab",
48178527Sassar			      id->prefix);
48255682Smarkm	return KRB5_KT_NOWRITE;
48378527Sassar    }
48457416Smarkm    entry->timestamp = time(NULL);
48555682Smarkm    return (*id->add)(context, id,entry);
48655682Smarkm}
48755682Smarkm
48855682Smarkm/*
48955682Smarkm * Remove the entry `entry' from the keytab `id'.
49055682Smarkm * Return 0 or an error.
49155682Smarkm */
49255682Smarkm
49355682Smarkmkrb5_error_code
49455682Smarkmkrb5_kt_remove_entry(krb5_context context,
49555682Smarkm		     krb5_keytab id,
49655682Smarkm		     krb5_keytab_entry *entry)
49755682Smarkm{
49878527Sassar    if(id->remove == NULL) {
49978527Sassar	krb5_set_error_string(context,
50078527Sassar			      "Remove is not supported in the %s keytab",
50178527Sassar			      id->prefix);
50255682Smarkm	return KRB5_KT_NOWRITE;
50378527Sassar    }
50455682Smarkm    return (*id->remove)(context, id, entry);
50555682Smarkm}
506