xref: /freebsd/crypto/heimdal/lib/krb5/keytab.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*
2  * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
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.
16  *
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.
20  *
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
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 
36 RCSID("$Id: keytab.c,v 1.46 2000/02/07 03:18:05 assar Exp $");
37 
38 /*
39  * Register a new keytab in `ops'
40  * Return 0 or an error.
41  */
42 
43 krb5_error_code
44 krb5_kt_register(krb5_context context,
45 		 const krb5_kt_ops *ops)
46 {
47     struct krb5_keytab_data *tmp;
48 
49     tmp = realloc(context->kt_types,
50 		  (context->num_kt_types + 1) * sizeof(*context->kt_types));
51     if(tmp == NULL)
52 	return ENOMEM;
53     memcpy(&tmp[context->num_kt_types], ops,
54 	   sizeof(tmp[context->num_kt_types]));
55     context->kt_types = tmp;
56     context->num_kt_types++;
57     return 0;
58 }
59 
60 /*
61  * Resolve the keytab name (of the form `type:residual') in `name'
62  * into a keytab in `id'.
63  * Return 0 or an error
64  */
65 
66 krb5_error_code
67 krb5_kt_resolve(krb5_context context,
68 		const char *name,
69 		krb5_keytab *id)
70 {
71     krb5_keytab k;
72     int i;
73     const char *type, *residual;
74     size_t type_len;
75     krb5_error_code ret;
76 
77     residual = strchr(name, ':');
78     if(residual == NULL) {
79 	type = "FILE";
80 	type_len = strlen(type);
81 	residual = name;
82     } else {
83 	type = name;
84 	type_len = residual - name;
85 	residual++;
86     }
87 
88     for(i = 0; i < context->num_kt_types; i++) {
89 	if(strncmp(type, context->kt_types[i].prefix, type_len) == 0)
90 	    break;
91     }
92     if(i == context->num_kt_types)
93 	return KRB5_KT_UNKNOWN_TYPE;
94 
95     k = malloc (sizeof(*k));
96     if (k == NULL)
97 	return ENOMEM;
98     memcpy(k, &context->kt_types[i], sizeof(*k));
99     k->data = NULL;
100     ret = (*k->resolve)(context, residual, k);
101     if(ret) {
102 	free(k);
103 	k = NULL;
104     }
105     *id = k;
106     return ret;
107 }
108 
109 /*
110  * copy the name of the default keytab into `name'.
111  * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
112  */
113 
114 krb5_error_code
115 krb5_kt_default_name(krb5_context context, char *name, size_t namesize)
116 {
117     if (strlcpy (name, context->default_keytab, namesize) >= namesize)
118 	return KRB5_CONFIG_NOTENUFSPACE;
119     return 0;
120 }
121 
122 /*
123  * Set `id' to the default keytab.
124  * Return 0 or an error.
125  */
126 
127 krb5_error_code
128 krb5_kt_default(krb5_context context, krb5_keytab *id)
129 {
130     return krb5_kt_resolve (context, context->default_keytab, id);
131 }
132 
133 /*
134  * Read the key identified by `(principal, vno, enctype)' from the
135  * keytab in `keyprocarg' (the default if == NULL) into `*key'.
136  * Return 0 or an error.
137  */
138 
139 krb5_error_code
140 krb5_kt_read_service_key(krb5_context context,
141 			 krb5_pointer keyprocarg,
142 			 krb5_principal principal,
143 			 krb5_kvno vno,
144 			 krb5_enctype enctype,
145 			 krb5_keyblock **key)
146 {
147     krb5_keytab keytab;
148     krb5_keytab_entry entry;
149     krb5_error_code ret;
150 
151     if (keyprocarg)
152 	ret = krb5_kt_resolve (context, keyprocarg, &keytab);
153     else
154 	ret = krb5_kt_default (context, &keytab);
155 
156     if (ret)
157 	return ret;
158 
159     ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
160     krb5_kt_close (context, keytab);
161     if (ret)
162 	return ret;
163     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
164     krb5_kt_free_entry(context, &entry);
165     return ret;
166 }
167 
168 /*
169  * Retrieve the name of the keytab `keytab' into `name', `namesize'
170  * Return 0 or an error.
171  */
172 
173 krb5_error_code
174 krb5_kt_get_name(krb5_context context,
175 		 krb5_keytab keytab,
176 		 char *name,
177 		 size_t namesize)
178 {
179     return (*keytab->get_name)(context, keytab, name, namesize);
180 }
181 
182 /*
183  * Finish using the keytab in `id'.  All resources will be released.
184  * Return 0 or an error.
185  */
186 
187 krb5_error_code
188 krb5_kt_close(krb5_context context,
189 	      krb5_keytab id)
190 {
191     krb5_error_code ret;
192 
193     ret = (*id->close)(context, id);
194     if(ret == 0)
195 	free(id);
196     return ret;
197 }
198 
199 /*
200  * Compare `entry' against `principal, vno, enctype'.
201  * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
202  * Return TRUE if they compare the same, FALSE otherwise.
203  */
204 
205 krb5_boolean
206 krb5_kt_compare(krb5_context context,
207 		krb5_keytab_entry *entry,
208 		krb5_const_principal principal,
209 		krb5_kvno vno,
210 		krb5_enctype enctype)
211 {
212     if(principal != NULL &&
213        !krb5_principal_compare(context, entry->principal, principal))
214 	return FALSE;
215     if(vno && vno != entry->vno)
216 	return FALSE;
217     if(enctype && enctype != entry->keyblock.keytype)
218 	return FALSE;
219     return TRUE;
220 }
221 
222 /*
223  * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
224  * from the keytab `id'.
225  * Return 0 or an error.
226  */
227 
228 krb5_error_code
229 krb5_kt_get_entry(krb5_context context,
230 		  krb5_keytab id,
231 		  krb5_const_principal principal,
232 		  krb5_kvno kvno,
233 		  krb5_enctype enctype,
234 		  krb5_keytab_entry *entry)
235 {
236     krb5_keytab_entry tmp;
237     krb5_error_code ret;
238     krb5_kt_cursor cursor;
239 
240     if(id->get)
241 	return (*id->get)(context, id, principal, kvno, enctype, entry);
242 
243     ret = krb5_kt_start_seq_get (context, id, &cursor);
244     if (ret)
245 	return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */
246 
247     entry->vno = 0;
248     while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
249 	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
250 	    if (kvno == tmp.vno) {
251 		krb5_kt_copy_entry_contents (context, &tmp, entry);
252 		krb5_kt_free_entry (context, &tmp);
253 		krb5_kt_end_seq_get(context, id, &cursor);
254 		return 0;
255 	    } else if (kvno == 0 && tmp.vno > entry->vno) {
256 		if (entry->vno)
257 		    krb5_kt_free_entry (context, entry);
258 		krb5_kt_copy_entry_contents (context, &tmp, entry);
259 	    }
260 	}
261 	krb5_kt_free_entry(context, &tmp);
262     }
263     krb5_kt_end_seq_get (context, id, &cursor);
264     if (entry->vno)
265 	return 0;
266     else
267 	return KRB5_KT_NOTFOUND;
268 }
269 
270 /*
271  * Copy the contents of `in' into `out'.
272  * Return 0 or an error.
273  */
274 
275 krb5_error_code
276 krb5_kt_copy_entry_contents(krb5_context context,
277 			    const krb5_keytab_entry *in,
278 			    krb5_keytab_entry *out)
279 {
280     krb5_error_code ret;
281 
282     memset(out, 0, sizeof(*out));
283     out->vno = in->vno;
284 
285     ret = krb5_copy_principal (context, in->principal, &out->principal);
286     if (ret)
287 	goto fail;
288     ret = krb5_copy_keyblock_contents (context,
289 				       &in->keyblock,
290 				       &out->keyblock);
291     if (ret)
292 	goto fail;
293     out->timestamp = in->timestamp;
294     return 0;
295 fail:
296     krb5_kt_free_entry (context, out);
297     return ret;
298 }
299 
300 /*
301  * Free the contents of `entry'.
302  */
303 
304 krb5_error_code
305 krb5_kt_free_entry(krb5_context context,
306 		   krb5_keytab_entry *entry)
307 {
308   krb5_free_principal (context, entry->principal);
309   krb5_free_keyblock_contents (context, &entry->keyblock);
310   return 0;
311 }
312 
313 #if 0
314 static int
315 xxxlock(int fd, int write)
316 {
317     if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) {
318 	sleep(1);
319 	if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
320 	    return -1;
321     }
322     return 0;
323 }
324 
325 static void
326 xxxunlock(int fd)
327 {
328     flock(fd, LOCK_UN);
329 }
330 #endif
331 
332 /*
333  * Set `cursor' to point at the beginning of `id'.
334  * Return 0 or an error.
335  */
336 
337 krb5_error_code
338 krb5_kt_start_seq_get(krb5_context context,
339 		      krb5_keytab id,
340 		      krb5_kt_cursor *cursor)
341 {
342     if(id->start_seq_get == NULL)
343 	return HEIM_ERR_OPNOTSUPP;
344     return (*id->start_seq_get)(context, id, cursor);
345 }
346 
347 /*
348  * Get the next entry from `id' pointed to by `cursor' and advance the
349  * `cursor'.
350  * Return 0 or an error.
351  */
352 
353 krb5_error_code
354 krb5_kt_next_entry(krb5_context context,
355 		   krb5_keytab id,
356 		   krb5_keytab_entry *entry,
357 		   krb5_kt_cursor *cursor)
358 {
359     if(id->next_entry == NULL)
360 	return HEIM_ERR_OPNOTSUPP;
361     return (*id->next_entry)(context, id, entry, cursor);
362 }
363 
364 /*
365  * Release all resources associated with `cursor'.
366  */
367 
368 krb5_error_code
369 krb5_kt_end_seq_get(krb5_context context,
370 		    krb5_keytab id,
371 		    krb5_kt_cursor *cursor)
372 {
373     if(id->end_seq_get == NULL)
374 	return HEIM_ERR_OPNOTSUPP;
375     return (*id->end_seq_get)(context, id, cursor);
376 }
377 
378 /*
379  * Add the entry in `entry' to the keytab `id'.
380  * Return 0 or an error.
381  */
382 
383 krb5_error_code
384 krb5_kt_add_entry(krb5_context context,
385 		  krb5_keytab id,
386 		  krb5_keytab_entry *entry)
387 {
388     if(id->add == NULL)
389 	return KRB5_KT_NOWRITE;
390     entry->timestamp = time(NULL);
391     return (*id->add)(context, id,entry);
392 }
393 
394 /*
395  * Remove the entry `entry' from the keytab `id'.
396  * Return 0 or an error.
397  */
398 
399 krb5_error_code
400 krb5_kt_remove_entry(krb5_context context,
401 		     krb5_keytab id,
402 		     krb5_keytab_entry *entry)
403 {
404     if(id->remove == NULL)
405 	return KRB5_KT_NOWRITE;
406     return (*id->remove)(context, id, entry);
407 }
408