xref: /freebsd/crypto/heimdal/lib/krb5/keytab.c (revision b528cefc6b8f9670b31a865051741d946cb37085)
1 /*
2  * Copyright (c) 1997, 1998, 1999 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.45 2000/01/02 00:31:20 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     strncpy(name, context->default_keytab, namesize);
118     if(strlen(context->default_keytab) >= namesize)
119 	return KRB5_CONFIG_NOTENUFSPACE;
120     return 0;
121 }
122 
123 /*
124  * Set `id' to the default keytab.
125  * Return 0 or an error.
126  */
127 
128 krb5_error_code
129 krb5_kt_default(krb5_context context, krb5_keytab *id)
130 {
131     return krb5_kt_resolve (context, context->default_keytab, id);
132 }
133 
134 /*
135  * Read the key identified by `(principal, vno, enctype)' from the
136  * keytab in `keyprocarg' (the default if == NULL) into `*key'.
137  * Return 0 or an error.
138  */
139 
140 krb5_error_code
141 krb5_kt_read_service_key(krb5_context context,
142 			 krb5_pointer keyprocarg,
143 			 krb5_principal principal,
144 			 krb5_kvno vno,
145 			 krb5_enctype enctype,
146 			 krb5_keyblock **key)
147 {
148     krb5_keytab keytab;
149     krb5_keytab_entry entry;
150     krb5_error_code ret;
151 
152     if (keyprocarg)
153 	ret = krb5_kt_resolve (context, keyprocarg, &keytab);
154     else
155 	ret = krb5_kt_default (context, &keytab);
156 
157     if (ret)
158 	return ret;
159 
160     ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
161     krb5_kt_close (context, keytab);
162     if (ret)
163 	return ret;
164     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
165     krb5_kt_free_entry(context, &entry);
166     return ret;
167 }
168 
169 /*
170  * Retrieve the name of the keytab `keytab' into `name', `namesize'
171  * Return 0 or an error.
172  */
173 
174 krb5_error_code
175 krb5_kt_get_name(krb5_context context,
176 		 krb5_keytab keytab,
177 		 char *name,
178 		 size_t namesize)
179 {
180     return (*keytab->get_name)(context, keytab, name, namesize);
181 }
182 
183 /*
184  * Finish using the keytab in `id'.  All resources will be released.
185  * Return 0 or an error.
186  */
187 
188 krb5_error_code
189 krb5_kt_close(krb5_context context,
190 	      krb5_keytab id)
191 {
192     krb5_error_code ret;
193 
194     ret = (*id->close)(context, id);
195     if(ret == 0)
196 	free(id);
197     return ret;
198 }
199 
200 /*
201  * Compare `entry' against `principal, vno, enctype'.
202  * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
203  * Return TRUE if they compare the same, FALSE otherwise.
204  */
205 
206 krb5_boolean
207 krb5_kt_compare(krb5_context context,
208 		krb5_keytab_entry *entry,
209 		krb5_const_principal principal,
210 		krb5_kvno vno,
211 		krb5_enctype enctype)
212 {
213     if(principal != NULL &&
214        !krb5_principal_compare(context, entry->principal, principal))
215 	return FALSE;
216     if(vno && vno != entry->vno)
217 	return FALSE;
218     if(enctype && enctype != entry->keyblock.keytype)
219 	return FALSE;
220     return TRUE;
221 }
222 
223 /*
224  * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
225  * from the keytab `id'.
226  * Return 0 or an error.
227  */
228 
229 krb5_error_code
230 krb5_kt_get_entry(krb5_context context,
231 		  krb5_keytab id,
232 		  krb5_const_principal principal,
233 		  krb5_kvno kvno,
234 		  krb5_enctype enctype,
235 		  krb5_keytab_entry *entry)
236 {
237     krb5_keytab_entry tmp;
238     krb5_error_code ret;
239     krb5_kt_cursor cursor;
240 
241     if(id->get)
242 	return (*id->get)(context, id, principal, kvno, enctype, entry);
243 
244     ret = krb5_kt_start_seq_get (context, id, &cursor);
245     if (ret)
246 	return KRB5_KT_NOTFOUND; /* XXX i.e. file not found */
247 
248     entry->vno = 0;
249     while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
250 	if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
251 	    if (kvno == tmp.vno) {
252 		krb5_kt_copy_entry_contents (context, &tmp, entry);
253 		krb5_kt_free_entry (context, &tmp);
254 		krb5_kt_end_seq_get(context, id, &cursor);
255 		return 0;
256 	    } else if (kvno == 0 && tmp.vno > entry->vno) {
257 		if (entry->vno)
258 		    krb5_kt_free_entry (context, entry);
259 		krb5_kt_copy_entry_contents (context, &tmp, entry);
260 	    }
261 	}
262 	krb5_kt_free_entry(context, &tmp);
263     }
264     krb5_kt_end_seq_get (context, id, &cursor);
265     if (entry->vno)
266 	return 0;
267     else
268 	return KRB5_KT_NOTFOUND;
269 }
270 
271 /*
272  * Copy the contents of `in' into `out'.
273  * Return 0 or an error.
274  */
275 
276 krb5_error_code
277 krb5_kt_copy_entry_contents(krb5_context context,
278 			    const krb5_keytab_entry *in,
279 			    krb5_keytab_entry *out)
280 {
281     krb5_error_code ret;
282 
283     memset(out, 0, sizeof(*out));
284     out->vno = in->vno;
285 
286     ret = krb5_copy_principal (context, in->principal, &out->principal);
287     if (ret)
288 	goto fail;
289     ret = krb5_copy_keyblock_contents (context,
290 				       &in->keyblock,
291 				       &out->keyblock);
292     if (ret)
293 	goto fail;
294     out->timestamp = in->timestamp;
295     return 0;
296 fail:
297     krb5_kt_free_entry (context, out);
298     return ret;
299 }
300 
301 /*
302  * Free the contents of `entry'.
303  */
304 
305 krb5_error_code
306 krb5_kt_free_entry(krb5_context context,
307 		   krb5_keytab_entry *entry)
308 {
309   krb5_free_principal (context, entry->principal);
310   krb5_free_keyblock_contents (context, &entry->keyblock);
311   return 0;
312 }
313 
314 #if 0
315 static int
316 xxxlock(int fd, int write)
317 {
318     if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) {
319 	sleep(1);
320 	if(flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0)
321 	    return -1;
322     }
323     return 0;
324 }
325 
326 static void
327 xxxunlock(int fd)
328 {
329     flock(fd, LOCK_UN);
330 }
331 #endif
332 
333 /*
334  * Set `cursor' to point at the beginning of `id'.
335  * Return 0 or an error.
336  */
337 
338 krb5_error_code
339 krb5_kt_start_seq_get(krb5_context context,
340 		      krb5_keytab id,
341 		      krb5_kt_cursor *cursor)
342 {
343     if(id->start_seq_get == NULL)
344 	return HEIM_ERR_OPNOTSUPP;
345     return (*id->start_seq_get)(context, id, cursor);
346 }
347 
348 /*
349  * Get the next entry from `id' pointed to by `cursor' and advance the
350  * `cursor'.
351  * Return 0 or an error.
352  */
353 
354 krb5_error_code
355 krb5_kt_next_entry(krb5_context context,
356 		   krb5_keytab id,
357 		   krb5_keytab_entry *entry,
358 		   krb5_kt_cursor *cursor)
359 {
360     if(id->next_entry == NULL)
361 	return HEIM_ERR_OPNOTSUPP;
362     return (*id->next_entry)(context, id, entry, cursor);
363 }
364 
365 /*
366  * Release all resources associated with `cursor'.
367  */
368 
369 krb5_error_code
370 krb5_kt_end_seq_get(krb5_context context,
371 		    krb5_keytab id,
372 		    krb5_kt_cursor *cursor)
373 {
374     if(id->end_seq_get == NULL)
375 	return HEIM_ERR_OPNOTSUPP;
376     return (*id->end_seq_get)(context, id, cursor);
377 }
378 
379 /*
380  * Add the entry in `entry' to the keytab `id'.
381  * Return 0 or an error.
382  */
383 
384 krb5_error_code
385 krb5_kt_add_entry(krb5_context context,
386 		  krb5_keytab id,
387 		  krb5_keytab_entry *entry)
388 {
389     if(id->add == NULL)
390 	return KRB5_KT_NOWRITE;
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