1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/keytab/ktfns.c */
3 /*
4  * Copyright 2001,2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 /*
28  * Dispatch methods for keytab code.
29  */
30 
31 #ifndef LEAN_CLIENT
32 
33 #include "k5-int.h"
34 #include "../krb/int-proto.h"
35 #include "../os/os-proto.h"
36 
37 const char * KRB5_CALLCONV
krb5_kt_get_type(krb5_context context,krb5_keytab keytab)38 krb5_kt_get_type (krb5_context context, krb5_keytab keytab)
39 {
40     return keytab->ops->prefix;
41 }
42 
43 krb5_error_code KRB5_CALLCONV
krb5_kt_get_name(krb5_context context,krb5_keytab keytab,char * name,unsigned int namelen)44 krb5_kt_get_name(krb5_context context, krb5_keytab keytab, char *name,
45                  unsigned int namelen)
46 {
47     return krb5_x((keytab)->ops->get_name,(context, keytab,name,namelen));
48 }
49 
50 krb5_error_code KRB5_CALLCONV
krb5_kt_close(krb5_context context,krb5_keytab keytab)51 krb5_kt_close(krb5_context context, krb5_keytab keytab)
52 {
53     return krb5_x((keytab)->ops->close,(context, keytab));
54 }
55 
56 krb5_error_code KRB5_CALLCONV
krb5_kt_get_entry(krb5_context context,krb5_keytab keytab,krb5_const_principal principal,krb5_kvno vno,krb5_enctype enctype,krb5_keytab_entry * entry)57 krb5_kt_get_entry(krb5_context context, krb5_keytab keytab,
58                   krb5_const_principal principal, krb5_kvno vno,
59                   krb5_enctype enctype, krb5_keytab_entry *entry)
60 {
61     krb5_error_code err;
62     krb5_principal_data princ_data;
63 
64     if (krb5_is_referral_realm(&principal->realm)) {
65         char *realm;
66         princ_data = *principal;
67         principal = &princ_data;
68         err = krb5_get_default_realm(context, &realm);
69         if (err)
70             return err;
71         princ_data.realm.data = realm;
72         princ_data.realm.length = strlen(realm);
73     }
74     err = krb5_x((keytab)->ops->get,(context, keytab, principal, vno, enctype,
75                                      entry));
76     TRACE_KT_GET_ENTRY(context, keytab, principal, vno, enctype, err);
77     if (principal == &princ_data)
78         krb5_free_default_realm(context, princ_data.realm.data);
79     return err;
80 }
81 
82 krb5_error_code KRB5_CALLCONV
krb5_kt_start_seq_get(krb5_context context,krb5_keytab keytab,krb5_kt_cursor * cursor)83 krb5_kt_start_seq_get(krb5_context context, krb5_keytab keytab,
84                       krb5_kt_cursor *cursor)
85 {
86     return krb5_x((keytab)->ops->start_seq_get,(context, keytab, cursor));
87 }
88 
89 krb5_error_code KRB5_CALLCONV
krb5_kt_next_entry(krb5_context context,krb5_keytab keytab,krb5_keytab_entry * entry,krb5_kt_cursor * cursor)90 krb5_kt_next_entry(krb5_context context, krb5_keytab keytab,
91                    krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
92 {
93     return krb5_x((keytab)->ops->get_next,(context, keytab, entry, cursor));
94 }
95 
96 krb5_error_code KRB5_CALLCONV
krb5_kt_end_seq_get(krb5_context context,krb5_keytab keytab,krb5_kt_cursor * cursor)97 krb5_kt_end_seq_get(krb5_context context, krb5_keytab keytab,
98                     krb5_kt_cursor *cursor)
99 {
100     return krb5_x((keytab)->ops->end_get,(context, keytab, cursor));
101 }
102 
103 krb5_error_code KRB5_CALLCONV
krb5_kt_have_content(krb5_context context,krb5_keytab keytab)104 krb5_kt_have_content(krb5_context context, krb5_keytab keytab)
105 {
106     krb5_keytab_entry entry;
107     krb5_kt_cursor cursor;
108     krb5_error_code ret;
109     char name[1024];
110 
111     /* If the keytab is not iterable, assume that it has content. */
112     if (keytab->ops->start_seq_get == NULL)
113         return 0;
114 
115     /* See if we can get at least one entry via iteration. */
116     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
117     if (ret)
118 	goto no_entries;
119     ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
120     krb5_kt_end_seq_get(context, keytab, &cursor);
121     if (ret)
122 	goto no_entries;
123     krb5_kt_free_entry(context, &entry);
124     return 0;
125 
126 no_entries:
127     if (krb5_kt_get_name(context, keytab, name, sizeof(name)) == 0) {
128         k5_setmsg(context, KRB5_KT_NOTFOUND,
129                   _("Keytab %s is nonexistent or empty"), name);
130     }
131     return KRB5_KT_NOTFOUND;
132 }
133 
134 static krb5_error_code
match_entries(krb5_context context,krb5_keytab keytab,krb5_const_principal mprinc)135 match_entries(krb5_context context, krb5_keytab keytab,
136               krb5_const_principal mprinc)
137 {
138     krb5_error_code ret;
139     krb5_keytab_entry ent;
140     krb5_kt_cursor cursor;
141     krb5_boolean match;
142 
143     /* Scan the keytab for host-based entries matching accprinc. */
144     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
145     if (ret)
146         return ret;
147     while ((ret = krb5_kt_next_entry(context, keytab, &ent, &cursor)) == 0) {
148         match = krb5_sname_match(context, mprinc, ent.principal);
149         (void)krb5_free_keytab_entry_contents(context, &ent);
150         if (match)
151             break;
152     }
153     (void)krb5_kt_end_seq_get(context, keytab, &cursor);
154     if (ret && ret != KRB5_KT_END)
155         return ret;
156     return match ? 0 : KRB5_KT_NOTFOUND;
157 }
158 
159 krb5_error_code
k5_kt_have_match(krb5_context context,krb5_keytab keytab,krb5_principal mprinc)160 k5_kt_have_match(krb5_context context, krb5_keytab keytab,
161                  krb5_principal mprinc)
162 {
163     krb5_error_code ret;
164     struct canonprinc iter = { mprinc, .no_hostrealm = TRUE };
165     krb5_const_principal canonprinc = NULL;
166 
167     /* Don't try to canonicalize if we're going to ignore hostnames. */
168     if (k5_sname_wildcard_host(context, mprinc))
169         return match_entries(context, keytab, mprinc);
170 
171     while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
172            canonprinc != NULL) {
173         ret = match_entries(context, keytab, canonprinc);
174         if (ret != KRB5_KT_NOTFOUND)
175             break;
176     }
177     free_canonprinc(&iter);
178     return (ret == 0 && canonprinc == NULL) ? KRB5_KT_NOTFOUND : ret;
179 }
180 
181 /*
182  * In a couple of places we need to get a principal name from a keytab: when
183  * verifying credentials against a keytab, and when querying the name of a
184  * default GSS acceptor cred.  Keytabs do not have the concept of a default
185  * principal like ccaches do, so for now we just return the first principal
186  * listed in the keytab, or an error if it's not iterable.  In the future we
187  * could consider elevating this to a public API and giving keytab types an
188  * operation to return a default principal, and maybe extending the file format
189  * and tools to support it.  Returns KRB5_KT_NOTFOUND if the keytab is empty
190  * or non-iterable.
191  */
192 krb5_error_code
k5_kt_get_principal(krb5_context context,krb5_keytab keytab,krb5_principal * princ_out)193 k5_kt_get_principal(krb5_context context, krb5_keytab keytab,
194                     krb5_principal *princ_out)
195 {
196     krb5_error_code ret;
197     krb5_kt_cursor cursor;
198     krb5_keytab_entry kte;
199 
200     *princ_out = NULL;
201     if (keytab->ops->start_seq_get == NULL)
202         return KRB5_KT_NOTFOUND;
203     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
204     if (ret)
205         return ret;
206     ret = krb5_kt_next_entry(context, keytab, &kte, &cursor);
207     (void)krb5_kt_end_seq_get(context, keytab, &cursor);
208     if (ret)
209         return (ret == KRB5_KT_END) ? KRB5_KT_NOTFOUND : ret;
210     ret = krb5_copy_principal(context, kte.principal, princ_out);
211     krb5_kt_free_entry(context, &kte);
212     return ret;
213 }
214 #endif /* LEAN_CLIENT */
215