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