|
1 # |
|
2 # This patch provides support in kerberos for root acquiring a default cred via |
|
3 # either a root, host service principal or sam account name keys in the keytab |
|
4 # if root doesn't have a cred already. Note that if root has a client keytab |
|
5 # provisioned then that will be used instead. |
|
6 # |
|
7 # This is Solaris specific behavior that MIT will not take upstream. |
|
8 # Patch source: in-house |
|
9 # |
|
10 |
|
11 --- a/src/lib/gssapi/krb5/acquire_cred.c |
|
12 +++ b/src/lib/gssapi/krb5/acquire_cred.c |
|
13 @@ -77,6 +77,7 @@ |
|
14 #else |
|
15 #include <strings.h> |
|
16 #endif |
|
17 +#include <ctype.h> |
|
18 |
|
19 #ifdef USE_LEASH |
|
20 #ifdef _WIN64 |
|
21 @@ -88,6 +89,9 @@ |
|
22 static HANDLE hLeashDLL = INVALID_HANDLE_VALUE; |
|
23 #endif |
|
24 |
|
25 +/* for solaris root fallback check */ |
|
26 +static char defktname[BUFSIZ]; |
|
27 + |
|
28 #ifndef LEAN_CLIENT |
|
29 k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER; |
|
30 static char *krb5_gss_keytab = NULL; |
|
31 @@ -590,6 +594,151 @@ |
|
32 set_refresh_time(context, cred->ccache, refresh); |
|
33 } |
|
34 |
|
35 +#define SAM_ACCOUNT_LEN 17 /* 15:hostname + 1:$ + 1:\0 */ |
|
36 +krb5_error_code |
|
37 +get_sam_account_name(char **name) |
|
38 +{ |
|
39 + char *p, localname[SAM_ACCOUNT_LEN]; |
|
40 + |
|
41 + if (name == NULL) |
|
42 + return (EINVAL); |
|
43 + |
|
44 + if (gethostname(localname, SAM_ACCOUNT_LEN) != 0) |
|
45 + return (errno); |
|
46 + |
|
47 + localname[SAM_ACCOUNT_LEN - 2] = '\0'; |
|
48 + |
|
49 + if ((p = strchr(localname, '.')) != NULL) |
|
50 + *p = '\0'; |
|
51 + |
|
52 + for (p = localname; *p; p++) |
|
53 + *p = toupper(*p); |
|
54 + |
|
55 + (void) strlcat(localname, "$", SAM_ACCOUNT_LEN); |
|
56 + |
|
57 + *name = strdup(localname); |
|
58 + if (*name == NULL) |
|
59 + return (ENOMEM); |
|
60 + |
|
61 + return (0); |
|
62 +} |
|
63 + |
|
64 +krb5_error_code krb5_kt_find_realm(krb5_context, krb5_keytab, krb5_principal, |
|
65 + krb5_data *); |
|
66 + |
|
67 +static krb5_error_code |
|
68 +get_root_initcred_keytab(krb5_context context, |
|
69 + krb5_creds *kcreds, |
|
70 + krb5_gss_cred_id_rec *gsscred, |
|
71 + const char *name, |
|
72 + krb5_int32 type, |
|
73 + krb5_get_init_creds_opt *opt) |
|
74 +{ |
|
75 + krb5_principal client_princ; |
|
76 + krb5_error_code code; |
|
77 + |
|
78 + if (type == KRB5_NT_SRV_HST) { |
|
79 + code = krb5_sname_to_principal(context, NULL, name, type, |
|
80 + &client_princ); |
|
81 + } else { |
|
82 + /* Assuming KRB5_NT_PRINCIPAL */ |
|
83 + code = krb5_parse_name(context, name, &client_princ); |
|
84 + } |
|
85 + if (code) |
|
86 + return code; |
|
87 + |
|
88 + if (krb5_is_referral_realm(&client_princ->realm)) { |
|
89 + krb5_data realm; |
|
90 + |
|
91 + code = krb5_kt_find_realm(context, gsscred->client_keytab, |
|
92 + client_princ, &realm); |
|
93 + if (code == 0) { |
|
94 + krb5_free_data_contents(context, &client_princ->realm); |
|
95 + client_princ->realm.length = realm.length; |
|
96 + client_princ->realm.data = realm.data; |
|
97 + } else { |
|
98 + /* Try to set a useful error message */ |
|
99 + char *princ_name = NULL; |
|
100 + char kt_name[BUFSIZ]; |
|
101 + |
|
102 + (void) krb5_unparse_name(context, client_princ, &princ_name); |
|
103 + (void) krb5_kt_get_name(context, gsscred->client_keytab, kt_name, |
|
104 + BUFSIZ); |
|
105 + krb5_set_error_message(context, code, |
|
106 + _("Failed to find realm for %s in " |
|
107 + "keytab %s"), |
|
108 + princ_name != NULL ? princ_name : "unknown", |
|
109 + kt_name); |
|
110 + krb5_free_unparsed_name(context, princ_name); |
|
111 + } |
|
112 + } |
|
113 + if (code) |
|
114 + goto cleanup; |
|
115 + |
|
116 + code = krb5_get_init_creds_keytab(context, kcreds, client_princ, |
|
117 + gsscred->client_keytab, 0, NULL, opt); |
|
118 + if (code == 0) { |
|
119 + /* set the gsscred name to that of the princ for which an init cred was |
|
120 + * acquired. */ |
|
121 + if (gsscred->name != NULL) |
|
122 + (void) kg_release_name(context, &gsscred->name); |
|
123 + |
|
124 + code = kg_init_name(context, client_princ, NULL, NULL, NULL, |
|
125 + KG_INIT_NAME_NO_COPY, &gsscred->name); |
|
126 + /* Since KG_INIT_NAME_NO_COPY is set do not free client_princ if |
|
127 + * kg_init_name succeeds. */ |
|
128 + if (code == 0) |
|
129 + return 0; |
|
130 + else |
|
131 + krb5_free_cred_contents(context, kcreds); |
|
132 + } |
|
133 + |
|
134 +cleanup: |
|
135 + krb5_free_principal(context, client_princ); |
|
136 + return code; |
|
137 +} |
|
138 + |
|
139 +/* |
|
140 + * This implements long time Solaris behavior where processes running as root |
|
141 + * will try to acquire an init cred via the default/system keytab. The root, |
|
142 + * host and SAM princs are tried in that order until one succeeds or they all |
|
143 + * fail. |
|
144 + */ |
|
145 +static krb5_error_code |
|
146 +root_init_cred_kt_fallback(krb5_context context, |
|
147 + krb5_creds *kcreds, |
|
148 + krb5_gss_cred_id_rec *gsscred, |
|
149 + krb5_get_init_creds_opt *opt) |
|
150 +{ |
|
151 + char *sam_name = NULL; |
|
152 + krb5_error_code code; |
|
153 + |
|
154 + /* Try the root/<FQDN> service princ in system keytab */ |
|
155 + code = get_root_initcred_keytab(context, kcreds, gsscred, "root", |
|
156 + KRB5_NT_SRV_HST, opt); |
|
157 + if (code == 0) |
|
158 + goto out; |
|
159 + |
|
160 + /* Try the host/<FQDN> service princ in system keytab if the root princ |
|
161 + * wasn't found */ |
|
162 + code = get_root_initcred_keytab(context, kcreds, gsscred, "host", |
|
163 + KRB5_NT_SRV_HST, opt); |
|
164 + if (code == 0) |
|
165 + goto out; |
|
166 + |
|
167 + /* Try the SAM account princ in system keytab if the host service princ |
|
168 + * wasn't found for MS interop sake */ |
|
169 + code = get_sam_account_name(&sam_name); |
|
170 + if (code) |
|
171 + goto out; |
|
172 + |
|
173 + code = get_root_initcred_keytab(context, kcreds, gsscred, sam_name, |
|
174 + KRB5_NT_PRINCIPAL, opt); |
|
175 + free(sam_name); |
|
176 +out: |
|
177 + return code; |
|
178 +} |
|
179 + |
|
180 /* Get initial credentials using the supplied password or client keytab. */ |
|
181 static krb5_error_code |
|
182 get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred) |
|
183 @@ -609,8 +758,41 @@ |
|
184 cred->password, NULL, NULL, 0, |
|
185 NULL, opt); |
|
186 } else if (cred->client_keytab != NULL) { |
|
187 - code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ, |
|
188 - cred->client_keytab, 0, NULL, opt); |
|
189 + if (krb5_getuid() == 0) { |
|
190 + char clientktname[BUFSIZ]; |
|
191 + |
|
192 + /* assuming we only need to get the default keytab name once */ |
|
193 + if (defktname[0] == '\0') { |
|
194 + code = krb5_kt_default_name(context, defktname, |
|
195 + sizeof(defktname)); |
|
196 + if (code) |
|
197 + goto cleanup; |
|
198 + } |
|
199 + |
|
200 + code = krb5_kt_get_name(context, cred->client_keytab, clientktname, |
|
201 + sizeof(clientktname)); |
|
202 + if (code) |
|
203 + goto cleanup; |
|
204 + |
|
205 + /* |
|
206 + * If the client keytab name is the same as the system default |
|
207 + * keytab and we are root then we need to use the Solaris root |
|
208 + * fallback behavior in root_init_cred_kt_fallback(). |
|
209 + */ |
|
210 + if (strcmp(defktname, clientktname) == 0) { |
|
211 + code = root_init_cred_kt_fallback(context, &creds, cred, opt); |
|
212 + } else { |
|
213 + code = krb5_get_init_creds_keytab(context, &creds, |
|
214 + cred->name->princ, |
|
215 + cred->client_keytab, 0, NULL, |
|
216 + opt); |
|
217 + } |
|
218 + } else { |
|
219 + code = krb5_get_init_creds_keytab(context, &creds, |
|
220 + cred->name->princ, |
|
221 + cred->client_keytab, 0, NULL, |
|
222 + opt); |
|
223 + } |
|
224 } else { |
|
225 code = KRB5_KT_NOTFOUND; |
|
226 } |
|
227 @@ -700,6 +882,23 @@ |
|
228 krb5_clear_error_message(context); |
|
229 code = 0; |
|
230 } |
|
231 + /* |
|
232 + * The logic below is involved in providing support for Solaris |
|
233 + * behavior where root processes will fall back to acquiring an initial |
|
234 + * cred via the system/default keytab. The idea is that if the |
|
235 + * client_keytab could not be resolved or it doesn't exist then set the |
|
236 + * client_keytab field to the system/default keytab. |
|
237 + */ |
|
238 + if (krb5_getuid() == 0) { |
|
239 + if (cred->client_keytab == NULL || |
|
240 + krb5_kt_have_content(context, cred->client_keytab) != 0) { |
|
241 + |
|
242 + if (cred->client_keytab != NULL) |
|
243 + krb5_kt_close(context, cred->client_keytab); |
|
244 + |
|
245 + code = krb5_kt_default(context, &cred->client_keytab); |
|
246 + } |
|
247 + } |
|
248 } |
|
249 if (code) |
|
250 goto error; |
|
251 --- a/src/lib/krb5/keytab/Makefile.in |
|
252 +++ b/src/lib/krb5/keytab/Makefile.in |
|
253 @@ -13,6 +13,7 @@ |
|
254 ktremove.o \ |
|
255 ktfns.o \ |
|
256 kt_file.o \ |
|
257 + kt_findrealm.o \ |
|
258 kt_memory.o \ |
|
259 kt_srvtab.o \ |
|
260 read_servi.o \ |
|
261 @@ -26,6 +27,7 @@ |
|
262 $(OUTPRE)ktremove.$(OBJEXT) \ |
|
263 $(OUTPRE)ktfns.$(OBJEXT) \ |
|
264 $(OUTPRE)kt_file.$(OBJEXT) \ |
|
265 + $(OUTPRE)kt_findrealm.$(OBJEXT) \ |
|
266 $(OUTPRE)kt_memory.$(OBJEXT) \ |
|
267 $(OUTPRE)kt_srvtab.$(OBJEXT) \ |
|
268 $(OUTPRE)read_servi.$(OBJEXT) \ |
|
269 @@ -39,6 +41,7 @@ |
|
270 $(srcdir)/ktremove.c \ |
|
271 $(srcdir)/ktfns.c \ |
|
272 $(srcdir)/kt_file.c \ |
|
273 + $(srcdir)/kt_findrealm.c \ |
|
274 $(srcdir)/kt_memory.c \ |
|
275 $(srcdir)/kt_srvtab.c \ |
|
276 $(srcdir)/read_servi.c \ |