155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
36233294Sstas/**
37233294Sstas * @page krb5_keytab_intro The keytab handing functions
38233294Sstas * @section section_krb5_keytab Kerberos Keytabs
39233294Sstas *
40233294Sstas * See the library functions here: @ref krb5_keytab
41233294Sstas *
42233294Sstas * Keytabs are long term key storage for servers, their equvalment of
43233294Sstas * password files.
44233294Sstas *
45233294Sstas * Normally the only function that useful for server are to specify
46233294Sstas * what keytab to use to other core functions like krb5_rd_req()
47233294Sstas * krb5_kt_resolve(), and krb5_kt_close().
48233294Sstas *
49233294Sstas * @subsection krb5_keytab_names Keytab names
50233294Sstas *
51233294Sstas * A keytab name is on the form type:residual. The residual part is
52233294Sstas * specific to each keytab-type.
53233294Sstas *
54233294Sstas * When a keytab-name is resolved, the type is matched with an internal
55233294Sstas * list of keytab types. If there is no matching keytab type,
56233294Sstas * the default keytab is used. The current default type is FILE.
57233294Sstas *
58233294Sstas * The default value can be changed in the configuration file
59233294Sstas * /etc/krb5.conf by setting the variable
60233294Sstas * [defaults]default_keytab_name.
61233294Sstas *
62233294Sstas * The keytab types that are implemented in Heimdal are:
63233294Sstas * - file
64233294Sstas *   store the keytab in a file, the type's name is FILE .  The
65233294Sstas *   residual part is a filename. For compatibility with other
66233294Sstas *   Kerberos implemtation WRFILE and JAVA14 is also accepted.  WRFILE
67233294Sstas *   has the same format as FILE. JAVA14 have a format that is
68233294Sstas *   compatible with older versions of MIT kerberos and SUN's Java
69233294Sstas *   based installation.  They store a truncted kvno, so when the knvo
70233294Sstas *   excess 255, they are truncted in this format.
71233294Sstas *
72233294Sstas * - keytab
73233294Sstas *   store the keytab in a AFS keyfile (usually /usr/afs/etc/KeyFile ),
74233294Sstas *   the type's name is AFSKEYFILE. The residual part is a filename.
75233294Sstas *
76233294Sstas * - memory
77233294Sstas *   The keytab is stored in a memory segment. This allows sensitive
78233294Sstas *   and/or temporary data not to be stored on disk. The type's name
79233294Sstas *   is MEMORY. Each MEMORY keytab is referenced counted by and
80233294Sstas *   opened by the residual name, so two handles can point to the
81233294Sstas *   same memory area.  When the last user closes using krb5_kt_close()
82233294Sstas *   the keytab, the keys in they keytab is memset() to zero and freed
83233294Sstas *   and can no longer be looked up by name.
84233294Sstas *
85233294Sstas *
86233294Sstas * @subsection krb5_keytab_example Keytab example
87233294Sstas *
88233294Sstas *  This is a minimalistic version of ktutil.
89233294Sstas *
90233294Sstas * @code
91233294Sstasint
92233294Sstasmain (int argc, char **argv)
93233294Sstas{
94233294Sstas    krb5_context context;
95233294Sstas    krb5_keytab keytab;
96233294Sstas    krb5_kt_cursor cursor;
97233294Sstas    krb5_keytab_entry entry;
98233294Sstas    krb5_error_code ret;
99233294Sstas    char *principal;
10055682Smarkm
101233294Sstas    if (krb5_init_context (&context) != 0)
102233294Sstas	errx(1, "krb5_context");
103233294Sstas
104233294Sstas    ret = krb5_kt_default (context, &keytab);
105233294Sstas    if (ret)
106233294Sstas	krb5_err(context, 1, ret, "krb5_kt_default");
107233294Sstas
108233294Sstas    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
109233294Sstas    if (ret)
110233294Sstas	krb5_err(context, 1, ret, "krb5_kt_start_seq_get");
111233294Sstas    while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
112233294Sstas	krb5_unparse_name(context, entry.principal, &principal);
113233294Sstas	printf("principal: %s\n", principal);
114233294Sstas	free(principal);
115233294Sstas	krb5_kt_free_entry(context, &entry);
116233294Sstas    }
117233294Sstas    ret = krb5_kt_end_seq_get(context, keytab, &cursor);
118233294Sstas    if (ret)
119233294Sstas	krb5_err(context, 1, ret, "krb5_kt_end_seq_get");
120233294Sstas    ret = krb5_kt_close(context, keytab);
121233294Sstas    if (ret)
122233294Sstas	krb5_err(context, 1, ret, "krb5_kt_close");
123233294Sstas    krb5_free_context(context);
124233294Sstas    return 0;
125233294Sstas}
126233294Sstas * @endcode
127233294Sstas *
12855682Smarkm */
12955682Smarkm
130233294Sstas
131233294Sstas/**
132233294Sstas * Register a new keytab backend.
133233294Sstas *
134233294Sstas * @param context a Keberos context.
135233294Sstas * @param ops a backend to register.
136233294Sstas *
137233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
138233294Sstas *
139233294Sstas * @ingroup krb5_keytab
140233294Sstas */
141233294Sstas
142233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
14355682Smarkmkrb5_kt_register(krb5_context context,
14455682Smarkm		 const krb5_kt_ops *ops)
14555682Smarkm{
14655682Smarkm    struct krb5_keytab_data *tmp;
14755682Smarkm
148120945Snectar    if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) {
149233294Sstas	krb5_set_error_message(context, KRB5_KT_BADNAME,
150233294Sstas			       N_("can't register cache type, prefix too long", ""));
151178825Sdfr	return KRB5_KT_BADNAME;
152120945Snectar    }
153120945Snectar
15455682Smarkm    tmp = realloc(context->kt_types,
15555682Smarkm		  (context->num_kt_types + 1) * sizeof(*context->kt_types));
15678527Sassar    if(tmp == NULL) {
157233294Sstas	krb5_set_error_message(context, ENOMEM,
158233294Sstas			       N_("malloc: out of memory", ""));
15955682Smarkm	return ENOMEM;
16078527Sassar    }
16155682Smarkm    memcpy(&tmp[context->num_kt_types], ops,
16255682Smarkm	   sizeof(tmp[context->num_kt_types]));
16355682Smarkm    context->kt_types = tmp;
16455682Smarkm    context->num_kt_types++;
16555682Smarkm    return 0;
16655682Smarkm}
16755682Smarkm
168233294Sstasstatic const char *
169233294Sstaskeytab_name(const char *name, const char **type, size_t *type_len)
170233294Sstas{
171233294Sstas    const char *residual;
172233294Sstas
173233294Sstas    residual = strchr(name, ':');
174233294Sstas
175233294Sstas    if (residual == NULL ||
176233294Sstas	name[0] == '/'
177233294Sstas#ifdef _WIN32
178233294Sstas        /* Avoid treating <drive>:<path> as a keytab type
179233294Sstas         * specification */
180233294Sstas        || name + 1 == residual
181233294Sstas#endif
182233294Sstas        ) {
183233294Sstas
184233294Sstas        *type = "FILE";
185233294Sstas        *type_len = strlen(*type);
186233294Sstas        residual = name;
187233294Sstas    } else {
188233294Sstas        *type = name;
189233294Sstas        *type_len = residual - name;
190233294Sstas        residual++;
191233294Sstas    }
192233294Sstas
193233294Sstas    return residual;
194233294Sstas}
195233294Sstas
196233294Sstas/**
19755682Smarkm * Resolve the keytab name (of the form `type:residual') in `name'
19855682Smarkm * into a keytab in `id'.
199233294Sstas *
200233294Sstas * @param context a Keberos context.
201233294Sstas * @param name name to resolve
202233294Sstas * @param id resulting keytab, free with krb5_kt_close().
203233294Sstas *
204233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
205233294Sstas *
206233294Sstas * @ingroup krb5_keytab
20755682Smarkm */
20855682Smarkm
209233294Sstas
210233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
21155682Smarkmkrb5_kt_resolve(krb5_context context,
21255682Smarkm		const char *name,
21355682Smarkm		krb5_keytab *id)
21455682Smarkm{
21555682Smarkm    krb5_keytab k;
21655682Smarkm    int i;
21755682Smarkm    const char *type, *residual;
21855682Smarkm    size_t type_len;
21955682Smarkm    krb5_error_code ret;
22055682Smarkm
221233294Sstas    residual = keytab_name(name, &type, &type_len);
222233294Sstas
22355682Smarkm    for(i = 0; i < context->num_kt_types; i++) {
22490926Snectar	if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
22555682Smarkm	    break;
22655682Smarkm    }
22778527Sassar    if(i == context->num_kt_types) {
228233294Sstas	krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE,
229233294Sstas			       N_("unknown keytab type %.*s", "type"),
230233294Sstas			       (int)type_len, type);
23155682Smarkm	return KRB5_KT_UNKNOWN_TYPE;
23278527Sassar    }
233233294Sstas
23455682Smarkm    k = malloc (sizeof(*k));
23578527Sassar    if (k == NULL) {
236233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
23755682Smarkm	return ENOMEM;
23878527Sassar    }
23955682Smarkm    memcpy(k, &context->kt_types[i], sizeof(*k));
24055682Smarkm    k->data = NULL;
24155682Smarkm    ret = (*k->resolve)(context, residual, k);
24255682Smarkm    if(ret) {
24355682Smarkm	free(k);
24455682Smarkm	k = NULL;
24555682Smarkm    }
24655682Smarkm    *id = k;
24755682Smarkm    return ret;
24855682Smarkm}
24955682Smarkm
250233294Sstas/**
25155682Smarkm * copy the name of the default keytab into `name'.
252233294Sstas *
253233294Sstas * @param context a Keberos context.
254233294Sstas * @param name buffer where the name will be written
255233294Sstas * @param namesize length of name
256233294Sstas *
257233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
258233294Sstas *
259233294Sstas * @ingroup krb5_keytab
26055682Smarkm */
26155682Smarkm
262233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
26355682Smarkmkrb5_kt_default_name(krb5_context context, char *name, size_t namesize)
26455682Smarkm{
26578527Sassar    if (strlcpy (name, context->default_keytab, namesize) >= namesize) {
266233294Sstas	krb5_clear_error_message (context);
26755682Smarkm	return KRB5_CONFIG_NOTENUFSPACE;
26878527Sassar    }
26955682Smarkm    return 0;
27055682Smarkm}
27155682Smarkm
272233294Sstas/**
273233294Sstas * Copy the name of the default modify keytab into `name'.
274233294Sstas *
275233294Sstas * @param context a Keberos context.
276233294Sstas * @param name buffer where the name will be written
277233294Sstas * @param namesize length of name
278233294Sstas *
279233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
280233294Sstas *
281233294Sstas * @ingroup krb5_keytab
28278527Sassar */
28378527Sassar
284233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
28578527Sassarkrb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
28678527Sassar{
28790926Snectar    const char *kt = NULL;
28890926Snectar    if(context->default_keytab_modify == NULL) {
28990926Snectar	if(strncasecmp(context->default_keytab, "ANY:", 4) != 0)
29090926Snectar	    kt = context->default_keytab;
29190926Snectar	else {
29290926Snectar	    size_t len = strcspn(context->default_keytab + 4, ",");
29390926Snectar	    if(len >= namesize) {
294233294Sstas		krb5_clear_error_message(context);
29590926Snectar		return KRB5_CONFIG_NOTENUFSPACE;
29690926Snectar	    }
29790926Snectar	    strlcpy(name, context->default_keytab + 4, namesize);
29890926Snectar	    name[len] = '\0';
29990926Snectar	    return 0;
300233294Sstas	}
30190926Snectar    } else
30290926Snectar	kt = context->default_keytab_modify;
30390926Snectar    if (strlcpy (name, kt, namesize) >= namesize) {
304233294Sstas	krb5_clear_error_message (context);
30578527Sassar	return KRB5_CONFIG_NOTENUFSPACE;
30678527Sassar    }
30778527Sassar    return 0;
30878527Sassar}
30978527Sassar
310233294Sstas/**
31155682Smarkm * Set `id' to the default keytab.
312233294Sstas *
313233294Sstas * @param context a Keberos context.
314233294Sstas * @param id the new default keytab.
315233294Sstas *
316233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
317233294Sstas *
318233294Sstas * @ingroup krb5_keytab
31955682Smarkm */
32055682Smarkm
321233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
32255682Smarkmkrb5_kt_default(krb5_context context, krb5_keytab *id)
32355682Smarkm{
32455682Smarkm    return krb5_kt_resolve (context, context->default_keytab, id);
32555682Smarkm}
32655682Smarkm
327233294Sstas/**
32855682Smarkm * Read the key identified by `(principal, vno, enctype)' from the
32955682Smarkm * keytab in `keyprocarg' (the default if == NULL) into `*key'.
330233294Sstas *
331233294Sstas * @param context a Keberos context.
332233294Sstas * @param keyprocarg
333233294Sstas * @param principal
334233294Sstas * @param vno
335233294Sstas * @param enctype
336233294Sstas * @param key
337233294Sstas *
338233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
339233294Sstas *
340233294Sstas * @ingroup krb5_keytab
34155682Smarkm */
34255682Smarkm
343233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
34455682Smarkmkrb5_kt_read_service_key(krb5_context context,
34555682Smarkm			 krb5_pointer keyprocarg,
34655682Smarkm			 krb5_principal principal,
34755682Smarkm			 krb5_kvno vno,
34855682Smarkm			 krb5_enctype enctype,
34955682Smarkm			 krb5_keyblock **key)
35055682Smarkm{
35155682Smarkm    krb5_keytab keytab;
35255682Smarkm    krb5_keytab_entry entry;
35355682Smarkm    krb5_error_code ret;
35455682Smarkm
35555682Smarkm    if (keyprocarg)
35655682Smarkm	ret = krb5_kt_resolve (context, keyprocarg, &keytab);
35755682Smarkm    else
35855682Smarkm	ret = krb5_kt_default (context, &keytab);
35955682Smarkm
36055682Smarkm    if (ret)
36155682Smarkm	return ret;
36255682Smarkm
36355682Smarkm    ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
36455682Smarkm    krb5_kt_close (context, keytab);
36555682Smarkm    if (ret)
36655682Smarkm	return ret;
36755682Smarkm    ret = krb5_copy_keyblock (context, &entry.keyblock, key);
36855682Smarkm    krb5_kt_free_entry(context, &entry);
36955682Smarkm    return ret;
37055682Smarkm}
37155682Smarkm
372233294Sstas/**
373120945Snectar * Return the type of the `keytab' in the string `prefix of length
374120945Snectar * `prefixsize'.
375233294Sstas *
376233294Sstas * @param context a Keberos context.
377233294Sstas * @param keytab the keytab to get the prefix for
378233294Sstas * @param prefix prefix buffer
379233294Sstas * @param prefixsize length of prefix buffer
380233294Sstas *
381233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
382233294Sstas *
383233294Sstas * @ingroup krb5_keytab
384120945Snectar */
385120945Snectar
386233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
387120945Snectarkrb5_kt_get_type(krb5_context context,
388120945Snectar		 krb5_keytab keytab,
389120945Snectar		 char *prefix,
390120945Snectar		 size_t prefixsize)
391120945Snectar{
392120945Snectar    strlcpy(prefix, keytab->prefix, prefixsize);
393120945Snectar    return 0;
394120945Snectar}
395120945Snectar
396233294Sstas/**
39755682Smarkm * Retrieve the name of the keytab `keytab' into `name', `namesize'
398233294Sstas *
399233294Sstas * @param context a Keberos context.
400233294Sstas * @param keytab the keytab to get the name for.
401233294Sstas * @param name name buffer.
402233294Sstas * @param namesize size of name buffer.
403233294Sstas *
404233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
405233294Sstas *
406233294Sstas * @ingroup krb5_keytab
40755682Smarkm */
40855682Smarkm
409233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
410233294Sstaskrb5_kt_get_name(krb5_context context,
41155682Smarkm		 krb5_keytab keytab,
41255682Smarkm		 char *name,
41355682Smarkm		 size_t namesize)
41455682Smarkm{
41555682Smarkm    return (*keytab->get_name)(context, keytab, name, namesize);
41655682Smarkm}
41755682Smarkm
418233294Sstas/**
419178825Sdfr * Retrieve the full name of the keytab `keytab' and store the name in
420233294Sstas * `str'.
421233294Sstas *
422233294Sstas * @param context a Keberos context.
423233294Sstas * @param keytab keytab to get name for.
424233294Sstas * @param str the name of the keytab name, usee krb5_xfree() to free
425233294Sstas *        the string.  On error, *str is set to NULL.
426233294Sstas *
427233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
428233294Sstas *
429233294Sstas * @ingroup krb5_keytab
43055682Smarkm */
43155682Smarkm
432233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
433233294Sstaskrb5_kt_get_full_name(krb5_context context,
434178825Sdfr		      krb5_keytab keytab,
435178825Sdfr		      char **str)
436178825Sdfr{
437178825Sdfr    char type[KRB5_KT_PREFIX_MAX_LEN];
438178825Sdfr    char name[MAXPATHLEN];
439178825Sdfr    krb5_error_code ret;
440233294Sstas
441178825Sdfr    *str = NULL;
442178825Sdfr
443178825Sdfr    ret = krb5_kt_get_type(context, keytab, type, sizeof(type));
444178825Sdfr    if (ret)
445178825Sdfr	return ret;
446178825Sdfr
447178825Sdfr    ret = krb5_kt_get_name(context, keytab, name, sizeof(name));
448178825Sdfr    if (ret)
449178825Sdfr	return ret;
450178825Sdfr
451178825Sdfr    if (asprintf(str, "%s:%s", type, name) == -1) {
452233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
453178825Sdfr	*str = NULL;
454178825Sdfr	return ENOMEM;
455178825Sdfr    }
456178825Sdfr
457178825Sdfr    return 0;
458178825Sdfr}
459178825Sdfr
460233294Sstas/**
461178825Sdfr * Finish using the keytab in `id'.  All resources will be released,
462233294Sstas * even on errors.
463233294Sstas *
464233294Sstas * @param context a Keberos context.
465233294Sstas * @param id keytab to close.
466233294Sstas *
467233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
468233294Sstas *
469233294Sstas * @ingroup krb5_keytab
470178825Sdfr */
471178825Sdfr
472233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
473233294Sstaskrb5_kt_close(krb5_context context,
47455682Smarkm	      krb5_keytab id)
47555682Smarkm{
47655682Smarkm    krb5_error_code ret;
47755682Smarkm
47855682Smarkm    ret = (*id->close)(context, id);
479178825Sdfr    memset(id, 0, sizeof(*id));
480178825Sdfr    free(id);
48155682Smarkm    return ret;
48255682Smarkm}
48355682Smarkm
484233294Sstas/**
485233294Sstas * Destroy (remove) the keytab in `id'.  All resources will be released,
486233294Sstas * even on errors, does the equvalment of krb5_kt_close() on the resources.
487233294Sstas *
488233294Sstas * @param context a Keberos context.
489233294Sstas * @param id keytab to destroy.
490233294Sstas *
491233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
492233294Sstas *
493233294Sstas * @ingroup krb5_keytab
494233294Sstas */
495233294Sstas
496233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
497233294Sstaskrb5_kt_destroy(krb5_context context,
498233294Sstas		krb5_keytab id)
499233294Sstas{
500233294Sstas    krb5_error_code ret;
501233294Sstas
502233294Sstas    ret = (*id->destroy)(context, id);
503233294Sstas    krb5_kt_close(context, id);
504233294Sstas    return ret;
505233294Sstas}
506233294Sstas
50755682Smarkm/*
508233294Sstas * Match any aliases in keytab `entry' with `principal'.
509233294Sstas */
510233294Sstas
511233294Sstasstatic krb5_boolean
512233294Sstascompare_aliseses(krb5_context context,
513233294Sstas		 krb5_keytab_entry *entry,
514233294Sstas		 krb5_const_principal principal)
515233294Sstas{
516233294Sstas    unsigned int i;
517233294Sstas    if (entry->aliases == NULL)
518233294Sstas	return FALSE;
519233294Sstas    for (i = 0; i < entry->aliases->len; i++)
520233294Sstas	if (krb5_principal_compare(context, &entry->aliases->val[i], principal))
521233294Sstas	    return TRUE;
522233294Sstas    return FALSE;
523233294Sstas}
524233294Sstas
525233294Sstas/**
52655682Smarkm * Compare `entry' against `principal, vno, enctype'.
52755682Smarkm * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
52855682Smarkm * Return TRUE if they compare the same, FALSE otherwise.
529233294Sstas *
530233294Sstas * @param context a Keberos context.
531233294Sstas * @param entry an entry to match with.
532233294Sstas * @param principal principal to match, NULL matches all principals.
533233294Sstas * @param vno key version to match, 0 matches all key version numbers.
534233294Sstas * @param enctype encryption type to match, 0 matches all encryption types.
535233294Sstas *
536233294Sstas * @return Return TRUE or match, FALSE if not matched.
537233294Sstas *
538233294Sstas * @ingroup krb5_keytab
53955682Smarkm */
54055682Smarkm
541233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
54255682Smarkmkrb5_kt_compare(krb5_context context,
543233294Sstas		krb5_keytab_entry *entry,
54455682Smarkm		krb5_const_principal principal,
54555682Smarkm		krb5_kvno vno,
54655682Smarkm		krb5_enctype enctype)
54755682Smarkm{
548233294Sstas    if(principal != NULL &&
549233294Sstas       !(krb5_principal_compare(context, entry->principal, principal) ||
550233294Sstas	 compare_aliseses(context, entry, principal)))
55155682Smarkm	return FALSE;
55255682Smarkm    if(vno && vno != entry->vno)
55355682Smarkm	return FALSE;
55455682Smarkm    if(enctype && enctype != entry->keyblock.keytype)
55555682Smarkm	return FALSE;
55655682Smarkm    return TRUE;
55755682Smarkm}
55855682Smarkm
559233294Sstaskrb5_error_code
560233294Sstas_krb5_kt_principal_not_found(krb5_context context,
561233294Sstas			     krb5_error_code ret,
562233294Sstas			     krb5_keytab id,
563233294Sstas			     krb5_const_principal principal,
564233294Sstas			     krb5_enctype enctype,
565233294Sstas			     int kvno)
566233294Sstas{
567233294Sstas    char princ[256], kvno_str[25], *kt_name;
568233294Sstas    char *enctype_str = NULL;
569233294Sstas
570233294Sstas    krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
571233294Sstas    krb5_kt_get_full_name (context, id, &kt_name);
572233294Sstas    krb5_enctype_to_string(context, enctype, &enctype_str);
573233294Sstas
574233294Sstas    if (kvno)
575233294Sstas	snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
576233294Sstas    else
577233294Sstas	kvno_str[0] = '\0';
578233294Sstas
579233294Sstas    krb5_set_error_message (context, ret,
580233294Sstas			    N_("Failed to find %s%s in keytab %s (%s)",
581233294Sstas			       "principal, kvno, keytab file, enctype"),
582233294Sstas			    princ,
583233294Sstas			    kvno_str,
584233294Sstas			    kt_name ? kt_name : "unknown keytab",
585233294Sstas			    enctype_str ? enctype_str : "unknown enctype");
586233294Sstas    free(kt_name);
587233294Sstas    free(enctype_str);
588233294Sstas    return ret;
589233294Sstas}
590233294Sstas
591233294Sstas
592233294Sstas/**
59355682Smarkm * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
594233294Sstas * from the keytab `id'. Matching is done like krb5_kt_compare().
595233294Sstas *
596233294Sstas * @param context a Keberos context.
597233294Sstas * @param id a keytab.
598233294Sstas * @param principal principal to match, NULL matches all principals.
599233294Sstas * @param kvno key version to match, 0 matches all key version numbers.
600233294Sstas * @param enctype encryption type to match, 0 matches all encryption types.
601233294Sstas * @param entry the returned entry, free with krb5_kt_free_entry().
602233294Sstas *
603233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
604233294Sstas *
605233294Sstas * @ingroup krb5_keytab
60655682Smarkm */
60755682Smarkm
608233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
60955682Smarkmkrb5_kt_get_entry(krb5_context context,
61055682Smarkm		  krb5_keytab id,
61155682Smarkm		  krb5_const_principal principal,
61255682Smarkm		  krb5_kvno kvno,
61355682Smarkm		  krb5_enctype enctype,
61455682Smarkm		  krb5_keytab_entry *entry)
61555682Smarkm{
61655682Smarkm    krb5_keytab_entry tmp;
61755682Smarkm    krb5_error_code ret;
61855682Smarkm    krb5_kt_cursor cursor;
61955682Smarkm
62055682Smarkm    if(id->get)
62155682Smarkm	return (*id->get)(context, id, principal, kvno, enctype, entry);
62255682Smarkm
62355682Smarkm    ret = krb5_kt_start_seq_get (context, id, &cursor);
624178825Sdfr    if (ret) {
625233294Sstas	/* This is needed for krb5_verify_init_creds, but keep error
626233294Sstas	 * string from previous error for the human. */
627233294Sstas	context->error_code = KRB5_KT_NOTFOUND;
628233294Sstas	return KRB5_KT_NOTFOUND;
629178825Sdfr    }
63055682Smarkm
63155682Smarkm    entry->vno = 0;
63255682Smarkm    while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
63355682Smarkm	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
634102644Snectar	    /* the file keytab might only store the lower 8 bits of
635102644Snectar	       the kvno, so only compare those bits */
636102644Snectar	    if (kvno == tmp.vno
637102644Snectar		|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
63855682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
63955682Smarkm		krb5_kt_free_entry (context, &tmp);
64055682Smarkm		krb5_kt_end_seq_get(context, id, &cursor);
64155682Smarkm		return 0;
64255682Smarkm	    } else if (kvno == 0 && tmp.vno > entry->vno) {
64355682Smarkm		if (entry->vno)
64455682Smarkm		    krb5_kt_free_entry (context, entry);
64555682Smarkm		krb5_kt_copy_entry_contents (context, &tmp, entry);
64655682Smarkm	    }
64755682Smarkm	}
64855682Smarkm	krb5_kt_free_entry(context, &tmp);
64955682Smarkm    }
65055682Smarkm    krb5_kt_end_seq_get (context, id, &cursor);
651233294Sstas    if (entry->vno == 0)
652233294Sstas	return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND,
653233294Sstas					    id, principal, enctype, kvno);
654233294Sstas    return 0;
65555682Smarkm}
65655682Smarkm
657233294Sstas/**
65855682Smarkm * Copy the contents of `in' into `out'.
659233294Sstas *
660233294Sstas * @param context a Keberos context.
661233294Sstas * @param in the keytab entry to copy.
662233294Sstas * @param out the copy of the keytab entry, free with krb5_kt_free_entry().
663233294Sstas *
664233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
665233294Sstas *
666233294Sstas * @ingroup krb5_keytab
667233294Sstas */
66855682Smarkm
669233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
67055682Smarkmkrb5_kt_copy_entry_contents(krb5_context context,
67155682Smarkm			    const krb5_keytab_entry *in,
67255682Smarkm			    krb5_keytab_entry *out)
67355682Smarkm{
67455682Smarkm    krb5_error_code ret;
67555682Smarkm
67655682Smarkm    memset(out, 0, sizeof(*out));
67755682Smarkm    out->vno = in->vno;
67855682Smarkm
67955682Smarkm    ret = krb5_copy_principal (context, in->principal, &out->principal);
68055682Smarkm    if (ret)
68155682Smarkm	goto fail;
68255682Smarkm    ret = krb5_copy_keyblock_contents (context,
68355682Smarkm				       &in->keyblock,
68455682Smarkm				       &out->keyblock);
68555682Smarkm    if (ret)
68655682Smarkm	goto fail;
68755682Smarkm    out->timestamp = in->timestamp;
68855682Smarkm    return 0;
68955682Smarkmfail:
69055682Smarkm    krb5_kt_free_entry (context, out);
69155682Smarkm    return ret;
69255682Smarkm}
69355682Smarkm
694233294Sstas/**
69555682Smarkm * Free the contents of `entry'.
696233294Sstas *
697233294Sstas * @param context a Keberos context.
698233294Sstas * @param entry the entry to free
699233294Sstas *
700233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
701233294Sstas *
702233294Sstas * @ingroup krb5_keytab
70355682Smarkm */
70455682Smarkm
705233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
70655682Smarkmkrb5_kt_free_entry(krb5_context context,
70755682Smarkm		   krb5_keytab_entry *entry)
70855682Smarkm{
709178825Sdfr    krb5_free_principal (context, entry->principal);
710178825Sdfr    krb5_free_keyblock_contents (context, &entry->keyblock);
711178825Sdfr    memset(entry, 0, sizeof(*entry));
71255682Smarkm    return 0;
71355682Smarkm}
71455682Smarkm
715233294Sstas/**
71655682Smarkm * Set `cursor' to point at the beginning of `id'.
717233294Sstas *
718233294Sstas * @param context a Keberos context.
719233294Sstas * @param id a keytab.
720233294Sstas * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get().
721233294Sstas *
722233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
723233294Sstas *
724233294Sstas * @ingroup krb5_keytab
72555682Smarkm */
72655682Smarkm
727233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
72855682Smarkmkrb5_kt_start_seq_get(krb5_context context,
72955682Smarkm		      krb5_keytab id,
73055682Smarkm		      krb5_kt_cursor *cursor)
73155682Smarkm{
73278527Sassar    if(id->start_seq_get == NULL) {
733233294Sstas	krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
734233294Sstas			       N_("start_seq_get is not supported "
735233294Sstas				  "in the %s keytab type", ""),
736233294Sstas			       id->prefix);
73755682Smarkm	return HEIM_ERR_OPNOTSUPP;
73878527Sassar    }
73955682Smarkm    return (*id->start_seq_get)(context, id, cursor);
74055682Smarkm}
74155682Smarkm
742233294Sstas/**
743233294Sstas * Get the next entry from keytab, advance the cursor.  On last entry
744233294Sstas * the function will return KRB5_KT_END.
745233294Sstas *
746233294Sstas * @param context a Keberos context.
747233294Sstas * @param id a keytab.
748233294Sstas * @param entry the returned entry, free with krb5_kt_free_entry().
749233294Sstas * @param cursor the cursor of the iteration.
750233294Sstas *
751233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
752233294Sstas *
753233294Sstas * @ingroup krb5_keytab
75455682Smarkm */
75555682Smarkm
756233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75755682Smarkmkrb5_kt_next_entry(krb5_context context,
75855682Smarkm		   krb5_keytab id,
75955682Smarkm		   krb5_keytab_entry *entry,
76055682Smarkm		   krb5_kt_cursor *cursor)
76155682Smarkm{
76278527Sassar    if(id->next_entry == NULL) {
763233294Sstas	krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
764233294Sstas			       N_("next_entry is not supported in the %s "
765233294Sstas				  " keytab", ""),
766233294Sstas			       id->prefix);
76755682Smarkm	return HEIM_ERR_OPNOTSUPP;
76878527Sassar    }
76955682Smarkm    return (*id->next_entry)(context, id, entry, cursor);
77055682Smarkm}
77155682Smarkm
772233294Sstas/**
77355682Smarkm * Release all resources associated with `cursor'.
774233294Sstas *
775233294Sstas * @param context a Keberos context.
776233294Sstas * @param id a keytab.
777233294Sstas * @param cursor the cursor to free.
778233294Sstas *
779233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
780233294Sstas *
781233294Sstas * @ingroup krb5_keytab
78255682Smarkm */
78355682Smarkm
784233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
78555682Smarkmkrb5_kt_end_seq_get(krb5_context context,
78655682Smarkm		    krb5_keytab id,
78755682Smarkm		    krb5_kt_cursor *cursor)
78855682Smarkm{
78978527Sassar    if(id->end_seq_get == NULL) {
790233294Sstas	krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
791233294Sstas			       "end_seq_get is not supported in the %s "
792233294Sstas			       " keytab", id->prefix);
79355682Smarkm	return HEIM_ERR_OPNOTSUPP;
79478527Sassar    }
79555682Smarkm    return (*id->end_seq_get)(context, id, cursor);
79655682Smarkm}
79755682Smarkm
798233294Sstas/**
79955682Smarkm * Add the entry in `entry' to the keytab `id'.
800233294Sstas *
801233294Sstas * @param context a Keberos context.
802233294Sstas * @param id a keytab.
803233294Sstas * @param entry the entry to add
804233294Sstas *
805233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
806233294Sstas *
807233294Sstas * @ingroup krb5_keytab
80855682Smarkm */
80955682Smarkm
810233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
81155682Smarkmkrb5_kt_add_entry(krb5_context context,
81255682Smarkm		  krb5_keytab id,
81355682Smarkm		  krb5_keytab_entry *entry)
81455682Smarkm{
81578527Sassar    if(id->add == NULL) {
816233294Sstas	krb5_set_error_message(context, KRB5_KT_NOWRITE,
817233294Sstas			       N_("Add is not supported in the %s keytab", ""),
818233294Sstas			       id->prefix);
81955682Smarkm	return KRB5_KT_NOWRITE;
82078527Sassar    }
82157416Smarkm    entry->timestamp = time(NULL);
82255682Smarkm    return (*id->add)(context, id,entry);
82355682Smarkm}
82455682Smarkm
825233294Sstas/**
826233294Sstas * Remove an entry from the keytab, matching is done using
827233294Sstas * krb5_kt_compare().
828233294Sstas
829233294Sstas * @param context a Keberos context.
830233294Sstas * @param id a keytab.
831233294Sstas * @param entry the entry to remove
832233294Sstas *
833233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
834233294Sstas *
835233294Sstas * @ingroup krb5_keytab
83655682Smarkm */
83755682Smarkm
838233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
83955682Smarkmkrb5_kt_remove_entry(krb5_context context,
84055682Smarkm		     krb5_keytab id,
84155682Smarkm		     krb5_keytab_entry *entry)
84255682Smarkm{
84378527Sassar    if(id->remove == NULL) {
844233294Sstas	krb5_set_error_message(context, KRB5_KT_NOWRITE,
845233294Sstas			       N_("Remove is not supported in the %s keytab", ""),
846233294Sstas			       id->prefix);
84755682Smarkm	return KRB5_KT_NOWRITE;
84878527Sassar    }
84955682Smarkm    return (*id->remove)(context, id, entry);
85055682Smarkm}
851233294Sstas
852233294Sstas/**
853233294Sstas * Return true if the keytab exists and have entries
854233294Sstas *
855233294Sstas * @param context a Keberos context.
856233294Sstas * @param id a keytab.
857233294Sstas *
858233294Sstas * @return Return an error code or 0, see krb5_get_error_message().
859233294Sstas *
860233294Sstas * @ingroup krb5_keytab
861233294Sstas */
862233294Sstas
863233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
864233294Sstaskrb5_kt_have_content(krb5_context context,
865233294Sstas		     krb5_keytab id)
866233294Sstas{
867233294Sstas    krb5_keytab_entry entry;
868233294Sstas    krb5_kt_cursor cursor;
869233294Sstas    krb5_error_code ret;
870233294Sstas    char *name;
871233294Sstas
872233294Sstas    ret = krb5_kt_start_seq_get(context, id, &cursor);
873233294Sstas    if (ret)
874233294Sstas	goto notfound;
875233294Sstas
876233294Sstas    ret = krb5_kt_next_entry(context, id, &entry, &cursor);
877233294Sstas    krb5_kt_end_seq_get(context, id, &cursor);
878233294Sstas    if (ret)
879233294Sstas	goto notfound;
880233294Sstas
881233294Sstas    krb5_kt_free_entry(context, &entry);
882233294Sstas
883233294Sstas    return 0;
884233294Sstas
885233294Sstas notfound:
886233294Sstas    ret = krb5_kt_get_full_name(context, id, &name);
887233294Sstas    if (ret == 0) {
888233294Sstas	krb5_set_error_message(context, KRB5_KT_NOTFOUND,
889233294Sstas			       N_("No entry in keytab: %s", ""), name);
890233294Sstas	free(name);
891233294Sstas    }
892233294Sstas    return KRB5_KT_NOTFOUND;
893233294Sstas}
894