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