1 | /*
|
---|
2 | * GSSAPI Security Extensions
|
---|
3 | * RPC Pipe client and server routines
|
---|
4 | * Copyright (C) Simo Sorce 2010.
|
---|
5 | * Copyright (C) Andrew Bartlett 2004-2011.
|
---|
6 | * Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2005
|
---|
7 | *
|
---|
8 | * This program is free software; you can redistribute it and/or modify
|
---|
9 | * it under the terms of the GNU General Public License as published by
|
---|
10 | * the Free Software Foundation; either version 3 of the License, or
|
---|
11 | * (at your option) any later version.
|
---|
12 | *
|
---|
13 | * This program is distributed in the hope that it will be useful,
|
---|
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
16 | * GNU General Public License for more details.
|
---|
17 | *
|
---|
18 | * You should have received a copy of the GNU General Public License
|
---|
19 | * along with this program; if not, see <http://www.gnu.org/licenses/>.
|
---|
20 | */
|
---|
21 |
|
---|
22 | /* We support only GSSAPI/KRB5 here */
|
---|
23 |
|
---|
24 | #include "includes.h"
|
---|
25 | #include "gse.h"
|
---|
26 | #include "libads/kerberos_proto.h"
|
---|
27 | #include "auth/common_auth.h"
|
---|
28 | #include "auth/gensec/gensec.h"
|
---|
29 | #include "auth/gensec/gensec_internal.h"
|
---|
30 | #include "auth/credentials/credentials.h"
|
---|
31 | #include "../librpc/gen_ndr/dcerpc.h"
|
---|
32 |
|
---|
33 | #if defined(HAVE_KRB5)
|
---|
34 |
|
---|
35 | #include "auth/kerberos/pac_utils.h"
|
---|
36 | #include "auth/kerberos/gssapi_helper.h"
|
---|
37 | #include "gse_krb5.h"
|
---|
38 |
|
---|
39 | static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min);
|
---|
40 | static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
|
---|
41 | size_t data_size);
|
---|
42 |
|
---|
43 | struct gse_context {
|
---|
44 | gss_ctx_id_t gssapi_context;
|
---|
45 | gss_name_t server_name;
|
---|
46 | gss_name_t client_name;
|
---|
47 | OM_uint32 gss_want_flags, gss_got_flags;
|
---|
48 | size_t max_wrap_buf_size;
|
---|
49 | size_t sig_size;
|
---|
50 |
|
---|
51 | gss_cred_id_t delegated_cred_handle;
|
---|
52 |
|
---|
53 | NTTIME expire_time;
|
---|
54 |
|
---|
55 | /* gensec_gse only */
|
---|
56 | krb5_context k5ctx;
|
---|
57 | krb5_ccache ccache;
|
---|
58 | krb5_keytab keytab;
|
---|
59 |
|
---|
60 | gss_OID_desc gss_mech;
|
---|
61 | gss_cred_id_t creds;
|
---|
62 |
|
---|
63 | gss_OID ret_mech;
|
---|
64 | };
|
---|
65 |
|
---|
66 | /* free non talloc dependent contexts */
|
---|
67 | static int gse_context_destructor(void *ptr)
|
---|
68 | {
|
---|
69 | struct gse_context *gse_ctx;
|
---|
70 | OM_uint32 gss_min;
|
---|
71 |
|
---|
72 | gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
|
---|
73 | if (gse_ctx->k5ctx) {
|
---|
74 | if (gse_ctx->ccache) {
|
---|
75 | krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
|
---|
76 | gse_ctx->ccache = NULL;
|
---|
77 | }
|
---|
78 | if (gse_ctx->keytab) {
|
---|
79 | krb5_kt_close(gse_ctx->k5ctx, gse_ctx->keytab);
|
---|
80 | gse_ctx->keytab = NULL;
|
---|
81 | }
|
---|
82 | krb5_free_context(gse_ctx->k5ctx);
|
---|
83 | gse_ctx->k5ctx = NULL;
|
---|
84 | }
|
---|
85 | if (gse_ctx->gssapi_context != GSS_C_NO_CONTEXT) {
|
---|
86 | (void)gss_delete_sec_context(&gss_min,
|
---|
87 | &gse_ctx->gssapi_context,
|
---|
88 | GSS_C_NO_BUFFER);
|
---|
89 | }
|
---|
90 | if (gse_ctx->server_name) {
|
---|
91 | (void)gss_release_name(&gss_min,
|
---|
92 | &gse_ctx->server_name);
|
---|
93 | }
|
---|
94 | if (gse_ctx->client_name) {
|
---|
95 | (void)gss_release_name(&gss_min,
|
---|
96 | &gse_ctx->client_name);
|
---|
97 | }
|
---|
98 | if (gse_ctx->creds) {
|
---|
99 | (void)gss_release_cred(&gss_min,
|
---|
100 | &gse_ctx->creds);
|
---|
101 | }
|
---|
102 | if (gse_ctx->delegated_cred_handle) {
|
---|
103 | (void)gss_release_cred(&gss_min,
|
---|
104 | &gse_ctx->delegated_cred_handle);
|
---|
105 | }
|
---|
106 |
|
---|
107 | /* MIT and Heimdal differ as to if you can call
|
---|
108 | * gss_release_oid() on this OID, generated by
|
---|
109 | * gss_{accept,init}_sec_context(). However, as long as the
|
---|
110 | * oid is gss_mech_krb5 (which it always is at the moment),
|
---|
111 | * then this is a moot point, as both declare this particular
|
---|
112 | * OID static, and so no memory is lost. This assert is in
|
---|
113 | * place to ensure that the programmer who wishes to extend
|
---|
114 | * this code to EAP or other GSS mechanisms determines an
|
---|
115 | * implementation-dependent way of releasing any dynamically
|
---|
116 | * allocated OID */
|
---|
117 | SMB_ASSERT(smb_gss_oid_equal(&gse_ctx->gss_mech, GSS_C_NO_OID) ||
|
---|
118 | smb_gss_oid_equal(&gse_ctx->gss_mech, gss_mech_krb5));
|
---|
119 |
|
---|
120 | return 0;
|
---|
121 | }
|
---|
122 |
|
---|
123 | static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx,
|
---|
124 | bool do_sign, bool do_seal,
|
---|
125 | const char *ccache_name,
|
---|
126 | uint32_t add_gss_c_flags,
|
---|
127 | struct gse_context **_gse_ctx)
|
---|
128 | {
|
---|
129 | struct gse_context *gse_ctx;
|
---|
130 | krb5_error_code k5ret;
|
---|
131 | NTSTATUS status;
|
---|
132 |
|
---|
133 | gse_ctx = talloc_zero(mem_ctx, struct gse_context);
|
---|
134 | if (!gse_ctx) {
|
---|
135 | return NT_STATUS_NO_MEMORY;
|
---|
136 | }
|
---|
137 | talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
|
---|
138 |
|
---|
139 | gse_ctx->expire_time = GENSEC_EXPIRE_TIME_INFINITY;
|
---|
140 | gse_ctx->max_wrap_buf_size = UINT16_MAX;
|
---|
141 |
|
---|
142 | memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
|
---|
143 |
|
---|
144 | gse_ctx->gss_want_flags = GSS_C_MUTUAL_FLAG |
|
---|
145 | GSS_C_DELEG_FLAG |
|
---|
146 | GSS_C_DELEG_POLICY_FLAG |
|
---|
147 | GSS_C_REPLAY_FLAG |
|
---|
148 | GSS_C_SEQUENCE_FLAG;
|
---|
149 | if (do_sign) {
|
---|
150 | gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
|
---|
151 | }
|
---|
152 | if (do_seal) {
|
---|
153 | gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
|
---|
154 | gse_ctx->gss_want_flags |= GSS_C_CONF_FLAG;
|
---|
155 | }
|
---|
156 |
|
---|
157 | gse_ctx->gss_want_flags |= add_gss_c_flags;
|
---|
158 |
|
---|
159 | /* Initialize Kerberos Context */
|
---|
160 | initialize_krb5_error_table();
|
---|
161 |
|
---|
162 | k5ret = krb5_init_context(&gse_ctx->k5ctx);
|
---|
163 | if (k5ret) {
|
---|
164 | DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
|
---|
165 | error_message(k5ret)));
|
---|
166 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
167 | goto err_out;
|
---|
168 | }
|
---|
169 |
|
---|
170 | if (!ccache_name) {
|
---|
171 | ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
|
---|
172 | }
|
---|
173 | k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
|
---|
174 | &gse_ctx->ccache);
|
---|
175 | if (k5ret) {
|
---|
176 | DEBUG(1, ("Failed to resolve credential cache! (%s)\n",
|
---|
177 | error_message(k5ret)));
|
---|
178 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
179 | goto err_out;
|
---|
180 | }
|
---|
181 |
|
---|
182 | /* TODO: Should we enforce a enc_types list ?
|
---|
183 | ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
|
---|
184 | */
|
---|
185 |
|
---|
186 | *_gse_ctx = gse_ctx;
|
---|
187 | return NT_STATUS_OK;
|
---|
188 |
|
---|
189 | err_out:
|
---|
190 | TALLOC_FREE(gse_ctx);
|
---|
191 | return status;
|
---|
192 | }
|
---|
193 |
|
---|
194 | static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
|
---|
195 | bool do_sign, bool do_seal,
|
---|
196 | const char *ccache_name,
|
---|
197 | const char *server,
|
---|
198 | const char *service,
|
---|
199 | const char *username,
|
---|
200 | const char *password,
|
---|
201 | uint32_t add_gss_c_flags,
|
---|
202 | struct gse_context **_gse_ctx)
|
---|
203 | {
|
---|
204 | struct gse_context *gse_ctx;
|
---|
205 | OM_uint32 gss_maj, gss_min;
|
---|
206 | gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
|
---|
207 | gss_OID_set_desc mech_set;
|
---|
208 | #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
|
---|
209 | gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
|
---|
210 | #endif
|
---|
211 | NTSTATUS status;
|
---|
212 |
|
---|
213 | if (!server || !service) {
|
---|
214 | return NT_STATUS_INVALID_PARAMETER;
|
---|
215 | }
|
---|
216 |
|
---|
217 | status = gse_context_init(mem_ctx, do_sign, do_seal,
|
---|
218 | ccache_name, add_gss_c_flags,
|
---|
219 | &gse_ctx);
|
---|
220 | if (!NT_STATUS_IS_OK(status)) {
|
---|
221 | return NT_STATUS_NO_MEMORY;
|
---|
222 | }
|
---|
223 |
|
---|
224 | /* Guess the realm based on the supplied service, and avoid the GSS libs
|
---|
225 | doing DNS lookups which may fail.
|
---|
226 |
|
---|
227 | TODO: Loop with the KDC on some more combinations (local
|
---|
228 | realm in particular), possibly falling back to
|
---|
229 | GSS_C_NT_HOSTBASED_SERVICE
|
---|
230 | */
|
---|
231 | name_buffer.value = kerberos_get_principal_from_service_hostname(
|
---|
232 | gse_ctx, service, server, lp_realm());
|
---|
233 | if (!name_buffer.value) {
|
---|
234 | status = NT_STATUS_NO_MEMORY;
|
---|
235 | goto err_out;
|
---|
236 | }
|
---|
237 | name_buffer.length = strlen((char *)name_buffer.value);
|
---|
238 | gss_maj = gss_import_name(&gss_min, &name_buffer,
|
---|
239 | GSS_C_NT_USER_NAME,
|
---|
240 | &gse_ctx->server_name);
|
---|
241 | if (gss_maj) {
|
---|
242 | DEBUG(5, ("gss_import_name failed for %s, with [%s]\n",
|
---|
243 | (char *)name_buffer.value,
|
---|
244 | gse_errstr(gse_ctx, gss_maj, gss_min)));
|
---|
245 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
246 | goto err_out;
|
---|
247 | }
|
---|
248 |
|
---|
249 | /* TODO: get krb5 ticket using username/password, if no valid
|
---|
250 | * one already available in ccache */
|
---|
251 |
|
---|
252 | mech_set.count = 1;
|
---|
253 | mech_set.elements = &gse_ctx->gss_mech;
|
---|
254 |
|
---|
255 | gss_maj = gss_acquire_cred(&gss_min,
|
---|
256 | GSS_C_NO_NAME,
|
---|
257 | GSS_C_INDEFINITE,
|
---|
258 | &mech_set,
|
---|
259 | GSS_C_INITIATE,
|
---|
260 | &gse_ctx->creds,
|
---|
261 | NULL, NULL);
|
---|
262 | if (gss_maj) {
|
---|
263 | DEBUG(5, ("gss_acquire_creds failed for GSS_C_NO_NAME with [%s] -"
|
---|
264 | "the caller may retry after a kinit.\n",
|
---|
265 | gse_errstr(gse_ctx, gss_maj, gss_min)));
|
---|
266 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
267 | goto err_out;
|
---|
268 | }
|
---|
269 |
|
---|
270 | #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
|
---|
271 | /*
|
---|
272 | * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
|
---|
273 | *
|
---|
274 | * This allows us to disable SIGN and SEAL for
|
---|
275 | * AUTH_LEVEL_CONNECT and AUTH_LEVEL_INTEGRITY.
|
---|
276 | *
|
---|
277 | * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
|
---|
278 | * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
|
---|
279 | */
|
---|
280 | gss_maj = gss_set_cred_option(&gss_min, &gse_ctx->creds,
|
---|
281 | GSS_KRB5_CRED_NO_CI_FLAGS_X,
|
---|
282 | &empty_buffer);
|
---|
283 | if (gss_maj) {
|
---|
284 | DEBUG(0, ("gss_set_cred_option(GSS_KRB5_CRED_NO_CI_FLAGS_X), "
|
---|
285 | "failed with [%s]\n",
|
---|
286 | gse_errstr(gse_ctx, gss_maj, gss_min)));
|
---|
287 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
288 | goto err_out;
|
---|
289 | }
|
---|
290 | #endif
|
---|
291 |
|
---|
292 | *_gse_ctx = gse_ctx;
|
---|
293 | TALLOC_FREE(name_buffer.value);
|
---|
294 | return NT_STATUS_OK;
|
---|
295 |
|
---|
296 | err_out:
|
---|
297 | TALLOC_FREE(name_buffer.value);
|
---|
298 | TALLOC_FREE(gse_ctx);
|
---|
299 | return status;
|
---|
300 | }
|
---|
301 |
|
---|
302 | static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
|
---|
303 | struct gse_context *gse_ctx,
|
---|
304 | const DATA_BLOB *token_in,
|
---|
305 | DATA_BLOB *token_out)
|
---|
306 | {
|
---|
307 | OM_uint32 gss_maj, gss_min;
|
---|
308 | gss_buffer_desc in_data;
|
---|
309 | gss_buffer_desc out_data;
|
---|
310 | DATA_BLOB blob = data_blob_null;
|
---|
311 | NTSTATUS status;
|
---|
312 | OM_uint32 time_rec = 0;
|
---|
313 | struct timeval tv;
|
---|
314 |
|
---|
315 | in_data.value = token_in->data;
|
---|
316 | in_data.length = token_in->length;
|
---|
317 |
|
---|
318 | gss_maj = gss_init_sec_context(&gss_min,
|
---|
319 | gse_ctx->creds,
|
---|
320 | &gse_ctx->gssapi_context,
|
---|
321 | gse_ctx->server_name,
|
---|
322 | &gse_ctx->gss_mech,
|
---|
323 | gse_ctx->gss_want_flags,
|
---|
324 | 0, GSS_C_NO_CHANNEL_BINDINGS,
|
---|
325 | &in_data, NULL, &out_data,
|
---|
326 | &gse_ctx->gss_got_flags, &time_rec);
|
---|
327 | switch (gss_maj) {
|
---|
328 | case GSS_S_COMPLETE:
|
---|
329 | /* we are done with it */
|
---|
330 | tv = timeval_current_ofs(time_rec, 0);
|
---|
331 | gse_ctx->expire_time = timeval_to_nttime(&tv);
|
---|
332 |
|
---|
333 | status = NT_STATUS_OK;
|
---|
334 | break;
|
---|
335 | case GSS_S_CONTINUE_NEEDED:
|
---|
336 | /* we will need a third leg */
|
---|
337 | status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
---|
338 | break;
|
---|
339 | default:
|
---|
340 | DEBUG(0, ("gss_init_sec_context failed with [%s]\n",
|
---|
341 | gse_errstr(talloc_tos(), gss_maj, gss_min)));
|
---|
342 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
343 | goto done;
|
---|
344 | }
|
---|
345 |
|
---|
346 | /* we may be told to return nothing */
|
---|
347 | if (out_data.length) {
|
---|
348 | blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
|
---|
349 | if (!blob.data) {
|
---|
350 | status = NT_STATUS_NO_MEMORY;
|
---|
351 | }
|
---|
352 |
|
---|
353 | gss_maj = gss_release_buffer(&gss_min, &out_data);
|
---|
354 | }
|
---|
355 |
|
---|
356 | done:
|
---|
357 | *token_out = blob;
|
---|
358 | return status;
|
---|
359 | }
|
---|
360 |
|
---|
361 | static NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
|
---|
362 | bool do_sign, bool do_seal,
|
---|
363 | uint32_t add_gss_c_flags,
|
---|
364 | struct gse_context **_gse_ctx)
|
---|
365 | {
|
---|
366 | struct gse_context *gse_ctx;
|
---|
367 | OM_uint32 gss_maj, gss_min;
|
---|
368 | krb5_error_code ret;
|
---|
369 | NTSTATUS status;
|
---|
370 |
|
---|
371 | status = gse_context_init(mem_ctx, do_sign, do_seal,
|
---|
372 | NULL, add_gss_c_flags, &gse_ctx);
|
---|
373 | if (!NT_STATUS_IS_OK(status)) {
|
---|
374 | return NT_STATUS_NO_MEMORY;
|
---|
375 | }
|
---|
376 |
|
---|
377 | ret = gse_krb5_get_server_keytab(gse_ctx->k5ctx,
|
---|
378 | &gse_ctx->keytab);
|
---|
379 | if (ret) {
|
---|
380 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
381 | goto done;
|
---|
382 | }
|
---|
383 |
|
---|
384 | #ifdef HAVE_GSS_KRB5_IMPORT_CRED
|
---|
385 |
|
---|
386 | /* This creates a GSSAPI cred_id_t with the keytab set */
|
---|
387 | gss_maj = gss_krb5_import_cred(&gss_min, NULL, NULL, gse_ctx->keytab,
|
---|
388 | &gse_ctx->creds);
|
---|
389 |
|
---|
390 | if (gss_maj != 0
|
---|
391 | && gss_maj != (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME)) {
|
---|
392 | DEBUG(0, ("gss_krb5_import_cred failed with [%s]\n",
|
---|
393 | gse_errstr(gse_ctx, gss_maj, gss_min)));
|
---|
394 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
395 | goto done;
|
---|
396 |
|
---|
397 | /* This is the error the MIT krb5 1.9 gives when it
|
---|
398 | * implements the function, but we do not specify the
|
---|
399 | * principal. However, when we specify the principal
|
---|
400 | * as host$@REALM the GSS acceptor fails with 'wrong
|
---|
401 | * principal in request'. Work around the issue by
|
---|
402 | * falling back to the alternate approach below. */
|
---|
403 | } else if (gss_maj == (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME))
|
---|
404 | #endif
|
---|
405 | /* FIXME!!!
|
---|
406 | * This call sets the default keytab for the whole server, not
|
---|
407 | * just for this context. Need to find a way that does not alter
|
---|
408 | * the state of the whole server ... */
|
---|
409 | {
|
---|
410 | const char *ktname;
|
---|
411 | gss_OID_set_desc mech_set;
|
---|
412 |
|
---|
413 | ret = smb_krb5_keytab_name(gse_ctx, gse_ctx->k5ctx,
|
---|
414 | gse_ctx->keytab, &ktname);
|
---|
415 | if (ret) {
|
---|
416 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
417 | goto done;
|
---|
418 | }
|
---|
419 |
|
---|
420 | ret = gsskrb5_register_acceptor_identity(ktname);
|
---|
421 | if (ret) {
|
---|
422 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
423 | goto done;
|
---|
424 | }
|
---|
425 |
|
---|
426 | mech_set.count = 1;
|
---|
427 | mech_set.elements = &gse_ctx->gss_mech;
|
---|
428 |
|
---|
429 | gss_maj = gss_acquire_cred(&gss_min,
|
---|
430 | GSS_C_NO_NAME,
|
---|
431 | GSS_C_INDEFINITE,
|
---|
432 | &mech_set,
|
---|
433 | GSS_C_ACCEPT,
|
---|
434 | &gse_ctx->creds,
|
---|
435 | NULL, NULL);
|
---|
436 |
|
---|
437 | if (gss_maj) {
|
---|
438 | DEBUG(0, ("gss_acquire_creds failed with [%s]\n",
|
---|
439 | gse_errstr(gse_ctx, gss_maj, gss_min)));
|
---|
440 | status = NT_STATUS_INTERNAL_ERROR;
|
---|
441 | goto done;
|
---|
442 | }
|
---|
443 | }
|
---|
444 |
|
---|
445 | status = NT_STATUS_OK;
|
---|
446 |
|
---|
447 | done:
|
---|
448 | if (!NT_STATUS_IS_OK(status)) {
|
---|
449 | TALLOC_FREE(gse_ctx);
|
---|
450 | }
|
---|
451 |
|
---|
452 | *_gse_ctx = gse_ctx;
|
---|
453 | return status;
|
---|
454 | }
|
---|
455 |
|
---|
456 | static NTSTATUS gse_get_server_auth_token(TALLOC_CTX *mem_ctx,
|
---|
457 | struct gse_context *gse_ctx,
|
---|
458 | const DATA_BLOB *token_in,
|
---|
459 | DATA_BLOB *token_out)
|
---|
460 | {
|
---|
461 | OM_uint32 gss_maj, gss_min;
|
---|
462 | gss_buffer_desc in_data;
|
---|
463 | gss_buffer_desc out_data;
|
---|
464 | DATA_BLOB blob = data_blob_null;
|
---|
465 | NTSTATUS status;
|
---|
466 | OM_uint32 time_rec = 0;
|
---|
467 | struct timeval tv;
|
---|
468 |
|
---|
469 | in_data.value = token_in->data;
|
---|
470 | in_data.length = token_in->length;
|
---|
471 |
|
---|
472 | gss_maj = gss_accept_sec_context(&gss_min,
|
---|
473 | &gse_ctx->gssapi_context,
|
---|
474 | gse_ctx->creds,
|
---|
475 | &in_data,
|
---|
476 | GSS_C_NO_CHANNEL_BINDINGS,
|
---|
477 | &gse_ctx->client_name,
|
---|
478 | &gse_ctx->ret_mech,
|
---|
479 | &out_data,
|
---|
480 | &gse_ctx->gss_got_flags,
|
---|
481 | &time_rec,
|
---|
482 | &gse_ctx->delegated_cred_handle);
|
---|
483 | switch (gss_maj) {
|
---|
484 | case GSS_S_COMPLETE:
|
---|
485 | /* we are done with it */
|
---|
486 | tv = timeval_current_ofs(time_rec, 0);
|
---|
487 | gse_ctx->expire_time = timeval_to_nttime(&tv);
|
---|
488 |
|
---|
489 | status = NT_STATUS_OK;
|
---|
490 | break;
|
---|
491 | case GSS_S_CONTINUE_NEEDED:
|
---|
492 | /* we will need a third leg */
|
---|
493 | status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
---|
494 | break;
|
---|
495 | default:
|
---|
496 | DEBUG(1, ("gss_accept_sec_context failed with [%s]\n",
|
---|
497 | gse_errstr(talloc_tos(), gss_maj, gss_min)));
|
---|
498 |
|
---|
499 | if (gse_ctx->gssapi_context) {
|
---|
500 | gss_delete_sec_context(&gss_min,
|
---|
501 | &gse_ctx->gssapi_context,
|
---|
502 | GSS_C_NO_BUFFER);
|
---|
503 | }
|
---|
504 |
|
---|
505 | /*
|
---|
506 | * If we got an output token, make Windows aware of it
|
---|
507 | * by telling it that more processing is needed
|
---|
508 | */
|
---|
509 | if (out_data.length > 0) {
|
---|
510 | status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
---|
511 | /* Fall through to handle the out token */
|
---|
512 | } else {
|
---|
513 | status = NT_STATUS_LOGON_FAILURE;
|
---|
514 | goto done;
|
---|
515 | }
|
---|
516 | }
|
---|
517 |
|
---|
518 | /* we may be told to return nothing */
|
---|
519 | if (out_data.length) {
|
---|
520 | blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
|
---|
521 | if (!blob.data) {
|
---|
522 | status = NT_STATUS_NO_MEMORY;
|
---|
523 | }
|
---|
524 | gss_maj = gss_release_buffer(&gss_min, &out_data);
|
---|
525 | }
|
---|
526 |
|
---|
527 |
|
---|
528 | done:
|
---|
529 | *token_out = blob;
|
---|
530 | return status;
|
---|
531 | }
|
---|
532 |
|
---|
533 | static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
|
---|
534 | {
|
---|
535 | OM_uint32 gss_min, gss_maj;
|
---|
536 | gss_buffer_desc msg_min;
|
---|
537 | gss_buffer_desc msg_maj;
|
---|
538 | OM_uint32 msg_ctx = 0;
|
---|
539 |
|
---|
540 | char *errstr = NULL;
|
---|
541 |
|
---|
542 | ZERO_STRUCT(msg_min);
|
---|
543 | ZERO_STRUCT(msg_maj);
|
---|
544 |
|
---|
545 | gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
|
---|
546 | GSS_C_NO_OID, &msg_ctx, &msg_maj);
|
---|
547 | if (gss_maj) {
|
---|
548 | goto done;
|
---|
549 | }
|
---|
550 | errstr = talloc_strndup(mem_ctx,
|
---|
551 | (char *)msg_maj.value,
|
---|
552 | msg_maj.length);
|
---|
553 | if (!errstr) {
|
---|
554 | goto done;
|
---|
555 | }
|
---|
556 | gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
|
---|
557 | (gss_OID)discard_const(gss_mech_krb5),
|
---|
558 | &msg_ctx, &msg_min);
|
---|
559 | if (gss_maj) {
|
---|
560 | goto done;
|
---|
561 | }
|
---|
562 |
|
---|
563 | errstr = talloc_strdup_append_buffer(errstr, ": ");
|
---|
564 | if (!errstr) {
|
---|
565 | goto done;
|
---|
566 | }
|
---|
567 | errstr = talloc_strndup_append_buffer(errstr,
|
---|
568 | (char *)msg_min.value,
|
---|
569 | msg_min.length);
|
---|
570 | if (!errstr) {
|
---|
571 | goto done;
|
---|
572 | }
|
---|
573 |
|
---|
574 | done:
|
---|
575 | if (msg_min.value) {
|
---|
576 | gss_maj = gss_release_buffer(&gss_min, &msg_min);
|
---|
577 | }
|
---|
578 | if (msg_maj.value) {
|
---|
579 | gss_maj = gss_release_buffer(&gss_min, &msg_maj);
|
---|
580 | }
|
---|
581 | return errstr;
|
---|
582 | }
|
---|
583 |
|
---|
584 | static NTSTATUS gensec_gse_client_start(struct gensec_security *gensec_security)
|
---|
585 | {
|
---|
586 | struct gse_context *gse_ctx;
|
---|
587 | struct cli_credentials *creds = gensec_get_credentials(gensec_security);
|
---|
588 | NTSTATUS nt_status;
|
---|
589 | OM_uint32 want_flags = 0;
|
---|
590 | bool do_sign = false, do_seal = false;
|
---|
591 | const char *hostname = gensec_get_target_hostname(gensec_security);
|
---|
592 | const char *service = gensec_get_target_service(gensec_security);
|
---|
593 | const char *username = cli_credentials_get_username(creds);
|
---|
594 | const char *password = cli_credentials_get_password(creds);
|
---|
595 |
|
---|
596 | if (!hostname) {
|
---|
597 | DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
|
---|
598 | return NT_STATUS_INVALID_PARAMETER;
|
---|
599 | }
|
---|
600 | if (is_ipaddress(hostname)) {
|
---|
601 | DEBUG(2, ("Cannot do GSE to an IP address\n"));
|
---|
602 | return NT_STATUS_INVALID_PARAMETER;
|
---|
603 | }
|
---|
604 | if (strcmp(hostname, "localhost") == 0) {
|
---|
605 | DEBUG(2, ("GSE to 'localhost' does not make sense\n"));
|
---|
606 | return NT_STATUS_INVALID_PARAMETER;
|
---|
607 | }
|
---|
608 |
|
---|
609 | if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
|
---|
610 | do_sign = true;
|
---|
611 | }
|
---|
612 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
---|
613 | do_sign = true;
|
---|
614 | }
|
---|
615 | if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
---|
616 | do_seal = true;
|
---|
617 | }
|
---|
618 | if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
|
---|
619 | want_flags |= GSS_C_DCE_STYLE;
|
---|
620 | }
|
---|
621 |
|
---|
622 | nt_status = gse_init_client(gensec_security, do_sign, do_seal, NULL,
|
---|
623 | hostname, service,
|
---|
624 | username, password, want_flags,
|
---|
625 | &gse_ctx);
|
---|
626 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
627 | return nt_status;
|
---|
628 | }
|
---|
629 | gensec_security->private_data = gse_ctx;
|
---|
630 | return NT_STATUS_OK;
|
---|
631 | }
|
---|
632 |
|
---|
633 | static NTSTATUS gensec_gse_server_start(struct gensec_security *gensec_security)
|
---|
634 | {
|
---|
635 | struct gse_context *gse_ctx;
|
---|
636 | NTSTATUS nt_status;
|
---|
637 | OM_uint32 want_flags = 0;
|
---|
638 | bool do_sign = false, do_seal = false;
|
---|
639 |
|
---|
640 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
---|
641 | do_sign = true;
|
---|
642 | }
|
---|
643 | if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
---|
644 | do_seal = true;
|
---|
645 | }
|
---|
646 | if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
|
---|
647 | want_flags |= GSS_C_DCE_STYLE;
|
---|
648 | }
|
---|
649 |
|
---|
650 | nt_status = gse_init_server(gensec_security, do_sign, do_seal, want_flags,
|
---|
651 | &gse_ctx);
|
---|
652 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
653 | return nt_status;
|
---|
654 | }
|
---|
655 | gensec_security->private_data = gse_ctx;
|
---|
656 | return NT_STATUS_OK;
|
---|
657 | }
|
---|
658 |
|
---|
659 | /**
|
---|
660 | * Next state function for the GSE GENSEC mechanism
|
---|
661 | *
|
---|
662 | * @param gensec_gse_state GSE State
|
---|
663 | * @param mem_ctx The TALLOC_CTX for *out to be allocated on
|
---|
664 | * @param in The request, as a DATA_BLOB
|
---|
665 | * @param out The reply, as an talloc()ed DATA_BLOB, on *mem_ctx
|
---|
666 | * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
|
---|
667 | * or NT_STATUS_OK if the user is authenticated.
|
---|
668 | */
|
---|
669 |
|
---|
670 | static NTSTATUS gensec_gse_update(struct gensec_security *gensec_security,
|
---|
671 | TALLOC_CTX *mem_ctx,
|
---|
672 | struct tevent_context *ev,
|
---|
673 | const DATA_BLOB in, DATA_BLOB *out)
|
---|
674 | {
|
---|
675 | NTSTATUS status;
|
---|
676 | struct gse_context *gse_ctx =
|
---|
677 | talloc_get_type_abort(gensec_security->private_data,
|
---|
678 | struct gse_context);
|
---|
679 |
|
---|
680 | switch (gensec_security->gensec_role) {
|
---|
681 | case GENSEC_CLIENT:
|
---|
682 | status = gse_get_client_auth_token(mem_ctx, gse_ctx,
|
---|
683 | &in, out);
|
---|
684 | break;
|
---|
685 | case GENSEC_SERVER:
|
---|
686 | status = gse_get_server_auth_token(mem_ctx, gse_ctx,
|
---|
687 | &in, out);
|
---|
688 | break;
|
---|
689 | }
|
---|
690 | if (!NT_STATUS_IS_OK(status)) {
|
---|
691 | return status;
|
---|
692 | }
|
---|
693 |
|
---|
694 | return NT_STATUS_OK;
|
---|
695 | }
|
---|
696 |
|
---|
697 | static NTSTATUS gensec_gse_wrap(struct gensec_security *gensec_security,
|
---|
698 | TALLOC_CTX *mem_ctx,
|
---|
699 | const DATA_BLOB *in,
|
---|
700 | DATA_BLOB *out)
|
---|
701 | {
|
---|
702 | struct gse_context *gse_ctx =
|
---|
703 | talloc_get_type_abort(gensec_security->private_data,
|
---|
704 | struct gse_context);
|
---|
705 | OM_uint32 maj_stat, min_stat;
|
---|
706 | gss_buffer_desc input_token, output_token;
|
---|
707 | int conf_state;
|
---|
708 | input_token.length = in->length;
|
---|
709 | input_token.value = in->data;
|
---|
710 |
|
---|
711 | maj_stat = gss_wrap(&min_stat,
|
---|
712 | gse_ctx->gssapi_context,
|
---|
713 | gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
|
---|
714 | GSS_C_QOP_DEFAULT,
|
---|
715 | &input_token,
|
---|
716 | &conf_state,
|
---|
717 | &output_token);
|
---|
718 | if (GSS_ERROR(maj_stat)) {
|
---|
719 | DEBUG(0, ("gensec_gse_wrap: GSS Wrap failed: %s\n",
|
---|
720 | gse_errstr(talloc_tos(), maj_stat, min_stat)));
|
---|
721 | return NT_STATUS_ACCESS_DENIED;
|
---|
722 | }
|
---|
723 |
|
---|
724 | *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
|
---|
725 | gss_release_buffer(&min_stat, &output_token);
|
---|
726 |
|
---|
727 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
|
---|
728 | && !conf_state) {
|
---|
729 | return NT_STATUS_ACCESS_DENIED;
|
---|
730 | }
|
---|
731 | return NT_STATUS_OK;
|
---|
732 | }
|
---|
733 |
|
---|
734 | static NTSTATUS gensec_gse_unwrap(struct gensec_security *gensec_security,
|
---|
735 | TALLOC_CTX *mem_ctx,
|
---|
736 | const DATA_BLOB *in,
|
---|
737 | DATA_BLOB *out)
|
---|
738 | {
|
---|
739 | struct gse_context *gse_ctx =
|
---|
740 | talloc_get_type_abort(gensec_security->private_data,
|
---|
741 | struct gse_context);
|
---|
742 | OM_uint32 maj_stat, min_stat;
|
---|
743 | gss_buffer_desc input_token, output_token;
|
---|
744 | int conf_state;
|
---|
745 | gss_qop_t qop_state;
|
---|
746 | input_token.length = in->length;
|
---|
747 | input_token.value = in->data;
|
---|
748 |
|
---|
749 | maj_stat = gss_unwrap(&min_stat,
|
---|
750 | gse_ctx->gssapi_context,
|
---|
751 | &input_token,
|
---|
752 | &output_token,
|
---|
753 | &conf_state,
|
---|
754 | &qop_state);
|
---|
755 | if (GSS_ERROR(maj_stat)) {
|
---|
756 | DEBUG(0, ("gensec_gse_unwrap: GSS UnWrap failed: %s\n",
|
---|
757 | gse_errstr(talloc_tos(), maj_stat, min_stat)));
|
---|
758 | return NT_STATUS_ACCESS_DENIED;
|
---|
759 | }
|
---|
760 |
|
---|
761 | *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
|
---|
762 | gss_release_buffer(&min_stat, &output_token);
|
---|
763 |
|
---|
764 | if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
|
---|
765 | && !conf_state) {
|
---|
766 | return NT_STATUS_ACCESS_DENIED;
|
---|
767 | }
|
---|
768 | return NT_STATUS_OK;
|
---|
769 | }
|
---|
770 |
|
---|
771 | static NTSTATUS gensec_gse_seal_packet(struct gensec_security *gensec_security,
|
---|
772 | TALLOC_CTX *mem_ctx,
|
---|
773 | uint8_t *data, size_t length,
|
---|
774 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
775 | DATA_BLOB *sig)
|
---|
776 | {
|
---|
777 | struct gse_context *gse_ctx =
|
---|
778 | talloc_get_type_abort(gensec_security->private_data,
|
---|
779 | struct gse_context);
|
---|
780 | bool hdr_signing = false;
|
---|
781 | size_t sig_size = 0;
|
---|
782 | NTSTATUS status;
|
---|
783 |
|
---|
784 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
785 | hdr_signing = true;
|
---|
786 | }
|
---|
787 |
|
---|
788 | sig_size = gensec_gse_sig_size(gensec_security, length);
|
---|
789 |
|
---|
790 | status = gssapi_seal_packet(gse_ctx->gssapi_context,
|
---|
791 | &gse_ctx->gss_mech,
|
---|
792 | hdr_signing, sig_size,
|
---|
793 | data, length,
|
---|
794 | whole_pdu, pdu_length,
|
---|
795 | mem_ctx, sig);
|
---|
796 | if (!NT_STATUS_IS_OK(status)) {
|
---|
797 | DEBUG(0, ("gssapi_seal_packet(hdr_signing=%u,sig_size=%zu,"
|
---|
798 | "data=%zu,pdu=%zu) failed: %s\n",
|
---|
799 | hdr_signing, sig_size, length, pdu_length,
|
---|
800 | nt_errstr(status)));
|
---|
801 | return status;
|
---|
802 | }
|
---|
803 |
|
---|
804 | return NT_STATUS_OK;
|
---|
805 | }
|
---|
806 |
|
---|
807 | static NTSTATUS gensec_gse_unseal_packet(struct gensec_security *gensec_security,
|
---|
808 | uint8_t *data, size_t length,
|
---|
809 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
810 | const DATA_BLOB *sig)
|
---|
811 | {
|
---|
812 | struct gse_context *gse_ctx =
|
---|
813 | talloc_get_type_abort(gensec_security->private_data,
|
---|
814 | struct gse_context);
|
---|
815 | bool hdr_signing = false;
|
---|
816 | NTSTATUS status;
|
---|
817 |
|
---|
818 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
819 | hdr_signing = true;
|
---|
820 | }
|
---|
821 |
|
---|
822 | status = gssapi_unseal_packet(gse_ctx->gssapi_context,
|
---|
823 | &gse_ctx->gss_mech,
|
---|
824 | hdr_signing,
|
---|
825 | data, length,
|
---|
826 | whole_pdu, pdu_length,
|
---|
827 | sig);
|
---|
828 | if (!NT_STATUS_IS_OK(status)) {
|
---|
829 | DEBUG(0, ("gssapi_unseal_packet(hdr_signing=%u,sig_size=%zu,"
|
---|
830 | "data=%zu,pdu=%zu) failed: %s\n",
|
---|
831 | hdr_signing, sig->length, length, pdu_length,
|
---|
832 | nt_errstr(status)));
|
---|
833 | return status;
|
---|
834 | }
|
---|
835 |
|
---|
836 | return NT_STATUS_OK;
|
---|
837 | }
|
---|
838 |
|
---|
839 | static NTSTATUS gensec_gse_sign_packet(struct gensec_security *gensec_security,
|
---|
840 | TALLOC_CTX *mem_ctx,
|
---|
841 | const uint8_t *data, size_t length,
|
---|
842 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
843 | DATA_BLOB *sig)
|
---|
844 | {
|
---|
845 | struct gse_context *gse_ctx =
|
---|
846 | talloc_get_type_abort(gensec_security->private_data,
|
---|
847 | struct gse_context);
|
---|
848 | bool hdr_signing = false;
|
---|
849 | NTSTATUS status;
|
---|
850 |
|
---|
851 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
852 | hdr_signing = true;
|
---|
853 | }
|
---|
854 |
|
---|
855 | status = gssapi_sign_packet(gse_ctx->gssapi_context,
|
---|
856 | &gse_ctx->gss_mech,
|
---|
857 | hdr_signing,
|
---|
858 | data, length,
|
---|
859 | whole_pdu, pdu_length,
|
---|
860 | mem_ctx, sig);
|
---|
861 | if (!NT_STATUS_IS_OK(status)) {
|
---|
862 | DEBUG(0, ("gssapi_sign_packet(hdr_signing=%u,"
|
---|
863 | "data=%zu,pdu=%zu) failed: %s\n",
|
---|
864 | hdr_signing, length, pdu_length,
|
---|
865 | nt_errstr(status)));
|
---|
866 | return status;
|
---|
867 | }
|
---|
868 |
|
---|
869 | return NT_STATUS_OK;
|
---|
870 | }
|
---|
871 |
|
---|
872 | static NTSTATUS gensec_gse_check_packet(struct gensec_security *gensec_security,
|
---|
873 | const uint8_t *data, size_t length,
|
---|
874 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
875 | const DATA_BLOB *sig)
|
---|
876 | {
|
---|
877 | struct gse_context *gse_ctx =
|
---|
878 | talloc_get_type_abort(gensec_security->private_data,
|
---|
879 | struct gse_context);
|
---|
880 | bool hdr_signing = false;
|
---|
881 | NTSTATUS status;
|
---|
882 |
|
---|
883 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
884 | hdr_signing = true;
|
---|
885 | }
|
---|
886 |
|
---|
887 | status = gssapi_check_packet(gse_ctx->gssapi_context,
|
---|
888 | &gse_ctx->gss_mech,
|
---|
889 | hdr_signing,
|
---|
890 | data, length,
|
---|
891 | whole_pdu, pdu_length,
|
---|
892 | sig);
|
---|
893 | if (!NT_STATUS_IS_OK(status)) {
|
---|
894 | DEBUG(0, ("gssapi_check_packet(hdr_signing=%u,sig_size=%zu"
|
---|
895 | "data=%zu,pdu=%zu) failed: %s\n",
|
---|
896 | hdr_signing, sig->length, length, pdu_length,
|
---|
897 | nt_errstr(status)));
|
---|
898 | return status;
|
---|
899 | }
|
---|
900 |
|
---|
901 | return NT_STATUS_OK;
|
---|
902 | }
|
---|
903 |
|
---|
904 | /* Try to figure out what features we actually got on the connection */
|
---|
905 | static bool gensec_gse_have_feature(struct gensec_security *gensec_security,
|
---|
906 | uint32_t feature)
|
---|
907 | {
|
---|
908 | struct gse_context *gse_ctx =
|
---|
909 | talloc_get_type_abort(gensec_security->private_data,
|
---|
910 | struct gse_context);
|
---|
911 |
|
---|
912 | if (feature & GENSEC_FEATURE_SESSION_KEY) {
|
---|
913 | return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
|
---|
914 | }
|
---|
915 | if (feature & GENSEC_FEATURE_SIGN) {
|
---|
916 | return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
|
---|
917 | }
|
---|
918 | if (feature & GENSEC_FEATURE_SEAL) {
|
---|
919 | return gse_ctx->gss_got_flags & GSS_C_CONF_FLAG;
|
---|
920 | }
|
---|
921 | if (feature & GENSEC_FEATURE_DCE_STYLE) {
|
---|
922 | return gse_ctx->gss_got_flags & GSS_C_DCE_STYLE;
|
---|
923 | }
|
---|
924 | if (feature & GENSEC_FEATURE_NEW_SPNEGO) {
|
---|
925 | NTSTATUS status;
|
---|
926 | uint32_t keytype;
|
---|
927 |
|
---|
928 | if (!(gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG)) {
|
---|
929 | return false;
|
---|
930 | }
|
---|
931 |
|
---|
932 | status = gssapi_get_session_key(talloc_tos(),
|
---|
933 | gse_ctx->gssapi_context, NULL, &keytype);
|
---|
934 | /*
|
---|
935 | * We should do a proper sig on the mechListMic unless
|
---|
936 | * we know we have to be backwards compatible with
|
---|
937 | * earlier windows versions.
|
---|
938 | *
|
---|
939 | * Negotiating a non-krb5
|
---|
940 | * mech for example should be regarded as having
|
---|
941 | * NEW_SPNEGO
|
---|
942 | */
|
---|
943 | if (NT_STATUS_IS_OK(status)) {
|
---|
944 | switch (keytype) {
|
---|
945 | case ENCTYPE_DES_CBC_CRC:
|
---|
946 | case ENCTYPE_DES_CBC_MD5:
|
---|
947 | case ENCTYPE_ARCFOUR_HMAC:
|
---|
948 | case ENCTYPE_DES3_CBC_SHA1:
|
---|
949 | return false;
|
---|
950 | }
|
---|
951 | }
|
---|
952 | return true;
|
---|
953 | }
|
---|
954 | /* We can always do async (rather than strict request/reply) packets. */
|
---|
955 | if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
|
---|
956 | return true;
|
---|
957 | }
|
---|
958 | if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
959 | if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
---|
960 | return true;
|
---|
961 | }
|
---|
962 |
|
---|
963 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
---|
964 | return true;
|
---|
965 | }
|
---|
966 |
|
---|
967 | return false;
|
---|
968 | }
|
---|
969 | return false;
|
---|
970 | }
|
---|
971 |
|
---|
972 | static NTTIME gensec_gse_expire_time(struct gensec_security *gensec_security)
|
---|
973 | {
|
---|
974 | struct gse_context *gse_ctx =
|
---|
975 | talloc_get_type_abort(gensec_security->private_data,
|
---|
976 | struct gse_context);
|
---|
977 |
|
---|
978 | return gse_ctx->expire_time;
|
---|
979 | }
|
---|
980 |
|
---|
981 | /*
|
---|
982 | * Extract the 'sesssion key' needed by SMB signing and ncacn_np
|
---|
983 | * (for encrypting some passwords).
|
---|
984 | *
|
---|
985 | * This breaks all the abstractions, but what do you expect...
|
---|
986 | */
|
---|
987 | static NTSTATUS gensec_gse_session_key(struct gensec_security *gensec_security,
|
---|
988 | TALLOC_CTX *mem_ctx,
|
---|
989 | DATA_BLOB *session_key)
|
---|
990 | {
|
---|
991 | struct gse_context *gse_ctx =
|
---|
992 | talloc_get_type_abort(gensec_security->private_data,
|
---|
993 | struct gse_context);
|
---|
994 |
|
---|
995 | return gssapi_get_session_key(mem_ctx, gse_ctx->gssapi_context, session_key, NULL);
|
---|
996 | }
|
---|
997 |
|
---|
998 | /* Get some basic (and authorization) information about the user on
|
---|
999 | * this session. This uses either the PAC (if present) or a local
|
---|
1000 | * database lookup */
|
---|
1001 | static NTSTATUS gensec_gse_session_info(struct gensec_security *gensec_security,
|
---|
1002 | TALLOC_CTX *mem_ctx,
|
---|
1003 | struct auth_session_info **_session_info)
|
---|
1004 | {
|
---|
1005 | struct gse_context *gse_ctx =
|
---|
1006 | talloc_get_type_abort(gensec_security->private_data,
|
---|
1007 | struct gse_context);
|
---|
1008 | NTSTATUS nt_status;
|
---|
1009 | TALLOC_CTX *tmp_ctx;
|
---|
1010 | struct auth_session_info *session_info = NULL;
|
---|
1011 | OM_uint32 maj_stat, min_stat;
|
---|
1012 | DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
|
---|
1013 |
|
---|
1014 | gss_buffer_desc name_token;
|
---|
1015 | char *principal_string;
|
---|
1016 |
|
---|
1017 | tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gse_session_info context");
|
---|
1018 | NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
|
---|
1019 |
|
---|
1020 | maj_stat = gss_display_name(&min_stat,
|
---|
1021 | gse_ctx->client_name,
|
---|
1022 | &name_token,
|
---|
1023 | NULL);
|
---|
1024 | if (GSS_ERROR(maj_stat)) {
|
---|
1025 | DEBUG(1, ("GSS display_name failed: %s\n",
|
---|
1026 | gse_errstr(talloc_tos(), maj_stat, min_stat)));
|
---|
1027 | talloc_free(tmp_ctx);
|
---|
1028 | return NT_STATUS_FOOBAR;
|
---|
1029 | }
|
---|
1030 |
|
---|
1031 | principal_string = talloc_strndup(tmp_ctx,
|
---|
1032 | (const char *)name_token.value,
|
---|
1033 | name_token.length);
|
---|
1034 |
|
---|
1035 | gss_release_buffer(&min_stat, &name_token);
|
---|
1036 |
|
---|
1037 | if (!principal_string) {
|
---|
1038 | talloc_free(tmp_ctx);
|
---|
1039 | return NT_STATUS_NO_MEMORY;
|
---|
1040 | }
|
---|
1041 |
|
---|
1042 | nt_status = gssapi_obtain_pac_blob(tmp_ctx, gse_ctx->gssapi_context,
|
---|
1043 | gse_ctx->client_name,
|
---|
1044 | &pac_blob);
|
---|
1045 |
|
---|
1046 | /* IF we have the PAC - otherwise we need to get this
|
---|
1047 | * data from elsewere
|
---|
1048 | */
|
---|
1049 | if (NT_STATUS_IS_OK(nt_status)) {
|
---|
1050 | pac_blob_ptr = &pac_blob;
|
---|
1051 | }
|
---|
1052 | nt_status = gensec_generate_session_info_pac(tmp_ctx,
|
---|
1053 | gensec_security,
|
---|
1054 | NULL,
|
---|
1055 | pac_blob_ptr, principal_string,
|
---|
1056 | gensec_get_remote_address(gensec_security),
|
---|
1057 | &session_info);
|
---|
1058 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
1059 | talloc_free(tmp_ctx);
|
---|
1060 | return nt_status;
|
---|
1061 | }
|
---|
1062 |
|
---|
1063 | nt_status = gensec_gse_session_key(gensec_security, session_info,
|
---|
1064 | &session_info->session_key);
|
---|
1065 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
1066 | talloc_free(tmp_ctx);
|
---|
1067 | return nt_status;
|
---|
1068 | }
|
---|
1069 |
|
---|
1070 | *_session_info = talloc_move(mem_ctx, &session_info);
|
---|
1071 | talloc_free(tmp_ctx);
|
---|
1072 |
|
---|
1073 | return NT_STATUS_OK;
|
---|
1074 | }
|
---|
1075 |
|
---|
1076 | static size_t gensec_gse_max_input_size(struct gensec_security *gensec_security)
|
---|
1077 | {
|
---|
1078 | struct gse_context *gse_ctx =
|
---|
1079 | talloc_get_type_abort(gensec_security->private_data,
|
---|
1080 | struct gse_context);
|
---|
1081 | OM_uint32 maj_stat, min_stat;
|
---|
1082 | OM_uint32 max_input_size;
|
---|
1083 |
|
---|
1084 | maj_stat = gss_wrap_size_limit(&min_stat,
|
---|
1085 | gse_ctx->gssapi_context,
|
---|
1086 | gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
|
---|
1087 | GSS_C_QOP_DEFAULT,
|
---|
1088 | gse_ctx->max_wrap_buf_size,
|
---|
1089 | &max_input_size);
|
---|
1090 | if (GSS_ERROR(maj_stat)) {
|
---|
1091 | TALLOC_CTX *mem_ctx = talloc_new(NULL);
|
---|
1092 | DEBUG(1, ("gensec_gssapi_max_input_size: determining signature size with gss_wrap_size_limit failed: %s\n",
|
---|
1093 | gse_errstr(mem_ctx, maj_stat, min_stat)));
|
---|
1094 | talloc_free(mem_ctx);
|
---|
1095 | return 0;
|
---|
1096 | }
|
---|
1097 |
|
---|
1098 | return max_input_size;
|
---|
1099 | }
|
---|
1100 |
|
---|
1101 | /* Find out the maximum output size negotiated on this connection */
|
---|
1102 | static size_t gensec_gse_max_wrapped_size(struct gensec_security *gensec_security)
|
---|
1103 | {
|
---|
1104 | struct gse_context *gse_ctx =
|
---|
1105 | talloc_get_type_abort(gensec_security->private_data,
|
---|
1106 | struct gse_context);
|
---|
1107 | return gse_ctx->max_wrap_buf_size;
|
---|
1108 | }
|
---|
1109 |
|
---|
1110 | static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
|
---|
1111 | size_t data_size)
|
---|
1112 | {
|
---|
1113 | struct gse_context *gse_ctx =
|
---|
1114 | talloc_get_type_abort(gensec_security->private_data,
|
---|
1115 | struct gse_context);
|
---|
1116 |
|
---|
1117 | if (gse_ctx->sig_size > 0) {
|
---|
1118 | return gse_ctx->sig_size;
|
---|
1119 | }
|
---|
1120 |
|
---|
1121 | gse_ctx->sig_size = gssapi_get_sig_size(gse_ctx->gssapi_context,
|
---|
1122 | &gse_ctx->gss_mech,
|
---|
1123 | gse_ctx->gss_want_flags,
|
---|
1124 | data_size);
|
---|
1125 | return gse_ctx->sig_size;
|
---|
1126 | }
|
---|
1127 |
|
---|
1128 | static const char *gensec_gse_krb5_oids[] = {
|
---|
1129 | GENSEC_OID_KERBEROS5_OLD,
|
---|
1130 | GENSEC_OID_KERBEROS5,
|
---|
1131 | NULL
|
---|
1132 | };
|
---|
1133 |
|
---|
1134 | const struct gensec_security_ops gensec_gse_krb5_security_ops = {
|
---|
1135 | .name = "gse_krb5",
|
---|
1136 | .auth_type = DCERPC_AUTH_TYPE_KRB5,
|
---|
1137 | .oid = gensec_gse_krb5_oids,
|
---|
1138 | .client_start = gensec_gse_client_start,
|
---|
1139 | .server_start = gensec_gse_server_start,
|
---|
1140 | .magic = gensec_magic_check_krb5_oid,
|
---|
1141 | .update = gensec_gse_update,
|
---|
1142 | .session_key = gensec_gse_session_key,
|
---|
1143 | .session_info = gensec_gse_session_info,
|
---|
1144 | .sig_size = gensec_gse_sig_size,
|
---|
1145 | .sign_packet = gensec_gse_sign_packet,
|
---|
1146 | .check_packet = gensec_gse_check_packet,
|
---|
1147 | .seal_packet = gensec_gse_seal_packet,
|
---|
1148 | .unseal_packet = gensec_gse_unseal_packet,
|
---|
1149 | .max_input_size = gensec_gse_max_input_size,
|
---|
1150 | .max_wrapped_size = gensec_gse_max_wrapped_size,
|
---|
1151 | .wrap = gensec_gse_wrap,
|
---|
1152 | .unwrap = gensec_gse_unwrap,
|
---|
1153 | .have_feature = gensec_gse_have_feature,
|
---|
1154 | .expire_time = gensec_gse_expire_time,
|
---|
1155 | .enabled = true,
|
---|
1156 | .kerberos = true,
|
---|
1157 | .priority = GENSEC_GSSAPI
|
---|
1158 | };
|
---|
1159 |
|
---|
1160 | #endif /* HAVE_KRB5 */
|
---|