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