/src/samba/source4/auth/gensec/gensec_gssapi.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Kerberos backend for GENSEC |
5 | | |
6 | | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005 |
7 | | Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2005 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | #include "includes.h" |
25 | | #include <tevent.h> |
26 | | #include "lib/util/tevent_ntstatus.h" |
27 | | #include "lib/events/events.h" |
28 | | #include "system/kerberos.h" |
29 | | #include "system/gssapi.h" |
30 | | #include "auth/kerberos/kerberos.h" |
31 | | #include "librpc/gen_ndr/krb5pac.h" |
32 | | #include "auth/auth.h" |
33 | | #include <ldb.h> |
34 | | #include "auth/auth_sam.h" |
35 | | #include "librpc/gen_ndr/dcerpc.h" |
36 | | #include "auth/credentials/credentials.h" |
37 | | #include "auth/credentials/credentials_krb5.h" |
38 | | #include "auth/gensec/gensec.h" |
39 | | #include "auth/gensec/gensec_internal.h" |
40 | | #include "auth/gensec/gensec_proto.h" |
41 | | #include "auth/gensec/gensec_toplevel_proto.h" |
42 | | #include "param/param.h" |
43 | | #include "auth/session_proto.h" |
44 | | #include "gensec_gssapi.h" |
45 | | #include "lib/util/util_net.h" |
46 | | #include "auth/kerberos/pac_utils.h" |
47 | | #include "auth/kerberos/gssapi_helper.h" |
48 | | #include "lib/util/smb_strtox.h" |
49 | | |
50 | | #ifndef gss_mech_spnego |
51 | | gss_OID_desc spnego_mech_oid_desc = |
52 | | { 6, discard_const_p(void, "\x2b\x06\x01\x05\x05\x02") }; |
53 | | #define gss_mech_spnego (&spnego_mech_oid_desc) |
54 | | #endif |
55 | | |
56 | | _PUBLIC_ NTSTATUS gensec_gssapi_init(TALLOC_CTX *); |
57 | | |
58 | | static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security); |
59 | | static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security); |
60 | | static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size); |
61 | | |
62 | | static int gensec_gssapi_destructor(struct gensec_gssapi_state *gensec_gssapi_state) |
63 | 0 | { |
64 | 0 | OM_uint32 min_stat; |
65 | |
|
66 | 0 | if (gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { |
67 | 0 | gss_release_cred(&min_stat, |
68 | 0 | &gensec_gssapi_state->delegated_cred_handle); |
69 | 0 | } |
70 | |
|
71 | 0 | if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) { |
72 | 0 | gss_delete_sec_context(&min_stat, |
73 | 0 | &gensec_gssapi_state->gssapi_context, |
74 | 0 | GSS_C_NO_BUFFER); |
75 | 0 | } |
76 | |
|
77 | 0 | if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) { |
78 | 0 | gss_release_name(&min_stat, |
79 | 0 | &gensec_gssapi_state->server_name); |
80 | 0 | } |
81 | 0 | if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) { |
82 | 0 | gss_release_name(&min_stat, |
83 | 0 | &gensec_gssapi_state->client_name); |
84 | 0 | } |
85 | |
|
86 | 0 | return 0; |
87 | 0 | } |
88 | | |
89 | | static NTSTATUS gensec_gssapi_setup_server_principal(TALLOC_CTX *mem_ctx, |
90 | | const char *target_principal, |
91 | | const char *service, |
92 | | const char *hostname, |
93 | | const char *realm, |
94 | | const gss_OID mech, |
95 | | char **pserver_principal, |
96 | | gss_name_t *pserver_name) |
97 | 0 | { |
98 | 0 | char *server_principal = NULL; |
99 | 0 | gss_buffer_desc name_token; |
100 | 0 | gss_OID name_type; |
101 | 0 | OM_uint32 maj_stat, min_stat = 0; |
102 | |
|
103 | 0 | if (target_principal != NULL) { |
104 | 0 | server_principal = talloc_strdup(mem_ctx, target_principal); |
105 | 0 | name_type = GSS_C_NULL_OID; |
106 | 0 | } else { |
107 | 0 | server_principal = talloc_asprintf(mem_ctx, |
108 | 0 | "%s/%s@%s", |
109 | 0 | service, hostname, realm); |
110 | 0 | name_type = GSS_C_NT_USER_NAME; |
111 | 0 | } |
112 | 0 | if (server_principal == NULL) { |
113 | 0 | return NT_STATUS_NO_MEMORY; |
114 | 0 | } |
115 | | |
116 | 0 | name_token.value = (uint8_t *)server_principal; |
117 | 0 | name_token.length = strlen(server_principal); |
118 | |
|
119 | 0 | maj_stat = gss_import_name(&min_stat, |
120 | 0 | &name_token, |
121 | 0 | name_type, |
122 | 0 | pserver_name); |
123 | 0 | if (maj_stat) { |
124 | 0 | DBG_WARNING("GSS Import name of %s failed: %s\n", |
125 | 0 | server_principal, |
126 | 0 | gssapi_error_string(mem_ctx, |
127 | 0 | maj_stat, |
128 | 0 | min_stat, |
129 | 0 | mech)); |
130 | 0 | TALLOC_FREE(server_principal); |
131 | 0 | return NT_STATUS_INVALID_PARAMETER; |
132 | 0 | } |
133 | | |
134 | 0 | *pserver_principal = server_principal; |
135 | |
|
136 | 0 | return NT_STATUS_OK; |
137 | 0 | } |
138 | | |
139 | | static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security) |
140 | 0 | { |
141 | 0 | struct gensec_gssapi_state *gensec_gssapi_state; |
142 | 0 | krb5_error_code ret; |
143 | 0 | #ifdef SAMBA4_USES_HEIMDAL |
144 | 0 | const char *realm; |
145 | 0 | #endif |
146 | |
|
147 | 0 | gensec_gssapi_state = talloc_zero(gensec_security, struct gensec_gssapi_state); |
148 | 0 | if (!gensec_gssapi_state) { |
149 | 0 | return NT_STATUS_NO_MEMORY; |
150 | 0 | } |
151 | | |
152 | 0 | gensec_security->private_data = gensec_gssapi_state; |
153 | |
|
154 | 0 | gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT; |
155 | |
|
156 | 0 | if (gensec_security->channel_bindings != NULL) { |
157 | 0 | gensec_gssapi_state->_input_chan_bindings.initiator_addrtype = |
158 | 0 | gensec_security->channel_bindings->initiator_addrtype; |
159 | 0 | gensec_gssapi_state->_input_chan_bindings.initiator_address.value = |
160 | 0 | gensec_security->channel_bindings->initiator_address.data; |
161 | 0 | gensec_gssapi_state->_input_chan_bindings.initiator_address.length = |
162 | 0 | gensec_security->channel_bindings->initiator_address.length; |
163 | |
|
164 | 0 | gensec_gssapi_state->_input_chan_bindings.acceptor_addrtype = |
165 | 0 | gensec_security->channel_bindings->acceptor_addrtype; |
166 | 0 | gensec_gssapi_state->_input_chan_bindings.acceptor_address.value = |
167 | 0 | gensec_security->channel_bindings->acceptor_address.data; |
168 | 0 | gensec_gssapi_state->_input_chan_bindings.acceptor_address.length = |
169 | 0 | gensec_security->channel_bindings->acceptor_address.length; |
170 | |
|
171 | 0 | gensec_gssapi_state->_input_chan_bindings.application_data.value = |
172 | 0 | gensec_security->channel_bindings->application_data.data; |
173 | 0 | gensec_gssapi_state->_input_chan_bindings.application_data.length = |
174 | 0 | gensec_security->channel_bindings->application_data.length; |
175 | |
|
176 | 0 | gensec_gssapi_state->input_chan_bindings = |
177 | 0 | &gensec_gssapi_state->_input_chan_bindings; |
178 | 0 | } else { |
179 | 0 | gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; |
180 | 0 | } |
181 | |
|
182 | 0 | gensec_gssapi_state->server_name = GSS_C_NO_NAME; |
183 | 0 | gensec_gssapi_state->client_name = GSS_C_NO_NAME; |
184 | |
|
185 | 0 | gensec_gssapi_state->gss_want_flags = 0; |
186 | 0 | gensec_gssapi_state->expire_time = GENSEC_EXPIRE_TIME_INFINITY; |
187 | |
|
188 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "delegation_by_kdc_policy", true)) { |
189 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_DELEG_POLICY_FLAG; |
190 | 0 | } |
191 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "mutual", true)) { |
192 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_MUTUAL_FLAG; |
193 | 0 | } |
194 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "delegation", false)) { |
195 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_DELEG_FLAG; |
196 | 0 | } |
197 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "replay", true)) { |
198 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_REPLAY_FLAG; |
199 | 0 | } |
200 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "sequence", true)) { |
201 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_SEQUENCE_FLAG; |
202 | 0 | } |
203 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_NO_DELEGATION) { |
204 | 0 | gensec_gssapi_state->gss_want_flags &= ~GSS_C_DELEG_FLAG; |
205 | 0 | gensec_gssapi_state->gss_want_flags &= ~GSS_C_DELEG_POLICY_FLAG; |
206 | 0 | } |
207 | |
|
208 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { |
209 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_INTEG_FLAG; |
210 | 0 | } |
211 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { |
212 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_INTEG_FLAG; |
213 | 0 | } |
214 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { |
215 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_INTEG_FLAG; |
216 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_CONF_FLAG; |
217 | 0 | } |
218 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) { |
219 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_DCE_STYLE; |
220 | 0 | } |
221 | |
|
222 | 0 | gensec_gssapi_state->gss_got_flags = 0; |
223 | |
|
224 | 0 | switch (gensec_security->ops->auth_type) { |
225 | 0 | case DCERPC_AUTH_TYPE_SPNEGO: |
226 | 0 | gensec_gssapi_state->gss_oid = gss_mech_spnego; |
227 | 0 | break; |
228 | 0 | case DCERPC_AUTH_TYPE_KRB5: |
229 | 0 | default: |
230 | 0 | gensec_gssapi_state->gss_oid = |
231 | 0 | discard_const_p(void, gss_mech_krb5); |
232 | 0 | break; |
233 | 0 | } |
234 | | |
235 | 0 | ret = smb_krb5_init_context(gensec_gssapi_state, |
236 | 0 | gensec_security->settings->lp_ctx, |
237 | 0 | &gensec_gssapi_state->smb_krb5_context); |
238 | 0 | if (ret) { |
239 | 0 | DEBUG(1,("gensec_gssapi_start: smb_krb5_init_context failed (%s)\n", |
240 | 0 | error_message(ret))); |
241 | 0 | talloc_free(gensec_gssapi_state); |
242 | 0 | return NT_STATUS_INTERNAL_ERROR; |
243 | 0 | } |
244 | | |
245 | 0 | gensec_gssapi_state->client_cred = NULL; |
246 | 0 | gensec_gssapi_state->server_cred = NULL; |
247 | |
|
248 | 0 | gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL; |
249 | |
|
250 | 0 | gensec_gssapi_state->sasl = false; |
251 | 0 | gensec_gssapi_state->sasl_state = STAGE_GSS_NEG; |
252 | 0 | gensec_gssapi_state->sasl_protection = 0; |
253 | |
|
254 | 0 | gensec_gssapi_state->max_wrap_buf_size |
255 | 0 | = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "max wrap buf size", 65536); |
256 | 0 | gensec_gssapi_state->gss_exchange_count = 0; |
257 | 0 | gensec_gssapi_state->sig_size = 0; |
258 | |
|
259 | 0 | talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destructor); |
260 | |
|
261 | 0 | #ifdef SAMBA4_USES_HEIMDAL |
262 | 0 | realm = lpcfg_realm(gensec_security->settings->lp_ctx); |
263 | 0 | if (realm != NULL) { |
264 | 0 | ret = gsskrb5_set_default_realm(realm); |
265 | 0 | if (ret) { |
266 | 0 | DEBUG(1,("gensec_gssapi_start: gsskrb5_set_default_realm failed\n")); |
267 | 0 | talloc_free(gensec_gssapi_state); |
268 | 0 | return NT_STATUS_INTERNAL_ERROR; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | /* don't do DNS lookups of any kind, it might/will fail for a netbios name */ |
273 | 0 | ret = gsskrb5_set_dns_canonicalize(false); |
274 | 0 | if (ret) { |
275 | 0 | DEBUG(1,("gensec_gssapi_start: gsskrb5_set_dns_canonicalize failed\n")); |
276 | 0 | talloc_free(gensec_gssapi_state); |
277 | 0 | return NT_STATUS_INTERNAL_ERROR; |
278 | 0 | } |
279 | 0 | #endif |
280 | 0 | return NT_STATUS_OK; |
281 | 0 | } |
282 | | |
283 | | static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security) |
284 | 0 | { |
285 | 0 | NTSTATUS nt_status; |
286 | 0 | int ret; |
287 | 0 | struct gensec_gssapi_state *gensec_gssapi_state; |
288 | 0 | struct cli_credentials *machine_account; |
289 | 0 | struct gssapi_creds_container *gcc; |
290 | |
|
291 | 0 | nt_status = gensec_gssapi_start(gensec_security); |
292 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
293 | 0 | return nt_status; |
294 | 0 | } |
295 | | |
296 | 0 | gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
297 | |
|
298 | 0 | machine_account = gensec_get_credentials(gensec_security); |
299 | |
|
300 | 0 | if (!machine_account) { |
301 | 0 | DEBUG(3, ("No machine account credentials specified\n")); |
302 | 0 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; |
303 | 0 | } else { |
304 | 0 | ret = cli_credentials_get_server_gss_creds(machine_account, |
305 | 0 | gensec_security->settings->lp_ctx, &gcc); |
306 | 0 | if (ret) { |
307 | 0 | DEBUG(1, ("Acquiring acceptor credentials failed: %s\n", |
308 | 0 | error_message(ret))); |
309 | 0 | return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; |
310 | 0 | } |
311 | 0 | } |
312 | | |
313 | 0 | gensec_gssapi_state->server_cred = gcc; |
314 | 0 | return NT_STATUS_OK; |
315 | |
|
316 | 0 | } |
317 | | |
318 | | static NTSTATUS gensec_gssapi_sasl_server_start(struct gensec_security *gensec_security) |
319 | 0 | { |
320 | 0 | NTSTATUS nt_status; |
321 | 0 | struct gensec_gssapi_state *gensec_gssapi_state; |
322 | 0 | nt_status = gensec_gssapi_server_start(gensec_security); |
323 | |
|
324 | 0 | if (NT_STATUS_IS_OK(nt_status)) { |
325 | 0 | gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
326 | 0 | gensec_gssapi_state->sasl = true; |
327 | 0 | } |
328 | 0 | return nt_status; |
329 | 0 | } |
330 | | |
331 | | static NTSTATUS gensec_gssapi_client_creds(struct gensec_security *gensec_security, |
332 | | struct tevent_context *ev) |
333 | 0 | { |
334 | 0 | struct gensec_gssapi_state *gensec_gssapi_state; |
335 | 0 | struct gssapi_creds_container *gcc; |
336 | 0 | struct cli_credentials *creds = gensec_get_credentials(gensec_security); |
337 | 0 | const char *error_string; |
338 | 0 | int ret; |
339 | |
|
340 | 0 | gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
341 | | |
342 | | /* Only run this the first time the update() call is made */ |
343 | 0 | if (gensec_gssapi_state->client_cred) { |
344 | 0 | return NT_STATUS_OK; |
345 | 0 | } |
346 | | |
347 | 0 | ret = cli_credentials_get_client_gss_creds(creds, |
348 | 0 | ev, |
349 | 0 | gensec_security->settings->lp_ctx, &gcc, &error_string); |
350 | 0 | switch (ret) { |
351 | 0 | case 0: |
352 | 0 | break; |
353 | 0 | case EINVAL: |
354 | 0 | DEBUG(3, ("Cannot obtain client GSS credentials we need to contact %s : %s\n", gensec_gssapi_state->target_principal, error_string)); |
355 | 0 | return NT_STATUS_INVALID_PARAMETER; |
356 | 0 | case KRB5KDC_ERR_PREAUTH_FAILED: |
357 | 0 | case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: |
358 | 0 | case KRB5KRB_AP_ERR_BAD_INTEGRITY: |
359 | 0 | DEBUG(1, ("Wrong username or password: %s\n", error_string)); |
360 | 0 | return NT_STATUS_LOGON_FAILURE; |
361 | 0 | case KRB5KDC_ERR_CLIENT_REVOKED: |
362 | 0 | DEBUG(1, ("Account locked out: %s\n", error_string)); |
363 | 0 | return NT_STATUS_ACCOUNT_LOCKED_OUT; |
364 | 0 | case KRB5_REALM_UNKNOWN: |
365 | 0 | case KRB5_KDC_UNREACH: |
366 | 0 | DEBUG(3, ("Cannot reach a KDC we require to contact %s : %s\n", gensec_gssapi_state->target_principal, error_string)); |
367 | 0 | return NT_STATUS_NO_LOGON_SERVERS; |
368 | 0 | case KRB5_CC_NOTFOUND: |
369 | 0 | case KRB5_CC_END: |
370 | 0 | DEBUG(2, ("Error obtaining ticket we require to contact %s: (possibly due to clock skew between us and the KDC) %s\n", gensec_gssapi_state->target_principal, error_string)); |
371 | 0 | return NT_STATUS_TIME_DIFFERENCE_AT_DC; |
372 | 0 | default: |
373 | 0 | DEBUG(1, ("Acquiring initiator credentials failed: %s\n", error_string)); |
374 | 0 | return NT_STATUS_UNSUCCESSFUL; |
375 | 0 | } |
376 | | |
377 | 0 | gensec_gssapi_state->client_cred = gcc; |
378 | 0 | if (!talloc_reference(gensec_gssapi_state, gcc)) { |
379 | 0 | return NT_STATUS_NO_MEMORY; |
380 | 0 | } |
381 | | |
382 | 0 | return NT_STATUS_OK; |
383 | 0 | } |
384 | | |
385 | | static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security) |
386 | 0 | { |
387 | 0 | struct gensec_gssapi_state *gensec_gssapi_state; |
388 | 0 | struct cli_credentials *creds = gensec_get_credentials(gensec_security); |
389 | 0 | NTSTATUS nt_status; |
390 | |
|
391 | 0 | nt_status = gensec_kerberos_possible(gensec_security); |
392 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
393 | 0 | char *target_name = NULL; |
394 | 0 | char *cred_name = NULL; |
395 | |
|
396 | 0 | target_name = gensec_get_unparsed_target_principal(gensec_security, |
397 | 0 | gensec_security); |
398 | 0 | cred_name = cli_credentials_get_unparsed_name(creds, |
399 | 0 | gensec_security); |
400 | |
|
401 | 0 | DBG_NOTICE("Not using kerberos to %s as %s: %s\n", |
402 | 0 | target_name, cred_name, nt_errstr(nt_status)); |
403 | |
|
404 | 0 | TALLOC_FREE(target_name); |
405 | 0 | TALLOC_FREE(cred_name); |
406 | 0 | return nt_status; |
407 | 0 | } |
408 | | |
409 | 0 | nt_status = gensec_gssapi_start(gensec_security); |
410 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
411 | 0 | return nt_status; |
412 | 0 | } |
413 | | |
414 | 0 | gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
415 | |
|
416 | 0 | #ifdef HAVE_CLIENT_GSS_C_CHANNEL_BOUND_FLAG |
417 | | /* |
418 | | * We can only use GSS_C_CHANNEL_BOUND_FLAG if the kerberos library |
419 | | * supports that in order to add KERB_AP_OPTIONS_CBT. |
420 | | * |
421 | | * See: |
422 | | * https://github.com/heimdal/heimdal/pull/1234 |
423 | | * https://github.com/krb5/krb5/pull/1329 |
424 | | */ |
425 | 0 | if (!(gensec_security->want_features & GENSEC_FEATURE_CB_OPTIONAL)) { |
426 | 0 | gensec_gssapi_state->gss_want_flags |= GSS_C_CHANNEL_BOUND_FLAG; |
427 | 0 | } |
428 | 0 | #endif |
429 | |
|
430 | 0 | if (cli_credentials_get_impersonate_principal(creds)) { |
431 | 0 | gensec_gssapi_state->gss_want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); |
432 | 0 | } |
433 | |
|
434 | 0 | return NT_STATUS_OK; |
435 | 0 | } |
436 | | |
437 | | static NTSTATUS gensec_gssapi_sasl_client_start(struct gensec_security *gensec_security) |
438 | 0 | { |
439 | 0 | NTSTATUS nt_status; |
440 | 0 | struct gensec_gssapi_state *gensec_gssapi_state; |
441 | 0 | nt_status = gensec_gssapi_client_start(gensec_security); |
442 | |
|
443 | 0 | if (NT_STATUS_IS_OK(nt_status)) { |
444 | 0 | gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
445 | 0 | gensec_gssapi_state->sasl = true; |
446 | 0 | } |
447 | 0 | return nt_status; |
448 | 0 | } |
449 | | |
450 | | static NTSTATUS gensec_gssapi_update_internal(struct gensec_security *gensec_security, |
451 | | TALLOC_CTX *out_mem_ctx, |
452 | | struct tevent_context *ev, |
453 | | const DATA_BLOB in, DATA_BLOB *out) |
454 | 0 | { |
455 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
456 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
457 | 0 | NTSTATUS nt_status; |
458 | 0 | OM_uint32 maj_stat, min_stat; |
459 | 0 | OM_uint32 min_stat2; |
460 | 0 | gss_buffer_desc input_token = { 0, NULL }; |
461 | 0 | gss_buffer_desc output_token = { 0, NULL }; |
462 | 0 | struct cli_credentials *cli_creds = gensec_get_credentials(gensec_security); |
463 | 0 | const char *target_principal = gensec_get_target_principal(gensec_security); |
464 | 0 | const char *hostname = gensec_get_target_hostname(gensec_security); |
465 | 0 | const char *service = gensec_get_target_service(gensec_security); |
466 | 0 | gss_OID gss_oid_p = NULL; |
467 | 0 | OM_uint32 time_req = 0; |
468 | 0 | OM_uint32 time_rec = 0; |
469 | 0 | struct timeval tv; |
470 | |
|
471 | 0 | time_req = gensec_setting_int(gensec_security->settings, |
472 | 0 | "gensec_gssapi", "requested_life_time", |
473 | 0 | time_req); |
474 | |
|
475 | 0 | input_token.length = in.length; |
476 | 0 | input_token.value = in.data; |
477 | |
|
478 | 0 | switch (gensec_gssapi_state->sasl_state) { |
479 | 0 | case STAGE_GSS_NEG: |
480 | 0 | { |
481 | 0 | switch (gensec_security->gensec_role) { |
482 | 0 | case GENSEC_CLIENT: |
483 | 0 | { |
484 | 0 | const char *client_realm = NULL; |
485 | 0 | #ifdef SAMBA4_USES_HEIMDAL |
486 | 0 | struct gsskrb5_send_to_kdc send_to_kdc; |
487 | 0 | krb5_error_code ret; |
488 | | #else |
489 | | bool fallback = false; |
490 | | #endif |
491 | |
|
492 | 0 | nt_status = gensec_gssapi_client_creds(gensec_security, ev); |
493 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
494 | 0 | return nt_status; |
495 | 0 | } |
496 | | |
497 | 0 | #ifdef SAMBA4_USES_HEIMDAL |
498 | 0 | send_to_kdc.func = smb_krb5_send_and_recv_func; |
499 | 0 | send_to_kdc.ptr = ev; |
500 | |
|
501 | 0 | min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc); |
502 | 0 | if (min_stat) { |
503 | 0 | DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); |
504 | 0 | return NT_STATUS_INTERNAL_ERROR; |
505 | 0 | } |
506 | 0 | #endif |
507 | | |
508 | | /* |
509 | | * With credentials for |
510 | | * administrator@FOREST1.EXAMPLE.COM this patch changes |
511 | | * the target_principal for the ldap service of host |
512 | | * dc2.forest2.example.com from |
513 | | * |
514 | | * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM |
515 | | * |
516 | | * to |
517 | | * |
518 | | * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM |
519 | | * |
520 | | * Typically |
521 | | * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM |
522 | | * should be used in order to allow the KDC of |
523 | | * FOREST1.EXAMPLE.COM to generate a referral ticket |
524 | | * for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM. |
525 | | * |
526 | | * The problem is that KDCs only return such referral |
527 | | * tickets if there's a forest trust between |
528 | | * FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM. If |
529 | | * there's only an external domain trust between |
530 | | * FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM the KDC |
531 | | * of FOREST1.EXAMPLE.COM will respond with |
532 | | * S_PRINCIPAL_UNKNOWN when being asked for |
533 | | * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM. |
534 | | * |
535 | | * In the case of an external trust the client can |
536 | | * still ask explicitly for |
537 | | * krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM and |
538 | | * the KDC of FOREST1.EXAMPLE.COM will generate it. |
539 | | * |
540 | | * From there the client can use the |
541 | | * krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM |
542 | | * ticket and ask a KDC of FOREST2.EXAMPLE.COM for a |
543 | | * service ticket for |
544 | | * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM. |
545 | | * |
546 | | * With Heimdal we'll get the fallback on |
547 | | * S_PRINCIPAL_UNKNOWN behavior when we pass |
548 | | * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM as |
549 | | * target principal. As _krb5_get_cred_kdc_any() first |
550 | | * calls get_cred_kdc_referral() (which always starts |
551 | | * with the client realm) and falls back to |
552 | | * get_cred_kdc_capath() (which starts with the given |
553 | | * realm). |
554 | | * |
555 | | * MIT krb5 only tries the given realm of the target |
556 | | * principal, if we want to autodetect support for |
557 | | * transitive forest trusts, would have to do the |
558 | | * fallback ourself. |
559 | | */ |
560 | 0 | client_realm = cli_credentials_get_realm(cli_creds); |
561 | | #ifndef SAMBA4_USES_HEIMDAL |
562 | | if (gensec_gssapi_state->server_name == NULL) { |
563 | | nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, |
564 | | target_principal, |
565 | | service, |
566 | | hostname, |
567 | | client_realm, |
568 | | gensec_gssapi_state->gss_oid, |
569 | | &gensec_gssapi_state->target_principal, |
570 | | &gensec_gssapi_state->server_name); |
571 | | if (!NT_STATUS_IS_OK(nt_status)) { |
572 | | return nt_status; |
573 | | } |
574 | | |
575 | | maj_stat = gss_init_sec_context(&min_stat, |
576 | | gensec_gssapi_state->client_cred->creds, |
577 | | &gensec_gssapi_state->gssapi_context, |
578 | | gensec_gssapi_state->server_name, |
579 | | gensec_gssapi_state->gss_oid, |
580 | | gensec_gssapi_state->gss_want_flags, |
581 | | time_req, |
582 | | gensec_gssapi_state->input_chan_bindings, |
583 | | &input_token, |
584 | | &gss_oid_p, |
585 | | &output_token, |
586 | | &gensec_gssapi_state->gss_got_flags, /* ret flags */ |
587 | | &time_rec); |
588 | | if (maj_stat != GSS_S_FAILURE) { |
589 | | goto init_sec_context_done; |
590 | | } |
591 | | if (min_stat != (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { |
592 | | goto init_sec_context_done; |
593 | | } |
594 | | if (target_principal != NULL) { |
595 | | goto init_sec_context_done; |
596 | | } |
597 | | |
598 | | fallback = true; |
599 | | TALLOC_FREE(gensec_gssapi_state->target_principal); |
600 | | gss_release_name(&min_stat2, &gensec_gssapi_state->server_name); |
601 | | } |
602 | | #endif /* !SAMBA4_USES_HEIMDAL */ |
603 | 0 | if (gensec_gssapi_state->server_name == NULL) { |
604 | 0 | const char *server_realm = NULL; |
605 | |
|
606 | 0 | server_realm = smb_krb5_get_realm_from_hostname(gensec_gssapi_state, |
607 | 0 | hostname, |
608 | 0 | client_realm); |
609 | 0 | if (server_realm == NULL) { |
610 | 0 | return NT_STATUS_NO_MEMORY; |
611 | 0 | } |
612 | | |
613 | | #ifndef SAMBA4_USES_HEIMDAL |
614 | | if (fallback && |
615 | | strequal(client_realm, server_realm)) { |
616 | | goto init_sec_context_done; |
617 | | } |
618 | | #endif /* !SAMBA4_USES_HEIMDAL */ |
619 | | |
620 | 0 | nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, |
621 | 0 | target_principal, |
622 | 0 | service, |
623 | 0 | hostname, |
624 | 0 | server_realm, |
625 | 0 | gensec_gssapi_state->gss_oid, |
626 | 0 | &gensec_gssapi_state->target_principal, |
627 | 0 | &gensec_gssapi_state->server_name); |
628 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
629 | 0 | return nt_status; |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | 0 | maj_stat = gss_init_sec_context(&min_stat, |
634 | 0 | gensec_gssapi_state->client_cred->creds, |
635 | 0 | &gensec_gssapi_state->gssapi_context, |
636 | 0 | gensec_gssapi_state->server_name, |
637 | 0 | gensec_gssapi_state->gss_oid, |
638 | 0 | gensec_gssapi_state->gss_want_flags, |
639 | 0 | time_req, |
640 | 0 | gensec_gssapi_state->input_chan_bindings, |
641 | 0 | &input_token, |
642 | 0 | &gss_oid_p, |
643 | 0 | &output_token, |
644 | 0 | &gensec_gssapi_state->gss_got_flags, /* ret flags */ |
645 | 0 | &time_rec); |
646 | 0 | goto init_sec_context_done; |
647 | | /* JUMP! */ |
648 | 0 | init_sec_context_done: |
649 | 0 | if (gss_oid_p) { |
650 | 0 | gensec_gssapi_state->gss_oid = gss_oid_p; |
651 | 0 | } |
652 | |
|
653 | 0 | #ifdef SAMBA4_USES_HEIMDAL |
654 | 0 | send_to_kdc.func = smb_krb5_send_and_recv_func; |
655 | 0 | send_to_kdc.ptr = NULL; |
656 | |
|
657 | 0 | ret = gsskrb5_set_send_to_kdc(&send_to_kdc); |
658 | 0 | if (ret) { |
659 | 0 | DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); |
660 | 0 | return NT_STATUS_INTERNAL_ERROR; |
661 | 0 | } |
662 | 0 | #endif |
663 | 0 | break; |
664 | 0 | } |
665 | 0 | case GENSEC_SERVER: |
666 | 0 | { |
667 | 0 | maj_stat = gss_accept_sec_context(&min_stat, |
668 | 0 | &gensec_gssapi_state->gssapi_context, |
669 | 0 | gensec_gssapi_state->server_cred->creds, |
670 | 0 | &input_token, |
671 | 0 | gensec_gssapi_state->input_chan_bindings, |
672 | 0 | &gensec_gssapi_state->client_name, |
673 | 0 | &gss_oid_p, |
674 | 0 | &output_token, |
675 | 0 | &gensec_gssapi_state->gss_got_flags, |
676 | 0 | &time_rec, |
677 | 0 | &gensec_gssapi_state->delegated_cred_handle); |
678 | 0 | if (gss_oid_p) { |
679 | 0 | gensec_gssapi_state->gss_oid = gss_oid_p; |
680 | 0 | } |
681 | 0 | #ifdef GSS_C_CHANNEL_BOUND_FLAG |
682 | 0 | if (maj_stat == GSS_S_COMPLETE && |
683 | 0 | gensec_security->channel_bindings != NULL && |
684 | 0 | !(gensec_security->want_features & GENSEC_FEATURE_CB_OPTIONAL) && |
685 | 0 | !(gensec_gssapi_state->gss_got_flags & GSS_C_CHANNEL_BOUND_FLAG)) |
686 | 0 | { |
687 | | /* |
688 | | * If we require valid channel bindings |
689 | | * we need to check the client provided |
690 | | * them. |
691 | | * |
692 | | * We detect this if |
693 | | * GSS_C_CHANNEL_BOUND_FLAG is given. |
694 | | * |
695 | | * Recent heimdal and MIT releases support this |
696 | | * with older releases (e.g. MIT > 1.19). |
697 | | * |
698 | | * It means client with zero channel bindings |
699 | | * on a server with non-zero channel bindings |
700 | | * won't generate GSS_S_BAD_BINDINGS directly |
701 | | * unless KERB_AP_OPTIONS_CBT was also |
702 | | * provides by the client. |
703 | | * |
704 | | * So we need to convert a missing |
705 | | * GSS_C_CHANNEL_BOUND_FLAG into |
706 | | * GSS_S_BAD_BINDINGS by default |
707 | | * (unless GENSEC_FEATURE_CB_OPTIONAL is given). |
708 | | */ |
709 | 0 | maj_stat = GSS_S_BAD_BINDINGS; |
710 | 0 | min_stat = 0; |
711 | 0 | } |
712 | 0 | #endif /* GSS_C_CHANNEL_BOUND_FLAG */ |
713 | |
|
714 | 0 | break; |
715 | 0 | } |
716 | 0 | default: |
717 | 0 | return NT_STATUS_INVALID_PARAMETER; |
718 | |
|
719 | 0 | } |
720 | | |
721 | 0 | gensec_gssapi_state->gss_exchange_count++; |
722 | |
|
723 | 0 | if (maj_stat == GSS_S_COMPLETE) { |
724 | 0 | *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); |
725 | 0 | gss_release_buffer(&min_stat2, &output_token); |
726 | |
|
727 | 0 | if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG && |
728 | 0 | gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { |
729 | 0 | DEBUG(5, ("gensec_gssapi: credentials were delegated\n")); |
730 | 0 | } else { |
731 | 0 | DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n")); |
732 | 0 | } |
733 | |
|
734 | 0 | tv = timeval_current_ofs(time_rec, 0); |
735 | 0 | gensec_gssapi_state->expire_time = timeval_to_nttime(&tv); |
736 | | |
737 | | /* We may have been invoked as SASL, so there |
738 | | * is more work to do */ |
739 | 0 | if (gensec_gssapi_state->sasl) { |
740 | 0 | gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG; |
741 | 0 | return NT_STATUS_MORE_PROCESSING_REQUIRED; |
742 | 0 | } else { |
743 | 0 | gensec_gssapi_state->sasl_state = STAGE_DONE; |
744 | |
|
745 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
746 | 0 | DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n")); |
747 | 0 | } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { |
748 | 0 | DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n")); |
749 | 0 | } else { |
750 | 0 | DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n")); |
751 | 0 | } |
752 | |
|
753 | 0 | return NT_STATUS_OK; |
754 | 0 | } |
755 | 0 | } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { |
756 | 0 | *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); |
757 | 0 | gss_release_buffer(&min_stat2, &output_token); |
758 | |
|
759 | 0 | return NT_STATUS_MORE_PROCESSING_REQUIRED; |
760 | 0 | } else if (maj_stat == GSS_S_BAD_BINDINGS) { |
761 | 0 | DBG_WARNING("Got GSS_S_BAD_BINDINGS\n"); |
762 | 0 | return NT_STATUS_BAD_BINDINGS; |
763 | 0 | } else if (maj_stat == GSS_S_CONTEXT_EXPIRED) { |
764 | 0 | gss_cred_id_t creds = NULL; |
765 | 0 | gss_name_t name; |
766 | 0 | gss_buffer_desc buffer; |
767 | 0 | OM_uint32 lifetime = 0; |
768 | 0 | gss_cred_usage_t usage; |
769 | 0 | const char *role = NULL; |
770 | |
|
771 | 0 | switch (gensec_security->gensec_role) { |
772 | 0 | case GENSEC_CLIENT: |
773 | 0 | creds = gensec_gssapi_state->client_cred->creds; |
774 | 0 | role = "client"; |
775 | 0 | break; |
776 | 0 | case GENSEC_SERVER: |
777 | 0 | creds = gensec_gssapi_state->server_cred->creds; |
778 | 0 | role = "server"; |
779 | 0 | break; |
780 | 0 | } |
781 | | |
782 | 0 | DBG_ERR("GSS %s Update(krb5)(%d) failed, credentials " |
783 | 0 | "expired during GSSAPI handshake!\n", |
784 | 0 | role, |
785 | 0 | gensec_gssapi_state->gss_exchange_count); |
786 | |
|
787 | 0 | maj_stat = gss_inquire_cred(&min_stat, |
788 | 0 | creds, |
789 | 0 | &name, &lifetime, &usage, NULL); |
790 | |
|
791 | 0 | if (maj_stat == GSS_S_COMPLETE) { |
792 | 0 | const char *usage_string = NULL; |
793 | 0 | switch (usage) { |
794 | 0 | case GSS_C_BOTH: |
795 | 0 | usage_string = "GSS_C_BOTH"; |
796 | 0 | break; |
797 | 0 | case GSS_C_ACCEPT: |
798 | 0 | usage_string = "GSS_C_ACCEPT"; |
799 | 0 | break; |
800 | 0 | case GSS_C_INITIATE: |
801 | 0 | usage_string = "GSS_C_INITIATE"; |
802 | 0 | break; |
803 | 0 | } |
804 | 0 | maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); |
805 | 0 | if (maj_stat) { |
806 | 0 | buffer.value = NULL; |
807 | 0 | buffer.length = 0; |
808 | 0 | } |
809 | 0 | if (lifetime > 0) { |
810 | 0 | DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", |
811 | 0 | (int)buffer.length, (int)buffer.length, (char *)buffer.value, |
812 | 0 | lifetime, usage_string)); |
813 | 0 | } else { |
814 | 0 | DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", |
815 | 0 | (int)buffer.length, (int)buffer.length, (char *)buffer.value, |
816 | 0 | usage_string)); |
817 | 0 | } |
818 | 0 | gss_release_buffer(&min_stat, &buffer); |
819 | 0 | gss_release_name(&min_stat, &name); |
820 | 0 | } else if (maj_stat != GSS_S_COMPLETE) { |
821 | 0 | DEBUG(0, ("inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n", |
822 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
823 | 0 | } |
824 | 0 | return NT_STATUS_INVALID_PARAMETER; |
825 | 0 | } else if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, |
826 | 0 | gss_mech_krb5)) { |
827 | 0 | switch (min_stat) { |
828 | 0 | case (OM_uint32)KRB5KRB_AP_ERR_TKT_NYV: |
829 | 0 | DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n", |
830 | 0 | gensec_gssapi_state->target_principal, |
831 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
832 | 0 | return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */ |
833 | 0 | case (OM_uint32)KRB5KRB_AP_ERR_TKT_EXPIRED: |
834 | 0 | DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n", |
835 | 0 | gensec_gssapi_state->target_principal, |
836 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
837 | 0 | return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ |
838 | 0 | case (OM_uint32)KRB5_KDC_UNREACH: |
839 | 0 | DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticket to %s: %s\n", |
840 | 0 | gensec_gssapi_state->target_principal, |
841 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
842 | 0 | return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */ |
843 | 0 | case (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: |
844 | 0 | DEBUG(3, ("Server %s is not registered with our KDC: %s\n", |
845 | 0 | gensec_gssapi_state->target_principal, |
846 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
847 | 0 | return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ |
848 | 0 | case (OM_uint32)KRB5KRB_AP_ERR_MSG_TYPE: |
849 | | /* garbage input, possibly from the auto-mech detection */ |
850 | 0 | return NT_STATUS_INVALID_PARAMETER; |
851 | 0 | default: |
852 | 0 | DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n", |
853 | 0 | gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", |
854 | 0 | gensec_gssapi_state->gss_exchange_count, |
855 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
856 | 0 | return NT_STATUS_LOGON_FAILURE; |
857 | 0 | } |
858 | 0 | } else { |
859 | 0 | DEBUG(1, ("GSS %s Update(%d) failed: %s\n", |
860 | 0 | gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", |
861 | 0 | gensec_gssapi_state->gss_exchange_count, |
862 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
863 | 0 | return NT_STATUS_LOGON_FAILURE; |
864 | 0 | } |
865 | 0 | break; |
866 | 0 | } |
867 | | |
868 | | /* These last two stages are only done if we were invoked as SASL */ |
869 | 0 | case STAGE_SASL_SSF_NEG: |
870 | 0 | { |
871 | 0 | switch (gensec_security->gensec_role) { |
872 | 0 | case GENSEC_CLIENT: |
873 | 0 | { |
874 | 0 | uint8_t maxlength_proposed[4]; |
875 | 0 | uint8_t maxlength_accepted[4]; |
876 | 0 | uint8_t security_supported; |
877 | 0 | int conf_state; |
878 | 0 | gss_qop_t qop_state; |
879 | 0 | input_token.length = in.length; |
880 | 0 | input_token.value = in.data; |
881 | | |
882 | | /* As a client, we have just send a |
883 | | * zero-length blob to the server (after the |
884 | | * normal GSSAPI exchange), and it has replied |
885 | | * with it's SASL negotiation */ |
886 | |
|
887 | 0 | maj_stat = gss_unwrap(&min_stat, |
888 | 0 | gensec_gssapi_state->gssapi_context, |
889 | 0 | &input_token, |
890 | 0 | &output_token, |
891 | 0 | &conf_state, |
892 | 0 | &qop_state); |
893 | 0 | if (GSS_ERROR(maj_stat)) { |
894 | 0 | DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", |
895 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
896 | 0 | return NT_STATUS_ACCESS_DENIED; |
897 | 0 | } |
898 | | |
899 | 0 | if (output_token.length < 4) { |
900 | 0 | gss_release_buffer(&min_stat, &output_token); |
901 | 0 | return NT_STATUS_INVALID_PARAMETER; |
902 | 0 | } |
903 | | |
904 | 0 | memcpy(maxlength_proposed, output_token.value, 4); |
905 | 0 | gss_release_buffer(&min_stat, &output_token); |
906 | | |
907 | | /* first byte is the proposed security */ |
908 | 0 | security_supported = maxlength_proposed[0]; |
909 | 0 | maxlength_proposed[0] = '\0'; |
910 | | |
911 | | /* Rest is the proposed max wrap length */ |
912 | 0 | gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), |
913 | 0 | gensec_gssapi_state->max_wrap_buf_size); |
914 | 0 | gensec_gssapi_state->sasl_protection = 0; |
915 | 0 | if (security_supported & NEG_SEAL) { |
916 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
917 | 0 | gensec_gssapi_state->sasl_protection |= NEG_SEAL; |
918 | 0 | } |
919 | 0 | } |
920 | 0 | if (security_supported & NEG_SIGN) { |
921 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { |
922 | 0 | gensec_gssapi_state->sasl_protection |= NEG_SIGN; |
923 | 0 | } |
924 | 0 | } |
925 | 0 | if (security_supported & NEG_NONE) { |
926 | 0 | gensec_gssapi_state->sasl_protection |= NEG_NONE; |
927 | 0 | } |
928 | 0 | if (gensec_gssapi_state->sasl_protection == 0) { |
929 | 0 | DEBUG(1, ("Remote server does not support unprotected connections\n")); |
930 | 0 | return NT_STATUS_ACCESS_DENIED; |
931 | 0 | } |
932 | | |
933 | | /* Send back the negotiated max length */ |
934 | | |
935 | 0 | RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size); |
936 | |
|
937 | 0 | maxlength_accepted[0] = gensec_gssapi_state->sasl_protection; |
938 | |
|
939 | 0 | input_token.value = maxlength_accepted; |
940 | 0 | input_token.length = sizeof(maxlength_accepted); |
941 | |
|
942 | 0 | maj_stat = gss_wrap(&min_stat, |
943 | 0 | gensec_gssapi_state->gssapi_context, |
944 | 0 | false, |
945 | 0 | GSS_C_QOP_DEFAULT, |
946 | 0 | &input_token, |
947 | 0 | &conf_state, |
948 | 0 | &output_token); |
949 | 0 | if (GSS_ERROR(maj_stat)) { |
950 | 0 | DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", |
951 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
952 | 0 | return NT_STATUS_ACCESS_DENIED; |
953 | 0 | } |
954 | | |
955 | 0 | *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); |
956 | 0 | gss_release_buffer(&min_stat, &output_token); |
957 | | |
958 | | /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ |
959 | 0 | gensec_gssapi_state->sasl_state = STAGE_DONE; |
960 | |
|
961 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
962 | 0 | DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n")); |
963 | 0 | } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { |
964 | 0 | DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n")); |
965 | 0 | } else { |
966 | 0 | DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographic protection\n")); |
967 | 0 | } |
968 | |
|
969 | 0 | return NT_STATUS_OK; |
970 | 0 | } |
971 | 0 | case GENSEC_SERVER: |
972 | 0 | { |
973 | 0 | uint8_t maxlength_proposed[4]; |
974 | 0 | uint8_t security_supported = 0x0; |
975 | 0 | int conf_state; |
976 | | |
977 | | /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */ |
978 | 0 | if (in.length != 0) { |
979 | 0 | DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n")); |
980 | 0 | } |
981 | | |
982 | | /* Give the client some idea what we will support */ |
983 | |
|
984 | 0 | RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size); |
985 | | /* first byte is the proposed security */ |
986 | 0 | maxlength_proposed[0] = '\0'; |
987 | |
|
988 | 0 | gensec_gssapi_state->sasl_protection = 0; |
989 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
990 | 0 | security_supported |= NEG_SEAL; |
991 | 0 | } |
992 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { |
993 | 0 | security_supported |= NEG_SIGN; |
994 | 0 | } |
995 | 0 | if (security_supported == 0) { |
996 | | /* If we don't support anything, this must be 0 */ |
997 | 0 | RSIVAL(maxlength_proposed, 0, 0x0); |
998 | 0 | } |
999 | | |
1000 | | /* TODO: We may not wish to support this */ |
1001 | 0 | security_supported |= NEG_NONE; |
1002 | 0 | maxlength_proposed[0] = security_supported; |
1003 | |
|
1004 | 0 | input_token.value = maxlength_proposed; |
1005 | 0 | input_token.length = sizeof(maxlength_proposed); |
1006 | |
|
1007 | 0 | maj_stat = gss_wrap(&min_stat, |
1008 | 0 | gensec_gssapi_state->gssapi_context, |
1009 | 0 | false, |
1010 | 0 | GSS_C_QOP_DEFAULT, |
1011 | 0 | &input_token, |
1012 | 0 | &conf_state, |
1013 | 0 | &output_token); |
1014 | 0 | if (GSS_ERROR(maj_stat)) { |
1015 | 0 | DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", |
1016 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
1017 | 0 | return NT_STATUS_ACCESS_DENIED; |
1018 | 0 | } |
1019 | | |
1020 | 0 | *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); |
1021 | 0 | gss_release_buffer(&min_stat, &output_token); |
1022 | |
|
1023 | 0 | gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT; |
1024 | 0 | return NT_STATUS_MORE_PROCESSING_REQUIRED; |
1025 | 0 | } |
1026 | 0 | default: |
1027 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1028 | |
|
1029 | 0 | } |
1030 | 0 | } |
1031 | | /* This is s server-only stage */ |
1032 | 0 | case STAGE_SASL_SSF_ACCEPT: |
1033 | 0 | { |
1034 | 0 | uint8_t maxlength_accepted[4]; |
1035 | 0 | uint8_t security_accepted; |
1036 | 0 | int conf_state; |
1037 | 0 | gss_qop_t qop_state; |
1038 | 0 | input_token.length = in.length; |
1039 | 0 | input_token.value = in.data; |
1040 | |
|
1041 | 0 | maj_stat = gss_unwrap(&min_stat, |
1042 | 0 | gensec_gssapi_state->gssapi_context, |
1043 | 0 | &input_token, |
1044 | 0 | &output_token, |
1045 | 0 | &conf_state, |
1046 | 0 | &qop_state); |
1047 | 0 | if (GSS_ERROR(maj_stat)) { |
1048 | 0 | DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", |
1049 | 0 | gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
1050 | 0 | return NT_STATUS_ACCESS_DENIED; |
1051 | 0 | } |
1052 | | |
1053 | 0 | if (output_token.length < 4) { |
1054 | 0 | gss_release_buffer(&min_stat, &output_token); |
1055 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1056 | 0 | } |
1057 | | |
1058 | 0 | memcpy(maxlength_accepted, output_token.value, 4); |
1059 | 0 | gss_release_buffer(&min_stat, &output_token); |
1060 | | |
1061 | | /* first byte is the proposed security */ |
1062 | 0 | security_accepted = maxlength_accepted[0]; |
1063 | 0 | maxlength_accepted[0] = '\0'; |
1064 | | |
1065 | | /* Rest is the proposed max wrap length */ |
1066 | 0 | gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), |
1067 | 0 | gensec_gssapi_state->max_wrap_buf_size); |
1068 | |
|
1069 | 0 | gensec_gssapi_state->sasl_protection = 0; |
1070 | 0 | if (security_accepted & NEG_SEAL) { |
1071 | 0 | if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
1072 | 0 | DEBUG(1, ("Remote client wanted seal, but gensec refused\n")); |
1073 | 0 | return NT_STATUS_ACCESS_DENIED; |
1074 | 0 | } |
1075 | 0 | gensec_gssapi_state->sasl_protection |= NEG_SEAL; |
1076 | 0 | } |
1077 | 0 | if (security_accepted & NEG_SIGN) { |
1078 | 0 | if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { |
1079 | 0 | DEBUG(1, ("Remote client wanted sign, but gensec refused\n")); |
1080 | 0 | return NT_STATUS_ACCESS_DENIED; |
1081 | 0 | } |
1082 | 0 | gensec_gssapi_state->sasl_protection |= NEG_SIGN; |
1083 | 0 | } |
1084 | 0 | if (security_accepted & NEG_NONE) { |
1085 | 0 | gensec_gssapi_state->sasl_protection |= NEG_NONE; |
1086 | 0 | } |
1087 | | |
1088 | | /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ |
1089 | 0 | gensec_gssapi_state->sasl_state = STAGE_DONE; |
1090 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
1091 | 0 | DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n")); |
1092 | 0 | } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { |
1093 | 0 | DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n")); |
1094 | 0 | } else { |
1095 | 0 | DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n")); |
1096 | 0 | } |
1097 | |
|
1098 | 0 | *out = data_blob(NULL, 0); |
1099 | 0 | return NT_STATUS_OK; |
1100 | 0 | } |
1101 | 0 | default: |
1102 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1103 | 0 | } |
1104 | 0 | } |
1105 | | |
1106 | | struct gensec_gssapi_update_state { |
1107 | | NTSTATUS status; |
1108 | | DATA_BLOB out; |
1109 | | }; |
1110 | | |
1111 | | static struct tevent_req *gensec_gssapi_update_send(TALLOC_CTX *mem_ctx, |
1112 | | struct tevent_context *ev, |
1113 | | struct gensec_security *gensec_security, |
1114 | | const DATA_BLOB in) |
1115 | 0 | { |
1116 | 0 | struct tevent_req *req = NULL; |
1117 | 0 | struct gensec_gssapi_update_state *state = NULL; |
1118 | 0 | NTSTATUS status; |
1119 | |
|
1120 | 0 | req = tevent_req_create(mem_ctx, &state, |
1121 | 0 | struct gensec_gssapi_update_state); |
1122 | 0 | if (req == NULL) { |
1123 | 0 | return NULL; |
1124 | 0 | } |
1125 | | |
1126 | 0 | status = gensec_gssapi_update_internal(gensec_security, |
1127 | 0 | state, ev, in, |
1128 | 0 | &state->out); |
1129 | 0 | state->status = status; |
1130 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
1131 | 0 | tevent_req_done(req); |
1132 | 0 | return tevent_req_post(req, ev); |
1133 | 0 | } |
1134 | 0 | if (tevent_req_nterror(req, status)) { |
1135 | 0 | return tevent_req_post(req, ev); |
1136 | 0 | } |
1137 | | |
1138 | 0 | tevent_req_done(req); |
1139 | 0 | return tevent_req_post(req, ev); |
1140 | 0 | } |
1141 | | |
1142 | | static NTSTATUS gensec_gssapi_update_recv(struct tevent_req *req, |
1143 | | TALLOC_CTX *out_mem_ctx, |
1144 | | DATA_BLOB *out) |
1145 | 0 | { |
1146 | 0 | struct gensec_gssapi_update_state *state = |
1147 | 0 | tevent_req_data(req, |
1148 | 0 | struct gensec_gssapi_update_state); |
1149 | 0 | NTSTATUS status; |
1150 | |
|
1151 | 0 | *out = data_blob_null; |
1152 | |
|
1153 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1154 | 0 | tevent_req_received(req); |
1155 | 0 | return status; |
1156 | 0 | } |
1157 | | |
1158 | 0 | *out = state->out; |
1159 | 0 | talloc_steal(out_mem_ctx, state->out.data); |
1160 | 0 | status = state->status; |
1161 | 0 | tevent_req_received(req); |
1162 | 0 | return status; |
1163 | 0 | } |
1164 | | |
1165 | | static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, |
1166 | | TALLOC_CTX *mem_ctx, |
1167 | | const DATA_BLOB *in, |
1168 | | DATA_BLOB *out) |
1169 | 0 | { |
1170 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1171 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1172 | 0 | OM_uint32 maj_stat, min_stat; |
1173 | 0 | gss_buffer_desc input_token, output_token; |
1174 | 0 | int conf_state; |
1175 | 0 | input_token.length = in->length; |
1176 | 0 | input_token.value = in->data; |
1177 | |
|
1178 | 0 | maj_stat = gss_wrap(&min_stat, |
1179 | 0 | gensec_gssapi_state->gssapi_context, |
1180 | 0 | gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), |
1181 | 0 | GSS_C_QOP_DEFAULT, |
1182 | 0 | &input_token, |
1183 | 0 | &conf_state, |
1184 | 0 | &output_token); |
1185 | 0 | if (GSS_ERROR(maj_stat)) { |
1186 | 0 | DEBUG(1, ("gensec_gssapi_wrap: GSS Wrap failed: %s\n", |
1187 | 0 | gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
1188 | 0 | return NT_STATUS_ACCESS_DENIED; |
1189 | 0 | } |
1190 | | |
1191 | 0 | *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); |
1192 | 0 | gss_release_buffer(&min_stat, &output_token); |
1193 | |
|
1194 | 0 | if (gensec_gssapi_state->sasl) { |
1195 | 0 | size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security); |
1196 | 0 | if (max_wrapped_size < out->length) { |
1197 | 0 | DEBUG(1, ("gensec_gssapi_wrap: when wrapped, INPUT data (%u) is grew to be larger than SASL negotiated maximum output size (%u > %u)\n", |
1198 | 0 | (unsigned)in->length, |
1199 | 0 | (unsigned)out->length, |
1200 | 0 | (unsigned int)max_wrapped_size)); |
1201 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1202 | 0 | } |
1203 | 0 | } |
1204 | | |
1205 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) |
1206 | 0 | && !conf_state) { |
1207 | 0 | return NT_STATUS_ACCESS_DENIED; |
1208 | 0 | } |
1209 | 0 | return NT_STATUS_OK; |
1210 | 0 | } |
1211 | | |
1212 | | static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, |
1213 | | TALLOC_CTX *mem_ctx, |
1214 | | const DATA_BLOB *in, |
1215 | | DATA_BLOB *out) |
1216 | 0 | { |
1217 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1218 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1219 | 0 | OM_uint32 maj_stat, min_stat; |
1220 | 0 | gss_buffer_desc input_token, output_token; |
1221 | 0 | int conf_state; |
1222 | 0 | gss_qop_t qop_state; |
1223 | 0 | input_token.length = in->length; |
1224 | 0 | input_token.value = in->data; |
1225 | |
|
1226 | 0 | if (gensec_gssapi_state->sasl) { |
1227 | 0 | size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security); |
1228 | 0 | if (max_wrapped_size < in->length) { |
1229 | 0 | DEBUG(1, ("gensec_gssapi_unwrap: WRAPPED data is larger than SASL negotiated maximum size\n")); |
1230 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1231 | 0 | } |
1232 | 0 | } |
1233 | | |
1234 | | /* |
1235 | | * FIXME: input_message_buffer is marked const, but gss_unwrap() may |
1236 | | * modify it (see calls to rrc_rotate() in _gssapi_unwrap_cfx()). |
1237 | | */ |
1238 | 0 | maj_stat = gss_unwrap(&min_stat, |
1239 | 0 | gensec_gssapi_state->gssapi_context, |
1240 | 0 | &input_token, |
1241 | 0 | &output_token, |
1242 | 0 | &conf_state, |
1243 | 0 | &qop_state); |
1244 | 0 | if (GSS_ERROR(maj_stat)) { |
1245 | 0 | DEBUG(1, ("gensec_gssapi_unwrap: GSS UnWrap failed: %s\n", |
1246 | 0 | gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
1247 | 0 | return NT_STATUS_ACCESS_DENIED; |
1248 | 0 | } |
1249 | | |
1250 | 0 | *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); |
1251 | 0 | gss_release_buffer(&min_stat, &output_token); |
1252 | |
|
1253 | 0 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) |
1254 | 0 | && !conf_state) { |
1255 | 0 | return NT_STATUS_ACCESS_DENIED; |
1256 | 0 | } |
1257 | 0 | return NT_STATUS_OK; |
1258 | 0 | } |
1259 | | |
1260 | | /* Find out the maximum input size negotiated on this connection */ |
1261 | | |
1262 | | static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security) |
1263 | 0 | { |
1264 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1265 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1266 | 0 | OM_uint32 maj_stat, min_stat; |
1267 | 0 | OM_uint32 max_input_size; |
1268 | |
|
1269 | 0 | maj_stat = gss_wrap_size_limit(&min_stat, |
1270 | 0 | gensec_gssapi_state->gssapi_context, |
1271 | 0 | gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), |
1272 | 0 | GSS_C_QOP_DEFAULT, |
1273 | 0 | gensec_gssapi_state->max_wrap_buf_size, |
1274 | 0 | &max_input_size); |
1275 | 0 | if (GSS_ERROR(maj_stat)) { |
1276 | 0 | TALLOC_CTX *mem_ctx = talloc_new(NULL); |
1277 | 0 | DEBUG(1, ("gensec_gssapi_max_input_size: determining signature size with gss_wrap_size_limit failed: %s\n", |
1278 | 0 | gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
1279 | 0 | talloc_free(mem_ctx); |
1280 | 0 | return 0; |
1281 | 0 | } |
1282 | | |
1283 | 0 | return max_input_size; |
1284 | 0 | } |
1285 | | |
1286 | | /* Find out the maximum output size negotiated on this connection */ |
1287 | | static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security) |
1288 | 0 | { |
1289 | 0 | struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);; |
1290 | 0 | return gensec_gssapi_state->max_wrap_buf_size; |
1291 | 0 | } |
1292 | | |
1293 | | static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, |
1294 | | TALLOC_CTX *mem_ctx, |
1295 | | uint8_t *data, size_t length, |
1296 | | const uint8_t *whole_pdu, size_t pdu_length, |
1297 | | DATA_BLOB *sig) |
1298 | 0 | { |
1299 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1300 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1301 | 0 | bool hdr_signing = false; |
1302 | 0 | size_t sig_size = 0; |
1303 | 0 | NTSTATUS status; |
1304 | |
|
1305 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) { |
1306 | 0 | hdr_signing = true; |
1307 | 0 | } |
1308 | |
|
1309 | 0 | sig_size = gensec_gssapi_sig_size(gensec_security, length); |
1310 | |
|
1311 | 0 | status = gssapi_seal_packet(gensec_gssapi_state->gssapi_context, |
1312 | 0 | gensec_gssapi_state->gss_oid, |
1313 | 0 | hdr_signing, sig_size, |
1314 | 0 | data, length, |
1315 | 0 | whole_pdu, pdu_length, |
1316 | 0 | mem_ctx, sig); |
1317 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1318 | 0 | DEBUG(0, ("gssapi_seal_packet(hdr_signing=%u,sig_size=%zu," |
1319 | 0 | "data=%zu,pdu=%zu) failed: %s\n", |
1320 | 0 | hdr_signing, sig_size, length, pdu_length, |
1321 | 0 | nt_errstr(status))); |
1322 | 0 | return status; |
1323 | 0 | } |
1324 | | |
1325 | 0 | return NT_STATUS_OK; |
1326 | 0 | } |
1327 | | |
1328 | | static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, |
1329 | | uint8_t *data, size_t length, |
1330 | | const uint8_t *whole_pdu, size_t pdu_length, |
1331 | | const DATA_BLOB *sig) |
1332 | 0 | { |
1333 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1334 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1335 | 0 | bool hdr_signing = false; |
1336 | 0 | NTSTATUS status; |
1337 | |
|
1338 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) { |
1339 | 0 | hdr_signing = true; |
1340 | 0 | } |
1341 | |
|
1342 | 0 | status = gssapi_unseal_packet(gensec_gssapi_state->gssapi_context, |
1343 | 0 | gensec_gssapi_state->gss_oid, |
1344 | 0 | hdr_signing, |
1345 | 0 | data, length, |
1346 | 0 | whole_pdu, pdu_length, |
1347 | 0 | sig); |
1348 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1349 | 0 | DEBUG(0, ("gssapi_unseal_packet(hdr_signing=%u,sig_size=%zu," |
1350 | 0 | "data=%zu,pdu=%zu) failed: %s\n", |
1351 | 0 | hdr_signing, sig->length, length, pdu_length, |
1352 | 0 | nt_errstr(status))); |
1353 | 0 | return status; |
1354 | 0 | } |
1355 | | |
1356 | 0 | return NT_STATUS_OK; |
1357 | 0 | } |
1358 | | |
1359 | | static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security, |
1360 | | TALLOC_CTX *mem_ctx, |
1361 | | const uint8_t *data, size_t length, |
1362 | | const uint8_t *whole_pdu, size_t pdu_length, |
1363 | | DATA_BLOB *sig) |
1364 | 0 | { |
1365 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1366 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1367 | 0 | bool hdr_signing = false; |
1368 | 0 | NTSTATUS status; |
1369 | |
|
1370 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) { |
1371 | 0 | hdr_signing = true; |
1372 | 0 | } |
1373 | |
|
1374 | 0 | status = gssapi_sign_packet(gensec_gssapi_state->gssapi_context, |
1375 | 0 | gensec_gssapi_state->gss_oid, |
1376 | 0 | hdr_signing, |
1377 | 0 | data, length, |
1378 | 0 | whole_pdu, pdu_length, |
1379 | 0 | mem_ctx, sig); |
1380 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1381 | 0 | DEBUG(0, ("gssapi_sign_packet(hdr_signing=%u," |
1382 | 0 | "data=%zu,pdu=%zu) failed: %s\n", |
1383 | 0 | hdr_signing, length, pdu_length, |
1384 | 0 | nt_errstr(status))); |
1385 | 0 | return status; |
1386 | 0 | } |
1387 | | |
1388 | 0 | return NT_STATUS_OK; |
1389 | 0 | } |
1390 | | |
1391 | | static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, |
1392 | | const uint8_t *data, size_t length, |
1393 | | const uint8_t *whole_pdu, size_t pdu_length, |
1394 | | const DATA_BLOB *sig) |
1395 | 0 | { |
1396 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1397 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1398 | 0 | bool hdr_signing = false; |
1399 | 0 | NTSTATUS status; |
1400 | |
|
1401 | 0 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) { |
1402 | 0 | hdr_signing = true; |
1403 | 0 | } |
1404 | |
|
1405 | 0 | status = gssapi_check_packet(gensec_gssapi_state->gssapi_context, |
1406 | 0 | gensec_gssapi_state->gss_oid, |
1407 | 0 | hdr_signing, |
1408 | 0 | data, length, |
1409 | 0 | whole_pdu, pdu_length, |
1410 | 0 | sig); |
1411 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1412 | 0 | DEBUG(0, ("gssapi_check_packet(hdr_signing=%u,sig_size=%zu," |
1413 | 0 | "data=%zu,pdu=%zu) failed: %s\n", |
1414 | 0 | hdr_signing, sig->length, length, pdu_length, |
1415 | 0 | nt_errstr(status))); |
1416 | 0 | return status; |
1417 | 0 | } |
1418 | | |
1419 | 0 | return NT_STATUS_OK; |
1420 | 0 | } |
1421 | | |
1422 | | /* Try to figure out what features we actually got on the connection */ |
1423 | | static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security, |
1424 | | uint32_t feature) |
1425 | 0 | { |
1426 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1427 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1428 | 0 | if (feature & GENSEC_FEATURE_SIGN) { |
1429 | | /* If we are going GSSAPI SASL, then we honour the second negotiation */ |
1430 | 0 | if (gensec_gssapi_state->sasl |
1431 | 0 | && gensec_gssapi_state->sasl_state == STAGE_DONE) { |
1432 | 0 | return ((gensec_gssapi_state->sasl_protection & NEG_SIGN) |
1433 | 0 | && (gensec_gssapi_state->gss_got_flags & GSS_C_INTEG_FLAG)); |
1434 | 0 | } |
1435 | 0 | return gensec_gssapi_state->gss_got_flags & GSS_C_INTEG_FLAG; |
1436 | 0 | } |
1437 | 0 | if (feature & GENSEC_FEATURE_SEAL) { |
1438 | | /* If we are going GSSAPI SASL, then we honour the second negotiation */ |
1439 | 0 | if (gensec_gssapi_state->sasl |
1440 | 0 | && gensec_gssapi_state->sasl_state == STAGE_DONE) { |
1441 | 0 | return ((gensec_gssapi_state->sasl_protection & NEG_SEAL) |
1442 | 0 | && (gensec_gssapi_state->gss_got_flags & GSS_C_CONF_FLAG)); |
1443 | 0 | } |
1444 | 0 | return gensec_gssapi_state->gss_got_flags & GSS_C_CONF_FLAG; |
1445 | 0 | } |
1446 | 0 | if (feature & GENSEC_FEATURE_SESSION_KEY) { |
1447 | | /* Only for GSSAPI/Krb5 */ |
1448 | 0 | if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, |
1449 | 0 | gss_mech_krb5)) { |
1450 | 0 | return true; |
1451 | 0 | } |
1452 | 0 | } |
1453 | 0 | if (feature & GENSEC_FEATURE_DCE_STYLE) { |
1454 | 0 | return gensec_gssapi_state->gss_got_flags & GSS_C_DCE_STYLE; |
1455 | 0 | } |
1456 | 0 | if (feature & GENSEC_FEATURE_NEW_SPNEGO) { |
1457 | 0 | NTSTATUS status; |
1458 | 0 | uint32_t keytype; |
1459 | |
|
1460 | 0 | if (!(gensec_gssapi_state->gss_got_flags & GSS_C_INTEG_FLAG)) { |
1461 | 0 | return false; |
1462 | 0 | } |
1463 | | |
1464 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "force_new_spnego", false)) { |
1465 | 0 | return true; |
1466 | 0 | } |
1467 | 0 | if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "disable_new_spnego", false)) { |
1468 | 0 | return false; |
1469 | 0 | } |
1470 | | |
1471 | 0 | status = gssapi_get_session_key(gensec_gssapi_state, |
1472 | 0 | gensec_gssapi_state->gssapi_context, NULL, &keytype); |
1473 | | /* |
1474 | | * We should do a proper sig on the mechListMic unless |
1475 | | * we know we have to be backwards compatible with |
1476 | | * earlier windows versions. |
1477 | | * |
1478 | | * Negotiating a non-krb5 |
1479 | | * mech for example should be regarded as having |
1480 | | * NEW_SPNEGO |
1481 | | */ |
1482 | 0 | if (NT_STATUS_IS_OK(status)) { |
1483 | 0 | switch (keytype) { |
1484 | 0 | case ENCTYPE_DES_CBC_CRC: |
1485 | 0 | case ENCTYPE_DES_CBC_MD5: |
1486 | 0 | case ENCTYPE_ARCFOUR_HMAC: |
1487 | 0 | case ENCTYPE_DES3_CBC_SHA1: |
1488 | 0 | return false; |
1489 | 0 | } |
1490 | 0 | } |
1491 | 0 | return true; |
1492 | 0 | } |
1493 | | /* We can always do async (rather than strict request/reply) packets. */ |
1494 | 0 | if (feature & GENSEC_FEATURE_ASYNC_REPLIES) { |
1495 | 0 | return true; |
1496 | 0 | } |
1497 | 0 | if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) { |
1498 | 0 | return true; |
1499 | 0 | } |
1500 | 0 | return false; |
1501 | 0 | } |
1502 | | |
1503 | | static NTTIME gensec_gssapi_expire_time(struct gensec_security *gensec_security) |
1504 | 0 | { |
1505 | 0 | struct gensec_gssapi_state *gensec_gssapi_state = |
1506 | 0 | talloc_get_type_abort(gensec_security->private_data, |
1507 | 0 | struct gensec_gssapi_state); |
1508 | |
|
1509 | 0 | return gensec_gssapi_state->expire_time; |
1510 | 0 | } |
1511 | | |
1512 | | /* |
1513 | | * Extract the 'session key' needed by SMB signing and ncacn_np |
1514 | | * (for encrypting some passwords). |
1515 | | * |
1516 | | * This breaks all the abstractions, but what do you expect... |
1517 | | */ |
1518 | | static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, |
1519 | | TALLOC_CTX *mem_ctx, |
1520 | | DATA_BLOB *session_key) |
1521 | 0 | { |
1522 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1523 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1524 | 0 | return gssapi_get_session_key(mem_ctx, gensec_gssapi_state->gssapi_context, session_key, NULL); |
1525 | 0 | } |
1526 | | |
1527 | | /* Get some basic (and authorization) information about the user on |
1528 | | * this session. This uses either the PAC (if present) or a local |
1529 | | * database lookup */ |
1530 | | static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security, |
1531 | | TALLOC_CTX *mem_ctx, |
1532 | | struct auth_session_info **_session_info) |
1533 | 0 | { |
1534 | 0 | NTSTATUS nt_status; |
1535 | 0 | TALLOC_CTX *tmp_ctx; |
1536 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1537 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1538 | 0 | struct auth_session_info *session_info = NULL; |
1539 | 0 | OM_uint32 maj_stat, min_stat; |
1540 | 0 | DATA_BLOB pac_blob, *pac_blob_ptr = NULL; |
1541 | |
|
1542 | 0 | gss_buffer_desc name_token; |
1543 | 0 | char *principal_string; |
1544 | |
|
1545 | 0 | tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context"); |
1546 | 0 | NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); |
1547 | | |
1548 | 0 | maj_stat = gss_display_name (&min_stat, |
1549 | 0 | gensec_gssapi_state->client_name, |
1550 | 0 | &name_token, |
1551 | 0 | NULL); |
1552 | 0 | if (GSS_ERROR(maj_stat)) { |
1553 | 0 | DEBUG(1, ("GSS display_name failed: %s\n", |
1554 | 0 | gssapi_error_string(tmp_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); |
1555 | 0 | talloc_free(tmp_ctx); |
1556 | 0 | return NT_STATUS_FOOBAR; |
1557 | 0 | } |
1558 | | |
1559 | 0 | principal_string = talloc_strndup(tmp_ctx, |
1560 | 0 | (const char *)name_token.value, |
1561 | 0 | name_token.length); |
1562 | |
|
1563 | 0 | gss_release_buffer(&min_stat, &name_token); |
1564 | |
|
1565 | 0 | if (!principal_string) { |
1566 | 0 | talloc_free(tmp_ctx); |
1567 | 0 | return NT_STATUS_NO_MEMORY; |
1568 | 0 | } |
1569 | | |
1570 | 0 | nt_status = gssapi_obtain_pac_blob(tmp_ctx, gensec_gssapi_state->gssapi_context, |
1571 | 0 | gensec_gssapi_state->client_name, |
1572 | 0 | &pac_blob); |
1573 | | |
1574 | | /* IF we have the PAC - otherwise we need to get this |
1575 | | * data from elsewhere - local ldb, or (TODO) lookup of some |
1576 | | * kind... |
1577 | | */ |
1578 | 0 | if (NT_STATUS_IS_OK(nt_status)) { |
1579 | 0 | pac_blob_ptr = &pac_blob; |
1580 | 0 | } |
1581 | 0 | nt_status = gensec_generate_session_info_pac(tmp_ctx, |
1582 | 0 | gensec_security, |
1583 | 0 | gensec_gssapi_state->smb_krb5_context, |
1584 | 0 | pac_blob_ptr, principal_string, |
1585 | 0 | gensec_get_remote_address(gensec_security), |
1586 | 0 | &session_info); |
1587 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
1588 | 0 | talloc_free(tmp_ctx); |
1589 | 0 | return nt_status; |
1590 | 0 | } |
1591 | | |
1592 | 0 | nt_status = gensec_gssapi_session_key(gensec_security, session_info, &session_info->session_key); |
1593 | 0 | if (!NT_STATUS_IS_OK(nt_status)) { |
1594 | 0 | talloc_free(tmp_ctx); |
1595 | 0 | return nt_status; |
1596 | 0 | } |
1597 | | |
1598 | 0 | if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG && |
1599 | 0 | gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { |
1600 | 0 | krb5_error_code ret; |
1601 | 0 | const char *error_string; |
1602 | |
|
1603 | 0 | DEBUG(10, ("gensec_gssapi: delegated credentials supplied by client\n")); |
1604 | | |
1605 | | /* |
1606 | | * Create anonymous credentials for now. |
1607 | | * |
1608 | | * We will update them with the provided client gss creds. |
1609 | | */ |
1610 | 0 | session_info->credentials = cli_credentials_init_anon(session_info); |
1611 | 0 | if (session_info->credentials == NULL) { |
1612 | 0 | talloc_free(tmp_ctx); |
1613 | 0 | return NT_STATUS_NO_MEMORY; |
1614 | 0 | } |
1615 | | |
1616 | 0 | ret = cli_credentials_set_client_gss_creds(session_info->credentials, |
1617 | 0 | gensec_security->settings->lp_ctx, |
1618 | 0 | gensec_gssapi_state->delegated_cred_handle, |
1619 | 0 | CRED_SPECIFIED, &error_string); |
1620 | 0 | if (ret) { |
1621 | 0 | talloc_free(tmp_ctx); |
1622 | 0 | DEBUG(2,("Failed to get gss creds: %s\n", error_string)); |
1623 | 0 | return NT_STATUS_NO_MEMORY; |
1624 | 0 | } |
1625 | | |
1626 | | /* This credential handle isn't useful for password authentication, so ensure nobody tries to do that */ |
1627 | 0 | cli_credentials_set_kerberos_state(session_info->credentials, |
1628 | 0 | CRED_USE_KERBEROS_REQUIRED, |
1629 | 0 | CRED_SPECIFIED); |
1630 | | |
1631 | | /* It has been taken from this place... */ |
1632 | 0 | gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL; |
1633 | 0 | } else { |
1634 | 0 | DEBUG(10, ("gensec_gssapi: NO delegated credentials supplied by client\n")); |
1635 | 0 | } |
1636 | | |
1637 | 0 | *_session_info = talloc_steal(mem_ctx, session_info); |
1638 | 0 | talloc_free(tmp_ctx); |
1639 | |
|
1640 | 0 | return NT_STATUS_OK; |
1641 | 0 | } |
1642 | | |
1643 | | static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size) |
1644 | 0 | { |
1645 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1646 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1647 | 0 | size_t sig_size; |
1648 | |
|
1649 | 0 | if (gensec_gssapi_state->sig_size > 0) { |
1650 | 0 | return gensec_gssapi_state->sig_size; |
1651 | 0 | } |
1652 | | |
1653 | 0 | sig_size = gssapi_get_sig_size(gensec_gssapi_state->gssapi_context, |
1654 | 0 | gensec_gssapi_state->gss_oid, |
1655 | 0 | gensec_gssapi_state->gss_got_flags, |
1656 | 0 | data_size); |
1657 | |
|
1658 | 0 | gensec_gssapi_state->sig_size = sig_size; |
1659 | 0 | return gensec_gssapi_state->sig_size; |
1660 | 0 | } |
1661 | | |
1662 | | static const char *gensec_gssapi_final_auth_type(struct gensec_security *gensec_security) |
1663 | 0 | { |
1664 | 0 | struct gensec_gssapi_state *gensec_gssapi_state |
1665 | 0 | = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); |
1666 | | /* Only return the string for GSSAPI/Krb5 */ |
1667 | 0 | if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, |
1668 | 0 | gss_mech_krb5)) { |
1669 | 0 | return GENSEC_FINAL_AUTH_TYPE_KRB5; |
1670 | 0 | } else { |
1671 | 0 | return "gensec_gssapi: UNKNOWN MECH"; |
1672 | 0 | } |
1673 | 0 | } |
1674 | | |
1675 | | static const char *gensec_gssapi_krb5_oids[] = { |
1676 | | GENSEC_OID_KERBEROS5_OLD, |
1677 | | GENSEC_OID_KERBEROS5, |
1678 | | NULL |
1679 | | }; |
1680 | | |
1681 | | static const char *gensec_gssapi_spnego_oids[] = { |
1682 | | GENSEC_OID_SPNEGO, |
1683 | | NULL |
1684 | | }; |
1685 | | |
1686 | | /* As a server, this could in theory accept any GSSAPI mech */ |
1687 | | static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = { |
1688 | | .name = "gssapi_spnego", |
1689 | | .sasl_name = "GSS-SPNEGO", |
1690 | | .auth_type = DCERPC_AUTH_TYPE_SPNEGO, |
1691 | | .oid = gensec_gssapi_spnego_oids, |
1692 | | .client_start = gensec_gssapi_client_start, |
1693 | | .server_start = gensec_gssapi_server_start, |
1694 | | .magic = gensec_magic_check_krb5_oid, |
1695 | | .update_send = gensec_gssapi_update_send, |
1696 | | .update_recv = gensec_gssapi_update_recv, |
1697 | | .session_key = gensec_gssapi_session_key, |
1698 | | .session_info = gensec_gssapi_session_info, |
1699 | | .sign_packet = gensec_gssapi_sign_packet, |
1700 | | .check_packet = gensec_gssapi_check_packet, |
1701 | | .seal_packet = gensec_gssapi_seal_packet, |
1702 | | .unseal_packet = gensec_gssapi_unseal_packet, |
1703 | | .max_input_size = gensec_gssapi_max_input_size, |
1704 | | .max_wrapped_size = gensec_gssapi_max_wrapped_size, |
1705 | | .wrap = gensec_gssapi_wrap, |
1706 | | .unwrap = gensec_gssapi_unwrap, |
1707 | | .have_feature = gensec_gssapi_have_feature, |
1708 | | .expire_time = gensec_gssapi_expire_time, |
1709 | | .final_auth_type = gensec_gssapi_final_auth_type, |
1710 | | .enabled = false, |
1711 | | .kerberos = true, |
1712 | | .priority = GENSEC_GSSAPI |
1713 | | }; |
1714 | | |
1715 | | /* As a server, this could in theory accept any GSSAPI mech */ |
1716 | | static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = { |
1717 | | .name = "gssapi_krb5", |
1718 | | .auth_type = DCERPC_AUTH_TYPE_KRB5, |
1719 | | .oid = gensec_gssapi_krb5_oids, |
1720 | | .client_start = gensec_gssapi_client_start, |
1721 | | .server_start = gensec_gssapi_server_start, |
1722 | | .magic = gensec_magic_check_krb5_oid, |
1723 | | .update_send = gensec_gssapi_update_send, |
1724 | | .update_recv = gensec_gssapi_update_recv, |
1725 | | .session_key = gensec_gssapi_session_key, |
1726 | | .session_info = gensec_gssapi_session_info, |
1727 | | .sig_size = gensec_gssapi_sig_size, |
1728 | | .sign_packet = gensec_gssapi_sign_packet, |
1729 | | .check_packet = gensec_gssapi_check_packet, |
1730 | | .seal_packet = gensec_gssapi_seal_packet, |
1731 | | .unseal_packet = gensec_gssapi_unseal_packet, |
1732 | | .max_input_size = gensec_gssapi_max_input_size, |
1733 | | .max_wrapped_size = gensec_gssapi_max_wrapped_size, |
1734 | | .wrap = gensec_gssapi_wrap, |
1735 | | .unwrap = gensec_gssapi_unwrap, |
1736 | | .have_feature = gensec_gssapi_have_feature, |
1737 | | .expire_time = gensec_gssapi_expire_time, |
1738 | | .final_auth_type = gensec_gssapi_final_auth_type, |
1739 | | .enabled = true, |
1740 | | .kerberos = true, |
1741 | | .priority = GENSEC_GSSAPI |
1742 | | }; |
1743 | | |
1744 | | /* As a server, this could in theory accept any GSSAPI mech */ |
1745 | | static const struct gensec_security_ops gensec_gssapi_sasl_krb5_security_ops = { |
1746 | | .name = "gssapi_krb5_sasl", |
1747 | | .sasl_name = "GSSAPI", |
1748 | | .client_start = gensec_gssapi_sasl_client_start, |
1749 | | .server_start = gensec_gssapi_sasl_server_start, |
1750 | | .update_send = gensec_gssapi_update_send, |
1751 | | .update_recv = gensec_gssapi_update_recv, |
1752 | | .session_key = gensec_gssapi_session_key, |
1753 | | .session_info = gensec_gssapi_session_info, |
1754 | | .max_input_size = gensec_gssapi_max_input_size, |
1755 | | .max_wrapped_size = gensec_gssapi_max_wrapped_size, |
1756 | | .wrap = gensec_gssapi_wrap, |
1757 | | .unwrap = gensec_gssapi_unwrap, |
1758 | | .have_feature = gensec_gssapi_have_feature, |
1759 | | .expire_time = gensec_gssapi_expire_time, |
1760 | | .final_auth_type = gensec_gssapi_final_auth_type, |
1761 | | .enabled = true, |
1762 | | .kerberos = true, |
1763 | | .priority = GENSEC_GSSAPI |
1764 | | }; |
1765 | | |
1766 | | _PUBLIC_ NTSTATUS gensec_gssapi_init(TALLOC_CTX *ctx) |
1767 | 0 | { |
1768 | 0 | NTSTATUS ret; |
1769 | |
|
1770 | 0 | ret = gensec_register(ctx, &gensec_gssapi_spnego_security_ops); |
1771 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
1772 | 0 | DEBUG(0,("Failed to register '%s' gensec backend!\n", |
1773 | 0 | gensec_gssapi_spnego_security_ops.name)); |
1774 | 0 | return ret; |
1775 | 0 | } |
1776 | | |
1777 | 0 | ret = gensec_register(ctx, &gensec_gssapi_krb5_security_ops); |
1778 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
1779 | 0 | DEBUG(0,("Failed to register '%s' gensec backend!\n", |
1780 | 0 | gensec_gssapi_krb5_security_ops.name)); |
1781 | 0 | return ret; |
1782 | 0 | } |
1783 | | |
1784 | 0 | ret = gensec_register(ctx, &gensec_gssapi_sasl_krb5_security_ops); |
1785 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
1786 | 0 | DEBUG(0,("Failed to register '%s' gensec backend!\n", |
1787 | 0 | gensec_gssapi_sasl_krb5_security_ops.name)); |
1788 | 0 | return ret; |
1789 | 0 | } |
1790 | | |
1791 | 0 | return ret; |
1792 | 0 | } |