1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 
3 /*
4  * lib/krb5/keytab/srvtab/kts_resolv.c
5  *
6  * Copyright 1990,1991,2002 by the Massachusetts Institute of Technology.
7  * All Rights Reserved.
8  *
9  * Export of this software from the United States of America may
10  *   require a specific license from the United States Government.
11  *   It is the responsibility of any person or organization contemplating
12  *   export to obtain such a license before exporting.
13  *
14  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15  * distribute this software and its documentation for any purpose and
16  * without fee is hereby granted, provided that the above copyright
17  * notice appear in all copies and that both that copyright notice and
18  * this permission notice appear in supporting documentation, and that
19  * the name of M.I.T. not be used in advertising or publicity pertaining
20  * to distribution of the software without specific, written prior
21  * permission.  Furthermore if you modify this software you must label
22  * your software as modified software and not distribute it in such a
23  * fashion that it might be confused with the original M.I.T. software.
24  * M.I.T. makes no representations about the suitability of
25  * this software for any purpose.  It is provided "as is" without express
26  * or implied warranty.
27  */
28 
29 #define NEED_SOCKETS
30 #include "k5-int.h"
31 #include <stdio.h>
32 
33 /*
34  * Constants
35  */
36 #define IGNORE_VNO 0
37 #define IGNORE_ENCTYPE 0
38 
39 #define KRB5_KT_VNO_1	0x0501	/* krb v5, keytab version 1 (DCE compat) */
40 #define KRB5_KT_VNO	0x0502	/* krb v5, keytab version 2 (standard)  */
41 
42 #define KRB5_KT_DEFAULT_VNO KRB5_KT_VNO
43 
44 /*
45  * Types
46  */
47 typedef struct _krb5_ktsrvtab_data {
48     char *name;			/* Name of the file */
49     FILE *openf;		/* open file, if any. */
50 } krb5_ktsrvtab_data;
51 
52 /*
53  * Macros
54  */
55 #define KTPRIVATE(id) ((krb5_ktsrvtab_data *)(id)->data)
56 #define KTFILENAME(id) (((krb5_ktsrvtab_data *)(id)->data)->name)
57 #define KTFILEP(id) (((krb5_ktsrvtab_data *)(id)->data)->openf)
58 
59 extern const struct _krb5_kt_ops krb5_kts_ops;
60 
61 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_resolve
62 	(krb5_context,
63 		   const char *,
64 		   krb5_keytab *);
65 
66 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_get_name
67 	(krb5_context,
68 		   krb5_keytab,
69 		   char *,
70 		   unsigned int);
71 
72 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_close
73 	(krb5_context,
74 		   krb5_keytab);
75 
76 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_get_entry
77 	(krb5_context,
78 		   krb5_keytab,
79 		   krb5_const_principal,
80 		   krb5_kvno,
81 		   krb5_enctype,
82 		   krb5_keytab_entry *);
83 
84 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_start_seq_get
85 	(krb5_context,
86 		   krb5_keytab,
87 		   krb5_kt_cursor *);
88 
89 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_get_next
90 	(krb5_context,
91 		   krb5_keytab,
92 		   krb5_keytab_entry *,
93 		   krb5_kt_cursor *);
94 
95 static krb5_error_code KRB5_CALLCONV krb5_ktsrvtab_end_get
96 	(krb5_context,
97 		   krb5_keytab,
98 		   krb5_kt_cursor *);
99 
100 static krb5_error_code krb5_ktsrvint_open
101 	(krb5_context,
102 		   krb5_keytab);
103 
104 static krb5_error_code krb5_ktsrvint_close
105 	(krb5_context,
106 		   krb5_keytab);
107 
108 static krb5_error_code krb5_ktsrvint_read_entry
109 	(krb5_context,
110 		   krb5_keytab,
111 		   krb5_keytab_entry *);
112 
113 /*
114  * This is an implementation specific resolver.  It returns a keytab id
115  * initialized with srvtab keytab routines.
116  */
117 
118 static krb5_error_code KRB5_CALLCONV
119 krb5_ktsrvtab_resolve(krb5_context context, const char *name, krb5_keytab *id)
120 {
121     krb5_ktsrvtab_data *data;
122     FILE *fp;
123 
124     /* Make sure we can open the srvtab file for reading. */
125     fp = fopen(name, "r");
126     if (!fp)
127 	return(errno);
128     fclose(fp);
129 
130     if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
131 	return(ENOMEM);
132 
133     (*id)->ops = &krb5_kts_ops;
134     data = (krb5_ktsrvtab_data *)malloc(sizeof(krb5_ktsrvtab_data));
135     if (data == NULL) {
136 	krb5_xfree(*id);
137 	return(ENOMEM);
138     }
139 
140     data->name = (char *)malloc(strlen(name) + 1);
141     if (data->name == NULL) {
142 	krb5_xfree(data);
143 	krb5_xfree(*id);
144 	return(ENOMEM);
145     }
146 
147     (void) strcpy(data->name, name);
148     data->openf = 0;
149 
150     (*id)->data = (krb5_pointer)data;
151     (*id)->magic = KV5M_KEYTAB;
152     return(0);
153 }
154 
155 /*
156  * "Close" a file-based keytab and invalidate the id.  This means
157  * free memory hidden in the structures.
158  */
159 
160 krb5_error_code KRB5_CALLCONV
161 krb5_ktsrvtab_close(krb5_context context, krb5_keytab id)
162   /*
163    * This routine is responsible for freeing all memory allocated
164    * for this keytab.  There are no system resources that need
165    * to be freed nor are there any open files.
166    *
167    * This routine should undo anything done by krb5_ktsrvtab_resolve().
168    */
169 {
170     krb5_xfree(KTFILENAME(id));
171     krb5_xfree(id->data);
172     id->ops = 0;
173     krb5_xfree(id);
174     return (0);
175 }
176 
177 /*
178  * This is the get_entry routine for the file based keytab implementation.
179  * It opens the keytab file, and either retrieves the entry or returns
180  * an error.
181  */
182 
183 krb5_error_code KRB5_CALLCONV
184 krb5_ktsrvtab_get_entry(krb5_context context, krb5_keytab id, krb5_const_principal principal, krb5_kvno kvno, krb5_enctype enctype, krb5_keytab_entry *entry)
185 {
186     krb5_keytab_entry best_entry, ent;
187     krb5_error_code kerror = 0;
188     int found_wrong_kvno = 0;
189 
190     /* Open the srvtab. */
191     if ((kerror = krb5_ktsrvint_open(context, id)))
192 	return(kerror);
193 
194     /* srvtab files only have DES_CBC_CRC keys. */
195     switch (enctype) {
196     case ENCTYPE_DES_CBC_CRC:
197     case ENCTYPE_DES_CBC_MD5:
198     case ENCTYPE_DES_CBC_MD4:
199     case ENCTYPE_DES_CBC_RAW:
200     case IGNORE_ENCTYPE:
201 	break;
202     default:
203 	return KRB5_KT_NOTFOUND;
204     }
205 
206     best_entry.principal = 0;
207     best_entry.vno = 0;
208     best_entry.key.contents = 0;
209     while ((kerror = krb5_ktsrvint_read_entry(context, id, &ent)) == 0) {
210 	ent.key.enctype = enctype;
211 	if (krb5_principal_compare(context, principal, ent.principal)) {
212 	    if (kvno == IGNORE_VNO) {
213 		if (!best_entry.principal || (best_entry.vno < ent.vno)) {
214 		    krb5_kt_free_entry(context, &best_entry);
215 		    best_entry = ent;
216 		}
217 	    } else {
218 		if (ent.vno == kvno) {
219 		    best_entry = ent;
220 		    break;
221 		} else {
222 		    found_wrong_kvno = 1;
223 		}
224 	    }
225 	} else {
226 	    krb5_kt_free_entry(context, &ent);
227 	}
228     }
229     if (kerror == KRB5_KT_END) {
230 	 if (best_entry.principal)
231 	      kerror = 0;
232 	 else if (found_wrong_kvno)
233 	      kerror = KRB5_KT_KVNONOTFOUND;
234 	 else
235 	      kerror = KRB5_KT_NOTFOUND;
236     }
237     if (kerror) {
238 	(void) krb5_ktsrvint_close(context, id);
239 	krb5_kt_free_entry(context, &best_entry);
240 	return kerror;
241     }
242     if ((kerror = krb5_ktsrvint_close(context, id)) != 0) {
243 	krb5_kt_free_entry(context, &best_entry);
244 	return kerror;
245     }
246     *entry = best_entry;
247     return 0;
248 }
249 
250 /*
251  * Get the name of the file containing a srvtab-based keytab.
252  */
253 
254 krb5_error_code KRB5_CALLCONV
255 krb5_ktsrvtab_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
256   /*
257    * This routine returns the name of the name of the file associated with
258    * this srvtab-based keytab.  The name is prefixed with PREFIX:, so that
259    * trt will happen if the name is passed back to resolve.
260    */
261 {
262     memset(name, 0, len);
263 
264     if (len < strlen(id->ops->prefix)+2)
265 	return(KRB5_KT_NAME_TOOLONG);
266     strcpy(name, id->ops->prefix);
267     name += strlen(id->ops->prefix);
268     name[0] = ':';
269     name++;
270     len -= strlen(id->ops->prefix)+1;
271 
272     if (len < strlen(KTFILENAME(id)+1))
273 	return(KRB5_KT_NAME_TOOLONG);
274     strcpy(name, KTFILENAME(id));
275     /* strcpy will NUL-terminate the destination */
276 
277     return(0);
278 }
279 
280 /*
281  * krb5_ktsrvtab_start_seq_get()
282  */
283 
284 krb5_error_code KRB5_CALLCONV
285 krb5_ktsrvtab_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
286 {
287     krb5_error_code retval;
288     long *fileoff;
289 
290     if ((retval = krb5_ktsrvint_open(context, id)))
291 	return retval;
292 
293     if (!(fileoff = (long *)malloc(sizeof(*fileoff)))) {
294 	krb5_ktsrvint_close(context, id);
295 	return ENOMEM;
296     }
297     *fileoff = ftell(KTFILEP(id));
298     *cursorp = (krb5_kt_cursor)fileoff;
299 
300     return 0;
301 }
302 
303 /*
304  * krb5_ktsrvtab_get_next()
305  */
306 
307 krb5_error_code KRB5_CALLCONV
308 krb5_ktsrvtab_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
309 {
310     long *fileoff = (long *)*cursor;
311     krb5_keytab_entry cur_entry;
312     krb5_error_code kerror;
313 
314     if (fseek(KTFILEP(id), *fileoff, 0) == -1)
315 	return KRB5_KT_END;
316     if ((kerror = krb5_ktsrvint_read_entry(context, id, &cur_entry)))
317 	return kerror;
318     *fileoff = ftell(KTFILEP(id));
319     *entry = cur_entry;
320     return 0;
321 }
322 
323 /*
324  * krb5_ktsrvtab_end_get()
325  */
326 
327 krb5_error_code KRB5_CALLCONV
328 krb5_ktsrvtab_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
329 {
330     krb5_xfree(*cursor);
331     return krb5_ktsrvint_close(context, id);
332 }
333 
334 /*
335  * krb5_kts_ops
336  */
337 
338 const struct _krb5_kt_ops krb5_kts_ops = {
339     0,
340     "SRVTAB", 	/* Prefix -- this string should not appear anywhere else! */
341     krb5_ktsrvtab_resolve,
342     krb5_ktsrvtab_get_name,
343     krb5_ktsrvtab_close,
344     krb5_ktsrvtab_get_entry,
345     krb5_ktsrvtab_start_seq_get,
346     krb5_ktsrvtab_get_next,
347     krb5_ktsrvtab_end_get,
348     0,
349     0,
350     0
351 };
352 
353 /*
354  * formerly: lib/krb5/keytab/srvtab/kts_util.c
355  *
356  * Copyright (c) Hewlett-Packard Company 1991
357  * Released to the Massachusetts Institute of Technology for inclusion
358  * in the Kerberos source code distribution.
359  *
360  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
361  * All Rights Reserved.
362  *
363  * Export of this software from the United States of America may
364  *   require a specific license from the United States Government.
365  *   It is the responsibility of any person or organization contemplating
366  *   export to obtain such a license before exporting.
367  *
368  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
369  * distribute this software and its documentation for any purpose and
370  * without fee is hereby granted, provided that the above copyright
371  * notice appear in all copies and that both that copyright notice and
372  * this permission notice appear in supporting documentation, and that
373  * the name of M.I.T. not be used in advertising or publicity pertaining
374  * to distribution of the software without specific, written prior
375  * permission.  Furthermore if you modify this software you must label
376  * your software as modified software and not distribute it in such a
377  * fashion that it might be confused with the original M.I.T. software.
378  * M.I.T. makes no representations about the suitability of
379  * this software for any purpose.  It is provided "as is" without express
380  * or implied warranty.
381  *
382  *
383  * This function contains utilities for the srvtab based implementation
384  * of the keytab.  There are no public functions in this file.
385  */
386 
387 #include <stdio.h>
388 
389 #ifdef ANSI_STDIO
390 #define		READ_MODE	"rb"
391 #else
392 #define		READ_MODE	"r"
393 #endif
394 
395 /* The maximum sizes for V4 aname, realm, sname, and instance +1 */
396 /* Taken from krb.h */
397 #define 	ANAME_SZ	40
398 #define		REALM_SZ	40
399 #define		SNAME_SZ	40
400 #define		INST_SZ		40
401 
402 static krb5_error_code
403 read_field(FILE *fp, char *s, int len)
404 {
405     int c;
406 
407     while ((c = getc(fp)) != 0) {
408 	if (c == EOF || len <= 1)
409 	    return KRB5_KT_END;
410 	*s = c;
411 	s++;
412 	len--;
413     }
414     *s = 0;
415     return 0;
416 }
417 
418 krb5_error_code
419 krb5_ktsrvint_open(krb5_context context, krb5_keytab id)
420 {
421     KTFILEP(id) = fopen(KTFILENAME(id), READ_MODE);
422     if (!KTFILEP(id))
423 	return errno;
424     return 0;
425 }
426 
427 krb5_error_code
428 krb5_ktsrvint_close(krb5_context context, krb5_keytab id)
429 {
430     if (!KTFILEP(id))
431 	return 0;
432     (void) fclose(KTFILEP(id));
433     KTFILEP(id) = 0;
434     return 0;
435 }
436 
437 krb5_error_code
438 krb5_ktsrvint_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *ret_entry)
439 {
440     FILE *fp;
441     char name[SNAME_SZ], instance[INST_SZ], realm[REALM_SZ];
442     unsigned char key[8];
443     int vno;
444     krb5_error_code kerror;
445 
446     /* Read in an entry from the srvtab file. */
447     fp = KTFILEP(id);
448     kerror = read_field(fp, name, sizeof(name));
449     if (kerror != 0)
450 	return kerror;
451     kerror = read_field(fp, instance, sizeof(instance));
452     if (kerror != 0)
453 	return kerror;
454     kerror = read_field(fp, realm, sizeof(realm));
455     if (kerror != 0)
456 	return kerror;
457     vno = getc(fp);
458     if (vno == EOF)
459 	return KRB5_KT_END;
460     if (fread(key, 1, sizeof(key), fp) != sizeof(key))
461 	return KRB5_KT_END;
462 
463     /* Fill in ret_entry with the data we read.  Everything maps well
464      * except for the timestamp, which we don't have a value for.  For
465      * now we just set it to 0. */
466     memset(ret_entry, 0, sizeof(*ret_entry));
467     ret_entry->magic = KV5M_KEYTAB_ENTRY;
468     kerror = krb5_425_conv_principal(context, name, instance, realm,
469 				     &ret_entry->principal);
470     if (kerror != 0)
471 	return kerror;
472     ret_entry->vno = vno;
473     ret_entry->timestamp = 0;
474     ret_entry->key.enctype = ENCTYPE_DES_CBC_CRC;
475     ret_entry->key.magic = KV5M_KEYBLOCK;
476     ret_entry->key.length = sizeof(key);
477     ret_entry->key.contents = malloc(sizeof(key));
478     if (!ret_entry->key.contents) {
479 	krb5_free_principal(context, ret_entry->principal);
480 	return ENOMEM;
481     }
482     memcpy(ret_entry->key.contents, key, sizeof(key));
483 
484     return 0;
485 }
486