2 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
36 __RCSID("$Heimdal: keytab.c 20211 2007-02-09 07:11:03Z lha $"
40 * Register a new keytab in `ops'
41 * Return 0 or an error.
44 krb5_error_code KRB5_LIB_FUNCTION
45 krb5_kt_register(krb5_context context
,
46 const krb5_kt_ops
*ops
)
48 struct krb5_keytab_data
*tmp
;
50 if (strlen(ops
->prefix
) > KRB5_KT_PREFIX_MAX_LEN
- 1) {
51 krb5_set_error_string(context
, "krb5_kt_register; prefix too long");
52 return KRB5_KT_BADNAME
;
55 tmp
= realloc(context
->kt_types
,
56 (context
->num_kt_types
+ 1) * sizeof(*context
->kt_types
));
58 krb5_set_error_string(context
, "malloc: out of memory");
61 memcpy(&tmp
[context
->num_kt_types
], ops
,
62 sizeof(tmp
[context
->num_kt_types
]));
63 context
->kt_types
= tmp
;
64 context
->num_kt_types
++;
69 * Resolve the keytab name (of the form `type:residual') in `name'
70 * into a keytab in `id'.
71 * Return 0 or an error
74 krb5_error_code KRB5_LIB_FUNCTION
75 krb5_kt_resolve(krb5_context context
,
81 const char *type
, *residual
;
85 residual
= strchr(name
, ':');
86 if(residual
== NULL
) {
88 type_len
= strlen(type
);
92 type_len
= residual
- name
;
96 for(i
= 0; i
< context
->num_kt_types
; i
++) {
97 if(strncasecmp(type
, context
->kt_types
[i
].prefix
, type_len
) == 0)
100 if(i
== context
->num_kt_types
) {
101 krb5_set_error_string(context
, "unknown keytab type %.*s",
102 (int)type_len
, type
);
103 return KRB5_KT_UNKNOWN_TYPE
;
106 k
= malloc (sizeof(*k
));
108 krb5_set_error_string(context
, "malloc: out of memory");
111 memcpy(k
, &context
->kt_types
[i
], sizeof(*k
));
113 ret
= (*k
->resolve
)(context
, residual
, k
);
123 * copy the name of the default keytab into `name'.
124 * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
127 krb5_error_code KRB5_LIB_FUNCTION
128 krb5_kt_default_name(krb5_context context
, char *name
, size_t namesize
)
130 if (strlcpy (name
, context
->default_keytab
, namesize
) >= namesize
) {
131 krb5_clear_error_string (context
);
132 return KRB5_CONFIG_NOTENUFSPACE
;
138 * copy the name of the default modify keytab into `name'.
139 * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
142 krb5_error_code KRB5_LIB_FUNCTION
143 krb5_kt_default_modify_name(krb5_context context
, char *name
, size_t namesize
)
145 const char *kt
= NULL
;
146 if(context
->default_keytab_modify
== NULL
) {
147 if(strncasecmp(context
->default_keytab
, "ANY:", 4) != 0)
148 kt
= context
->default_keytab
;
150 size_t len
= strcspn(context
->default_keytab
+ 4, ",");
151 if(len
>= namesize
) {
152 krb5_clear_error_string(context
);
153 return KRB5_CONFIG_NOTENUFSPACE
;
155 strlcpy(name
, context
->default_keytab
+ 4, namesize
);
160 kt
= context
->default_keytab_modify
;
161 if (strlcpy (name
, kt
, namesize
) >= namesize
) {
162 krb5_clear_error_string (context
);
163 return KRB5_CONFIG_NOTENUFSPACE
;
169 * Set `id' to the default keytab.
170 * Return 0 or an error.
173 krb5_error_code KRB5_LIB_FUNCTION
174 krb5_kt_default(krb5_context context
, krb5_keytab
*id
)
176 return krb5_kt_resolve (context
, context
->default_keytab
, id
);
180 * Read the key identified by `(principal, vno, enctype)' from the
181 * keytab in `keyprocarg' (the default if == NULL) into `*key'.
182 * Return 0 or an error.
185 krb5_error_code KRB5_LIB_FUNCTION
186 krb5_kt_read_service_key(krb5_context context
,
187 krb5_pointer keyprocarg
,
188 krb5_principal principal
,
190 krb5_enctype enctype
,
194 krb5_keytab_entry entry
;
198 ret
= krb5_kt_resolve (context
, keyprocarg
, &keytab
);
200 ret
= krb5_kt_default (context
, &keytab
);
205 ret
= krb5_kt_get_entry (context
, keytab
, principal
, vno
, enctype
, &entry
);
206 krb5_kt_close (context
, keytab
);
209 ret
= krb5_copy_keyblock (context
, &entry
.keyblock
, key
);
210 krb5_kt_free_entry(context
, &entry
);
215 * Return the type of the `keytab' in the string `prefix of length
219 krb5_error_code KRB5_LIB_FUNCTION
220 krb5_kt_get_type(krb5_context context
,
225 strlcpy(prefix
, keytab
->prefix
, prefixsize
);
230 * Retrieve the name of the keytab `keytab' into `name', `namesize'
231 * Return 0 or an error.
234 krb5_error_code KRB5_LIB_FUNCTION
235 krb5_kt_get_name(krb5_context context
,
240 return (*keytab
->get_name
)(context
, keytab
, name
, namesize
);
244 * Retrieve the full name of the keytab `keytab' and store the name in
245 * `str'. `str' needs to be freed by the caller using free(3).
246 * Returns 0 or an error. On error, *str is set to NULL.
249 krb5_error_code KRB5_LIB_FUNCTION
250 krb5_kt_get_full_name(krb5_context context
,
254 char type
[KRB5_KT_PREFIX_MAX_LEN
];
255 char name
[MAXPATHLEN
];
260 ret
= krb5_kt_get_type(context
, keytab
, type
, sizeof(type
));
264 ret
= krb5_kt_get_name(context
, keytab
, name
, sizeof(name
));
268 if (asprintf(str
, "%s:%s", type
, name
) == -1) {
269 krb5_set_error_string(context
, "malloc - out of memory");
278 * Finish using the keytab in `id'. All resources will be released,
279 * even on errors. Return 0 or an error.
282 krb5_error_code KRB5_LIB_FUNCTION
283 krb5_kt_close(krb5_context context
,
288 ret
= (*id
->close
)(context
, id
);
289 memset(id
, 0, sizeof(*id
));
295 * Compare `entry' against `principal, vno, enctype'.
296 * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
297 * Return TRUE if they compare the same, FALSE otherwise.
300 krb5_boolean KRB5_LIB_FUNCTION
301 krb5_kt_compare(krb5_context context
,
302 krb5_keytab_entry
*entry
,
303 krb5_const_principal principal
,
305 krb5_enctype enctype
)
307 if(principal
!= NULL
&&
308 !krb5_principal_compare(context
, entry
->principal
, principal
))
310 if(vno
&& vno
!= entry
->vno
)
312 if(enctype
&& enctype
!= entry
->keyblock
.keytype
)
318 * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
319 * from the keytab `id'.
320 * kvno == 0 is a wildcard and gives the keytab with the highest vno.
321 * Return 0 or an error.
324 krb5_error_code KRB5_LIB_FUNCTION
325 krb5_kt_get_entry(krb5_context context
,
327 krb5_const_principal principal
,
329 krb5_enctype enctype
,
330 krb5_keytab_entry
*entry
)
332 krb5_keytab_entry tmp
;
334 krb5_kt_cursor cursor
;
337 return (*id
->get
)(context
, id
, principal
, kvno
, enctype
, entry
);
339 ret
= krb5_kt_start_seq_get (context
, id
, &cursor
);
341 krb5_clear_error_string(context
);
342 return KRB5_KT_NOTFOUND
; /* XXX i.e. file not found */
346 while (krb5_kt_next_entry(context
, id
, &tmp
, &cursor
) == 0) {
347 if (krb5_kt_compare(context
, &tmp
, principal
, 0, enctype
)) {
348 /* the file keytab might only store the lower 8 bits of
349 the kvno, so only compare those bits */
351 || (tmp
.vno
< 256 && kvno
% 256 == tmp
.vno
)) {
352 krb5_kt_copy_entry_contents (context
, &tmp
, entry
);
353 krb5_kt_free_entry (context
, &tmp
);
354 krb5_kt_end_seq_get(context
, id
, &cursor
);
356 } else if (kvno
== 0 && tmp
.vno
> entry
->vno
) {
358 krb5_kt_free_entry (context
, entry
);
359 krb5_kt_copy_entry_contents (context
, &tmp
, entry
);
362 krb5_kt_free_entry(context
, &tmp
);
364 krb5_kt_end_seq_get (context
, id
, &cursor
);
368 char princ
[256], kvno_str
[25], *kt_name
;
369 char *enctype_str
= NULL
;
371 krb5_unparse_name_fixed (context
, principal
, princ
, sizeof(princ
));
372 krb5_kt_get_full_name (context
, id
, &kt_name
);
373 krb5_enctype_to_string(context
, enctype
, &enctype_str
);
376 snprintf(kvno_str
, sizeof(kvno_str
), "(kvno %d)", kvno
);
380 krb5_set_error_string (context
,
381 "Failed to find %s%s in keytab %s (%s)",
384 kt_name
? kt_name
: "unknown keytab",
385 enctype_str
? enctype_str
: "unknown enctype");
388 return KRB5_KT_NOTFOUND
;
393 * Copy the contents of `in' into `out'.
394 * Return 0 or an error. */
396 krb5_error_code KRB5_LIB_FUNCTION
397 krb5_kt_copy_entry_contents(krb5_context context
,
398 const krb5_keytab_entry
*in
,
399 krb5_keytab_entry
*out
)
403 memset(out
, 0, sizeof(*out
));
406 ret
= krb5_copy_principal (context
, in
->principal
, &out
->principal
);
409 ret
= krb5_copy_keyblock_contents (context
,
414 out
->timestamp
= in
->timestamp
;
417 krb5_kt_free_entry (context
, out
);
422 * Free the contents of `entry'.
425 krb5_error_code KRB5_LIB_FUNCTION
426 krb5_kt_free_entry(krb5_context context
,
427 krb5_keytab_entry
*entry
)
429 krb5_free_principal (context
, entry
->principal
);
430 krb5_free_keyblock_contents (context
, &entry
->keyblock
);
431 memset(entry
, 0, sizeof(*entry
));
436 * Set `cursor' to point at the beginning of `id'.
437 * Return 0 or an error.
440 krb5_error_code KRB5_LIB_FUNCTION
441 krb5_kt_start_seq_get(krb5_context context
,
443 krb5_kt_cursor
*cursor
)
445 if(id
->start_seq_get
== NULL
) {
446 krb5_set_error_string(context
,
447 "start_seq_get is not supported in the %s "
448 " keytab", id
->prefix
);
449 return HEIM_ERR_OPNOTSUPP
;
451 return (*id
->start_seq_get
)(context
, id
, cursor
);
455 * Get the next entry from `id' pointed to by `cursor' and advance the
457 * Return 0 or an error.
460 krb5_error_code KRB5_LIB_FUNCTION
461 krb5_kt_next_entry(krb5_context context
,
463 krb5_keytab_entry
*entry
,
464 krb5_kt_cursor
*cursor
)
466 if(id
->next_entry
== NULL
) {
467 krb5_set_error_string(context
,
468 "next_entry is not supported in the %s "
469 " keytab", id
->prefix
);
470 return HEIM_ERR_OPNOTSUPP
;
472 return (*id
->next_entry
)(context
, id
, entry
, cursor
);
476 * Release all resources associated with `cursor'.
479 krb5_error_code KRB5_LIB_FUNCTION
480 krb5_kt_end_seq_get(krb5_context context
,
482 krb5_kt_cursor
*cursor
)
484 if(id
->end_seq_get
== NULL
) {
485 krb5_set_error_string(context
,
486 "end_seq_get is not supported in the %s "
487 " keytab", id
->prefix
);
488 return HEIM_ERR_OPNOTSUPP
;
490 return (*id
->end_seq_get
)(context
, id
, cursor
);
494 * Add the entry in `entry' to the keytab `id'.
495 * Return 0 or an error.
498 krb5_error_code KRB5_LIB_FUNCTION
499 krb5_kt_add_entry(krb5_context context
,
501 krb5_keytab_entry
*entry
)
503 if(id
->add
== NULL
) {
504 krb5_set_error_string(context
, "Add is not supported in the %s keytab",
506 return KRB5_KT_NOWRITE
;
508 entry
->timestamp
= time(NULL
);
509 return (*id
->add
)(context
, id
,entry
);
513 * Remove the entry `entry' from the keytab `id'.
514 * Return 0 or an error.
517 krb5_error_code KRB5_LIB_FUNCTION
518 krb5_kt_remove_entry(krb5_context context
,
520 krb5_keytab_entry
*entry
)
522 if(id
->remove
== NULL
) {
523 krb5_set_error_string(context
,
524 "Remove is not supported in the %s keytab",
526 return KRB5_KT_NOWRITE
;
528 return (*id
->remove
)(context
, id
, entry
);