s3:gse: add simple gensec_gse_update_send/recv() wrapper functions
[Samba.git] / source3 / librpc / crypto / gse.c
blobbcda8de896f5cadbb6d839da6501e22b059be3fd
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
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.
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.
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/>.
22 /* We support only GSSAPI/KRB5 here */
24 #include "includes.h"
25 #include <tevent.h>
26 #include "lib/util/tevent_ntstatus.h"
27 #include "gse.h"
28 #include "libads/kerberos_proto.h"
29 #include "auth/common_auth.h"
30 #include "auth/gensec/gensec.h"
31 #include "auth/gensec/gensec_internal.h"
32 #include "auth/credentials/credentials.h"
33 #include "../librpc/gen_ndr/dcerpc.h"
35 #if defined(HAVE_KRB5)
37 #include "auth/kerberos/pac_utils.h"
38 #include "auth/kerberos/gssapi_helper.h"
39 #include "gse_krb5.h"
41 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min);
42 static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
43 size_t data_size);
45 struct gse_context {
46 gss_ctx_id_t gssapi_context;
47 gss_name_t server_name;
48 gss_name_t client_name;
49 OM_uint32 gss_want_flags, gss_got_flags;
50 size_t max_wrap_buf_size;
51 size_t sig_size;
53 gss_cred_id_t delegated_cred_handle;
55 NTTIME expire_time;
57 /* gensec_gse only */
58 krb5_context k5ctx;
59 krb5_ccache ccache;
60 krb5_keytab keytab;
62 gss_OID_desc gss_mech;
63 gss_cred_id_t creds;
65 gss_OID ret_mech;
68 /* free non talloc dependent contexts */
69 static int gse_context_destructor(void *ptr)
71 struct gse_context *gse_ctx;
72 OM_uint32 gss_min;
74 gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
75 if (gse_ctx->k5ctx) {
76 if (gse_ctx->ccache) {
77 krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
78 gse_ctx->ccache = NULL;
80 if (gse_ctx->keytab) {
81 krb5_kt_close(gse_ctx->k5ctx, gse_ctx->keytab);
82 gse_ctx->keytab = NULL;
84 krb5_free_context(gse_ctx->k5ctx);
85 gse_ctx->k5ctx = NULL;
87 if (gse_ctx->gssapi_context != GSS_C_NO_CONTEXT) {
88 (void)gss_delete_sec_context(&gss_min,
89 &gse_ctx->gssapi_context,
90 GSS_C_NO_BUFFER);
92 if (gse_ctx->server_name) {
93 (void)gss_release_name(&gss_min,
94 &gse_ctx->server_name);
96 if (gse_ctx->client_name) {
97 (void)gss_release_name(&gss_min,
98 &gse_ctx->client_name);
100 if (gse_ctx->creds) {
101 (void)gss_release_cred(&gss_min,
102 &gse_ctx->creds);
104 if (gse_ctx->delegated_cred_handle) {
105 (void)gss_release_cred(&gss_min,
106 &gse_ctx->delegated_cred_handle);
109 /* MIT and Heimdal differ as to if you can call
110 * gss_release_oid() on this OID, generated by
111 * gss_{accept,init}_sec_context(). However, as long as the
112 * oid is gss_mech_krb5 (which it always is at the moment),
113 * then this is a moot point, as both declare this particular
114 * OID static, and so no memory is lost. This assert is in
115 * place to ensure that the programmer who wishes to extend
116 * this code to EAP or other GSS mechanisms determines an
117 * implementation-dependent way of releasing any dynamically
118 * allocated OID */
119 SMB_ASSERT(smb_gss_oid_equal(&gse_ctx->gss_mech, GSS_C_NO_OID) ||
120 smb_gss_oid_equal(&gse_ctx->gss_mech, gss_mech_krb5));
122 return 0;
125 static NTSTATUS gse_setup_server_principal(TALLOC_CTX *mem_ctx,
126 const char *target_principal,
127 const char *service,
128 const char *hostname,
129 const char *realm,
130 char **pserver_principal,
131 gss_name_t *pserver_name)
133 char *server_principal = NULL;
134 gss_buffer_desc name_token;
135 gss_OID name_type;
136 OM_uint32 maj_stat, min_stat = 0;
138 if (target_principal != NULL) {
139 server_principal = talloc_strdup(mem_ctx, target_principal);
140 name_type = GSS_C_NULL_OID;
141 } else {
142 server_principal = talloc_asprintf(mem_ctx,
143 "%s/%s@%s",
144 service,
145 hostname,
146 realm);
147 name_type = GSS_C_NT_USER_NAME;
149 if (server_principal == NULL) {
150 return NT_STATUS_NO_MEMORY;
153 name_token.value = (uint8_t *)server_principal;
154 name_token.length = strlen(server_principal);
156 maj_stat = gss_import_name(&min_stat,
157 &name_token,
158 name_type,
159 pserver_name);
160 if (maj_stat) {
161 DBG_WARNING("GSS Import name of %s failed: %s\n",
162 server_principal,
163 gse_errstr(mem_ctx, maj_stat, min_stat));
164 TALLOC_FREE(server_principal);
165 return NT_STATUS_INVALID_PARAMETER;
168 *pserver_principal = server_principal;
170 return NT_STATUS_OK;
173 static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx,
174 bool do_sign, bool do_seal,
175 const char *ccache_name,
176 uint32_t add_gss_c_flags,
177 struct gse_context **_gse_ctx)
179 struct gse_context *gse_ctx;
180 krb5_error_code k5ret;
181 NTSTATUS status;
183 gse_ctx = talloc_zero(mem_ctx, struct gse_context);
184 if (!gse_ctx) {
185 return NT_STATUS_NO_MEMORY;
187 talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
189 gse_ctx->expire_time = GENSEC_EXPIRE_TIME_INFINITY;
190 gse_ctx->max_wrap_buf_size = UINT16_MAX;
192 memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
194 gse_ctx->gss_want_flags = GSS_C_MUTUAL_FLAG |
195 GSS_C_DELEG_POLICY_FLAG |
196 GSS_C_REPLAY_FLAG |
197 GSS_C_SEQUENCE_FLAG;
198 if (do_sign) {
199 gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
201 if (do_seal) {
202 gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
203 gse_ctx->gss_want_flags |= GSS_C_CONF_FLAG;
206 gse_ctx->gss_want_flags |= add_gss_c_flags;
208 /* Initialize Kerberos Context */
209 initialize_krb5_error_table();
211 k5ret = krb5_init_context(&gse_ctx->k5ctx);
212 if (k5ret) {
213 DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
214 error_message(k5ret)));
215 status = NT_STATUS_INTERNAL_ERROR;
216 goto err_out;
219 if (!ccache_name) {
220 ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
222 k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
223 &gse_ctx->ccache);
224 if (k5ret) {
225 DEBUG(1, ("Failed to resolve credential cache '%s'! (%s)\n",
226 ccache_name, error_message(k5ret)));
227 status = NT_STATUS_INTERNAL_ERROR;
228 goto err_out;
231 /* TODO: Should we enforce a enc_types list ?
232 ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
235 *_gse_ctx = gse_ctx;
236 return NT_STATUS_OK;
238 err_out:
239 TALLOC_FREE(gse_ctx);
240 return status;
243 static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
244 bool do_sign, bool do_seal,
245 const char *ccache_name,
246 const char *server,
247 const char *service,
248 const char *realm,
249 const char *username,
250 const char *password,
251 uint32_t add_gss_c_flags,
252 struct gse_context **_gse_ctx)
254 struct gse_context *gse_ctx;
255 OM_uint32 gss_maj, gss_min;
256 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
257 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
258 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
259 #endif
260 NTSTATUS status;
262 if (!server || !service) {
263 return NT_STATUS_INVALID_PARAMETER;
266 status = gse_context_init(mem_ctx, do_sign, do_seal,
267 ccache_name, add_gss_c_flags,
268 &gse_ctx);
269 if (!NT_STATUS_IS_OK(status)) {
270 return NT_STATUS_NO_MEMORY;
273 /* TODO: get krb5 ticket using username/password, if no valid
274 * one already available in ccache */
276 gss_maj = smb_gss_krb5_import_cred(&gss_min,
277 gse_ctx->k5ctx,
278 gse_ctx->ccache,
279 NULL, /* keytab_principal */
280 NULL, /* keytab */
281 &gse_ctx->creds);
282 if (gss_maj) {
283 char *ccache = NULL;
284 int kret;
286 kret = krb5_cc_get_full_name(gse_ctx->k5ctx,
287 gse_ctx->ccache,
288 &ccache);
289 if (kret != 0) {
290 ccache = NULL;
293 DEBUG(5, ("smb_gss_krb5_import_cred ccache[%s] failed with [%s] -"
294 "the caller may retry after a kinit.\n",
295 ccache, gse_errstr(gse_ctx, gss_maj, gss_min)));
296 SAFE_FREE(ccache);
297 status = NT_STATUS_INTERNAL_ERROR;
298 goto err_out;
301 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
303 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
305 * This allows us to disable SIGN and SEAL for
306 * AUTH_LEVEL_CONNECT and AUTH_LEVEL_INTEGRITY.
308 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
309 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
311 gss_maj = gss_set_cred_option(&gss_min, &gse_ctx->creds,
312 oid,
313 &empty_buffer);
314 if (gss_maj) {
315 DEBUG(0, ("gss_set_cred_option(GSS_KRB5_CRED_NO_CI_FLAGS_X), "
316 "failed with [%s]\n",
317 gse_errstr(gse_ctx, gss_maj, gss_min)));
318 status = NT_STATUS_INTERNAL_ERROR;
319 goto err_out;
321 #endif
323 *_gse_ctx = gse_ctx;
324 return NT_STATUS_OK;
326 err_out:
327 TALLOC_FREE(gse_ctx);
328 return status;
331 static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
332 struct gensec_security *gensec_security,
333 const DATA_BLOB *token_in,
334 DATA_BLOB *token_out)
336 struct gse_context *gse_ctx =
337 talloc_get_type_abort(gensec_security->private_data,
338 struct gse_context);
339 OM_uint32 gss_maj = 0;
340 OM_uint32 gss_min;
341 gss_buffer_desc in_data;
342 gss_buffer_desc out_data;
343 DATA_BLOB blob = data_blob_null;
344 NTSTATUS status;
345 OM_uint32 time_rec = 0;
346 struct timeval tv;
347 struct cli_credentials *cli_creds = gensec_get_credentials(gensec_security);
348 const char *target_principal = gensec_get_target_principal(gensec_security);
349 const char *hostname = gensec_get_target_hostname(gensec_security);
350 const char *service = gensec_get_target_service(gensec_security);
351 const char *client_realm = cli_credentials_get_realm(cli_creds);
352 char *server_principal = NULL;
353 char *server_realm = NULL;
354 bool fallback = false;
356 in_data.value = token_in->data;
357 in_data.length = token_in->length;
360 * With credentials for administrator@FOREST1.EXAMPLE.COM this patch
361 * changes the target_principal for the ldap service of host
362 * dc2.forest2.example.com from
364 * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM
366 * to
368 * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM
370 * Typically ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM should be
371 * used in order to allow the KDC of FOREST1.EXAMPLE.COM to generate a
372 * referral ticket for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM.
374 * The problem is that KDCs only return such referral tickets if
375 * there's a forest trust between FOREST1.EXAMPLE.COM and
376 * FOREST2.EXAMPLE.COM. If there's only an external domain trust
377 * between FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM the KDC of
378 * FOREST1.EXAMPLE.COM will respond with S_PRINCIPAL_UNKNOWN when being
379 * asked for ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM.
381 * In the case of an external trust the client can still ask explicitly
382 * for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM and the KDC of
383 * FOREST1.EXAMPLE.COM will generate it.
385 * From there the client can use the
386 * krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM ticket and ask a KDC
387 * of FOREST2.EXAMPLE.COM for a service ticket for
388 * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM.
390 * With Heimdal we'll get the fallback on S_PRINCIPAL_UNKNOWN behavior
391 * when we pass ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM as
392 * target principal. As _krb5_get_cred_kdc_any() first calls
393 * get_cred_kdc_referral() (which always starts with the client realm)
394 * and falls back to get_cred_kdc_capath() (which starts with the given
395 * realm).
397 * MIT krb5 only tries the given realm of the target principal, if we
398 * want to autodetect support for transitive forest trusts, would have
399 * to do the fallback ourself.
401 #ifndef SAMBA4_USES_HEIMDAL
402 if (gse_ctx->server_name == NULL) {
403 OM_uint32 gss_min2 = 0;
405 status = gse_setup_server_principal(mem_ctx,
406 target_principal,
407 service,
408 hostname,
409 client_realm,
410 &server_principal,
411 &gse_ctx->server_name);
412 if (!NT_STATUS_IS_OK(status)) {
413 return status;
416 gss_maj = gss_init_sec_context(&gss_min,
417 gse_ctx->creds,
418 &gse_ctx->gssapi_context,
419 gse_ctx->server_name,
420 &gse_ctx->gss_mech,
421 gse_ctx->gss_want_flags,
423 GSS_C_NO_CHANNEL_BINDINGS,
424 &in_data,
425 NULL,
426 &out_data,
427 &gse_ctx->gss_got_flags,
428 &time_rec);
429 if (gss_maj != GSS_S_FAILURE) {
430 goto init_sec_context_done;
432 if (gss_min != (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
433 goto init_sec_context_done;
435 if (target_principal != NULL) {
436 goto init_sec_context_done;
439 fallback = true;
440 TALLOC_FREE(server_principal);
441 gss_release_name(&gss_min2, &gse_ctx->server_name);
443 #endif /* !SAMBA4_USES_HEIMDAL */
445 if (gse_ctx->server_name == NULL) {
446 server_realm = smb_krb5_get_realm_from_hostname(mem_ctx,
447 hostname,
448 client_realm);
449 if (server_realm == NULL) {
450 return NT_STATUS_NO_MEMORY;
453 if (fallback &&
454 strequal(client_realm, server_realm)) {
455 goto init_sec_context_done;
458 status = gse_setup_server_principal(mem_ctx,
459 target_principal,
460 service,
461 hostname,
462 server_realm,
463 &server_principal,
464 &gse_ctx->server_name);
465 TALLOC_FREE(server_realm);
466 if (!NT_STATUS_IS_OK(status)) {
467 return status;
470 TALLOC_FREE(server_principal);
473 gss_maj = gss_init_sec_context(&gss_min,
474 gse_ctx->creds,
475 &gse_ctx->gssapi_context,
476 gse_ctx->server_name,
477 &gse_ctx->gss_mech,
478 gse_ctx->gss_want_flags,
479 0, GSS_C_NO_CHANNEL_BINDINGS,
480 &in_data, NULL, &out_data,
481 &gse_ctx->gss_got_flags, &time_rec);
482 goto init_sec_context_done;
483 /* JUMP! */
484 init_sec_context_done:
486 switch (gss_maj) {
487 case GSS_S_COMPLETE:
488 /* we are done with it */
489 tv = timeval_current_ofs(time_rec, 0);
490 gse_ctx->expire_time = timeval_to_nttime(&tv);
492 status = NT_STATUS_OK;
493 break;
494 case GSS_S_CONTINUE_NEEDED:
495 /* we will need a third leg */
496 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
497 break;
498 case GSS_S_CONTEXT_EXPIRED:
499 /* Make SPNEGO ignore us, we can't go any further here */
500 DBG_NOTICE("Context expired\n");
501 status = NT_STATUS_INVALID_PARAMETER;
502 goto done;
503 case GSS_S_FAILURE:
504 switch (gss_min) {
505 case (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
506 DBG_NOTICE("Server principal not found\n");
507 /* Make SPNEGO ignore us, we can't go any further here */
508 status = NT_STATUS_INVALID_PARAMETER;
509 goto done;
510 case (OM_uint32)KRB5KRB_AP_ERR_TKT_EXPIRED:
511 DBG_NOTICE("Ticket expired\n");
512 /* Make SPNEGO ignore us, we can't go any further here */
513 status = NT_STATUS_INVALID_PARAMETER;
514 goto done;
515 case (OM_uint32)KRB5KRB_AP_ERR_TKT_NYV:
516 DBG_NOTICE("Clockskew\n");
517 /* Make SPNEGO ignore us, we can't go any further here */
518 status = NT_STATUS_TIME_DIFFERENCE_AT_DC;
519 goto done;
520 case (OM_uint32)KRB5_KDC_UNREACH:
521 DBG_NOTICE("KDC unreachable\n");
522 /* Make SPNEGO ignore us, we can't go any further here */
523 status = NT_STATUS_NO_LOGON_SERVERS;
524 goto done;
525 case (OM_uint32)KRB5KRB_AP_ERR_MSG_TYPE:
526 /* Garbage input, possibly from the auto-mech detection */
527 status = NT_STATUS_INVALID_PARAMETER;
528 goto done;
529 default:
530 DBG_ERR("gss_init_sec_context failed with [%s](%u)\n",
531 gse_errstr(talloc_tos(), gss_maj, gss_min),
532 gss_min);
533 status = NT_STATUS_LOGON_FAILURE;
534 goto done;
536 break;
537 default:
538 DBG_ERR("gss_init_sec_context failed with [%s]\n",
539 gse_errstr(talloc_tos(), gss_maj, gss_min));
540 status = NT_STATUS_INTERNAL_ERROR;
541 goto done;
544 /* we may be told to return nothing */
545 if (out_data.length) {
546 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
547 if (!blob.data) {
548 status = NT_STATUS_NO_MEMORY;
551 gss_maj = gss_release_buffer(&gss_min, &out_data);
554 done:
555 *token_out = blob;
556 return status;
559 static NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
560 bool do_sign, bool do_seal,
561 uint32_t add_gss_c_flags,
562 struct gse_context **_gse_ctx)
564 struct gse_context *gse_ctx;
565 OM_uint32 gss_maj, gss_min;
566 krb5_error_code ret;
567 NTSTATUS status;
569 status = gse_context_init(mem_ctx, do_sign, do_seal,
570 NULL, add_gss_c_flags, &gse_ctx);
571 if (!NT_STATUS_IS_OK(status)) {
572 return NT_STATUS_NO_MEMORY;
575 ret = gse_krb5_get_server_keytab(gse_ctx->k5ctx,
576 &gse_ctx->keytab);
577 if (ret) {
578 status = NT_STATUS_INTERNAL_ERROR;
579 goto done;
582 /* This creates a GSSAPI cred_id_t with the keytab set */
583 gss_maj = smb_gss_krb5_import_cred(&gss_min, gse_ctx->k5ctx,
584 NULL, NULL, gse_ctx->keytab,
585 &gse_ctx->creds);
587 if (gss_maj != 0) {
588 DEBUG(0, ("smb_gss_krb5_import_cred failed with [%s]\n",
589 gse_errstr(gse_ctx, gss_maj, gss_min)));
590 status = NT_STATUS_INTERNAL_ERROR;
591 goto done;
594 status = NT_STATUS_OK;
596 done:
597 if (!NT_STATUS_IS_OK(status)) {
598 TALLOC_FREE(gse_ctx);
601 *_gse_ctx = gse_ctx;
602 return status;
605 static NTSTATUS gse_get_server_auth_token(TALLOC_CTX *mem_ctx,
606 struct gensec_security *gensec_security,
607 const DATA_BLOB *token_in,
608 DATA_BLOB *token_out)
610 struct gse_context *gse_ctx =
611 talloc_get_type_abort(gensec_security->private_data,
612 struct gse_context);
613 OM_uint32 gss_maj, gss_min;
614 gss_buffer_desc in_data;
615 gss_buffer_desc out_data;
616 DATA_BLOB blob = data_blob_null;
617 NTSTATUS status;
618 OM_uint32 time_rec = 0;
619 struct timeval tv;
621 in_data.value = token_in->data;
622 in_data.length = token_in->length;
624 gss_maj = gss_accept_sec_context(&gss_min,
625 &gse_ctx->gssapi_context,
626 gse_ctx->creds,
627 &in_data,
628 GSS_C_NO_CHANNEL_BINDINGS,
629 &gse_ctx->client_name,
630 &gse_ctx->ret_mech,
631 &out_data,
632 &gse_ctx->gss_got_flags,
633 &time_rec,
634 &gse_ctx->delegated_cred_handle);
635 switch (gss_maj) {
636 case GSS_S_COMPLETE:
637 /* we are done with it */
638 tv = timeval_current_ofs(time_rec, 0);
639 gse_ctx->expire_time = timeval_to_nttime(&tv);
641 status = NT_STATUS_OK;
642 break;
643 case GSS_S_CONTINUE_NEEDED:
644 /* we will need a third leg */
645 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
646 break;
647 default:
648 DEBUG(1, ("gss_accept_sec_context failed with [%s]\n",
649 gse_errstr(talloc_tos(), gss_maj, gss_min)));
651 if (gse_ctx->gssapi_context) {
652 gss_delete_sec_context(&gss_min,
653 &gse_ctx->gssapi_context,
654 GSS_C_NO_BUFFER);
658 * If we got an output token, make Windows aware of it
659 * by telling it that more processing is needed
661 if (out_data.length > 0) {
662 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
663 /* Fall through to handle the out token */
664 } else {
665 status = NT_STATUS_LOGON_FAILURE;
666 goto done;
670 /* we may be told to return nothing */
671 if (out_data.length) {
672 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
673 if (!blob.data) {
674 status = NT_STATUS_NO_MEMORY;
676 gss_maj = gss_release_buffer(&gss_min, &out_data);
680 done:
681 *token_out = blob;
682 return status;
685 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
687 OM_uint32 gss_min, gss_maj;
688 gss_buffer_desc msg_min;
689 gss_buffer_desc msg_maj;
690 OM_uint32 msg_ctx = 0;
692 char *errstr = NULL;
694 ZERO_STRUCT(msg_min);
695 ZERO_STRUCT(msg_maj);
697 gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
698 GSS_C_NO_OID, &msg_ctx, &msg_maj);
699 if (gss_maj) {
700 goto done;
702 errstr = talloc_strndup(mem_ctx,
703 (char *)msg_maj.value,
704 msg_maj.length);
705 if (!errstr) {
706 goto done;
708 gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
709 (gss_OID)discard_const(gss_mech_krb5),
710 &msg_ctx, &msg_min);
711 if (gss_maj) {
712 goto done;
715 errstr = talloc_strdup_append_buffer(errstr, ": ");
716 if (!errstr) {
717 goto done;
719 errstr = talloc_strndup_append_buffer(errstr,
720 (char *)msg_min.value,
721 msg_min.length);
722 if (!errstr) {
723 goto done;
726 done:
727 if (msg_min.value) {
728 gss_maj = gss_release_buffer(&gss_min, &msg_min);
730 if (msg_maj.value) {
731 gss_maj = gss_release_buffer(&gss_min, &msg_maj);
733 return errstr;
736 static NTSTATUS gensec_gse_client_start(struct gensec_security *gensec_security)
738 struct gse_context *gse_ctx;
739 struct cli_credentials *creds = gensec_get_credentials(gensec_security);
740 NTSTATUS nt_status;
741 OM_uint32 want_flags = 0;
742 bool do_sign = false, do_seal = false;
743 const char *hostname = gensec_get_target_hostname(gensec_security);
744 const char *service = gensec_get_target_service(gensec_security);
745 const char *username = cli_credentials_get_username(creds);
746 const char *password = cli_credentials_get_password(creds);
747 const char *realm = cli_credentials_get_realm(creds);
749 if (!hostname) {
750 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
751 return NT_STATUS_INVALID_PARAMETER;
753 if (is_ipaddress(hostname)) {
754 DEBUG(2, ("Cannot do GSE to an IP address\n"));
755 return NT_STATUS_INVALID_PARAMETER;
757 if (strcmp(hostname, "localhost") == 0) {
758 DEBUG(2, ("GSE to 'localhost' does not make sense\n"));
759 return NT_STATUS_INVALID_PARAMETER;
762 if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
763 do_sign = true;
765 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
766 do_sign = true;
768 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
769 do_seal = true;
771 if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
772 want_flags |= GSS_C_DCE_STYLE;
775 nt_status = gse_init_client(gensec_security, do_sign, do_seal, NULL,
776 hostname, service, realm,
777 username, password, want_flags,
778 &gse_ctx);
779 if (!NT_STATUS_IS_OK(nt_status)) {
780 return nt_status;
782 gensec_security->private_data = gse_ctx;
783 return NT_STATUS_OK;
786 static NTSTATUS gensec_gse_server_start(struct gensec_security *gensec_security)
788 struct gse_context *gse_ctx;
789 NTSTATUS nt_status;
790 OM_uint32 want_flags = 0;
791 bool do_sign = false, do_seal = false;
793 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
794 do_sign = true;
796 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
797 do_seal = true;
799 if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
800 want_flags |= GSS_C_DCE_STYLE;
803 nt_status = gse_init_server(gensec_security, do_sign, do_seal, want_flags,
804 &gse_ctx);
805 if (!NT_STATUS_IS_OK(nt_status)) {
806 return nt_status;
808 gensec_security->private_data = gse_ctx;
809 return NT_STATUS_OK;
812 struct gensec_gse_update_state {
813 NTSTATUS status;
814 DATA_BLOB out;
817 static NTSTATUS gensec_gse_update_internal(struct gensec_security *gensec_security,
818 TALLOC_CTX *mem_ctx,
819 const DATA_BLOB in,
820 DATA_BLOB *out);
822 static struct tevent_req *gensec_gse_update_send(TALLOC_CTX *mem_ctx,
823 struct tevent_context *ev,
824 struct gensec_security *gensec_security,
825 const DATA_BLOB in)
827 struct tevent_req *req = NULL;
828 struct gensec_gse_update_state *state = NULL;
829 NTSTATUS status;
831 req = tevent_req_create(mem_ctx, &state,
832 struct gensec_gse_update_state);
833 if (req == NULL) {
834 return NULL;
837 status = gensec_gse_update_internal(gensec_security,
838 state, in,
839 &state->out);
840 state->status = status;
841 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
842 tevent_req_done(req);
843 return tevent_req_post(req, ev);
845 if (tevent_req_nterror(req, status)) {
846 return tevent_req_post(req, ev);
849 tevent_req_done(req);
850 return tevent_req_post(req, ev);
853 static NTSTATUS gensec_gse_update_internal(struct gensec_security *gensec_security,
854 TALLOC_CTX *mem_ctx,
855 const DATA_BLOB in,
856 DATA_BLOB *out)
858 NTSTATUS status;
860 switch (gensec_security->gensec_role) {
861 case GENSEC_CLIENT:
862 status = gse_get_client_auth_token(mem_ctx,
863 gensec_security,
864 &in, out);
865 break;
866 case GENSEC_SERVER:
867 status = gse_get_server_auth_token(mem_ctx,
868 gensec_security,
869 &in, out);
870 break;
872 if (!NT_STATUS_IS_OK(status)) {
873 return status;
876 return NT_STATUS_OK;
879 static NTSTATUS gensec_gse_update_recv(struct tevent_req *req,
880 TALLOC_CTX *out_mem_ctx,
881 DATA_BLOB *out)
883 struct gensec_gse_update_state *state =
884 tevent_req_data(req,
885 struct gensec_gse_update_state);
886 NTSTATUS status;
888 *out = data_blob_null;
890 if (tevent_req_is_nterror(req, &status)) {
891 tevent_req_received(req);
892 return status;
895 *out = state->out;
896 talloc_steal(out_mem_ctx, state->out.data);
897 status = state->status;
898 tevent_req_received(req);
899 return status;
902 static NTSTATUS gensec_gse_wrap(struct gensec_security *gensec_security,
903 TALLOC_CTX *mem_ctx,
904 const DATA_BLOB *in,
905 DATA_BLOB *out)
907 struct gse_context *gse_ctx =
908 talloc_get_type_abort(gensec_security->private_data,
909 struct gse_context);
910 OM_uint32 maj_stat, min_stat;
911 gss_buffer_desc input_token, output_token;
912 int conf_state;
913 input_token.length = in->length;
914 input_token.value = in->data;
916 maj_stat = gss_wrap(&min_stat,
917 gse_ctx->gssapi_context,
918 gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
919 GSS_C_QOP_DEFAULT,
920 &input_token,
921 &conf_state,
922 &output_token);
923 if (GSS_ERROR(maj_stat)) {
924 DEBUG(0, ("gensec_gse_wrap: GSS Wrap failed: %s\n",
925 gse_errstr(talloc_tos(), maj_stat, min_stat)));
926 return NT_STATUS_ACCESS_DENIED;
929 *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
930 gss_release_buffer(&min_stat, &output_token);
932 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
933 && !conf_state) {
934 return NT_STATUS_ACCESS_DENIED;
936 return NT_STATUS_OK;
939 static NTSTATUS gensec_gse_unwrap(struct gensec_security *gensec_security,
940 TALLOC_CTX *mem_ctx,
941 const DATA_BLOB *in,
942 DATA_BLOB *out)
944 struct gse_context *gse_ctx =
945 talloc_get_type_abort(gensec_security->private_data,
946 struct gse_context);
947 OM_uint32 maj_stat, min_stat;
948 gss_buffer_desc input_token, output_token;
949 int conf_state;
950 gss_qop_t qop_state;
951 input_token.length = in->length;
952 input_token.value = in->data;
954 maj_stat = gss_unwrap(&min_stat,
955 gse_ctx->gssapi_context,
956 &input_token,
957 &output_token,
958 &conf_state,
959 &qop_state);
960 if (GSS_ERROR(maj_stat)) {
961 DEBUG(0, ("gensec_gse_unwrap: GSS UnWrap failed: %s\n",
962 gse_errstr(talloc_tos(), maj_stat, min_stat)));
963 return NT_STATUS_ACCESS_DENIED;
966 *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
967 gss_release_buffer(&min_stat, &output_token);
969 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
970 && !conf_state) {
971 return NT_STATUS_ACCESS_DENIED;
973 return NT_STATUS_OK;
976 static NTSTATUS gensec_gse_seal_packet(struct gensec_security *gensec_security,
977 TALLOC_CTX *mem_ctx,
978 uint8_t *data, size_t length,
979 const uint8_t *whole_pdu, size_t pdu_length,
980 DATA_BLOB *sig)
982 struct gse_context *gse_ctx =
983 talloc_get_type_abort(gensec_security->private_data,
984 struct gse_context);
985 bool hdr_signing = false;
986 size_t sig_size = 0;
987 NTSTATUS status;
989 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
990 hdr_signing = true;
993 sig_size = gensec_gse_sig_size(gensec_security, length);
995 status = gssapi_seal_packet(gse_ctx->gssapi_context,
996 &gse_ctx->gss_mech,
997 hdr_signing, sig_size,
998 data, length,
999 whole_pdu, pdu_length,
1000 mem_ctx, sig);
1001 if (!NT_STATUS_IS_OK(status)) {
1002 DEBUG(0, ("gssapi_seal_packet(hdr_signing=%u,sig_size=%zu,"
1003 "data=%zu,pdu=%zu) failed: %s\n",
1004 hdr_signing, sig_size, length, pdu_length,
1005 nt_errstr(status)));
1006 return status;
1009 return NT_STATUS_OK;
1012 static NTSTATUS gensec_gse_unseal_packet(struct gensec_security *gensec_security,
1013 uint8_t *data, size_t length,
1014 const uint8_t *whole_pdu, size_t pdu_length,
1015 const DATA_BLOB *sig)
1017 struct gse_context *gse_ctx =
1018 talloc_get_type_abort(gensec_security->private_data,
1019 struct gse_context);
1020 bool hdr_signing = false;
1021 NTSTATUS status;
1023 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
1024 hdr_signing = true;
1027 status = gssapi_unseal_packet(gse_ctx->gssapi_context,
1028 &gse_ctx->gss_mech,
1029 hdr_signing,
1030 data, length,
1031 whole_pdu, pdu_length,
1032 sig);
1033 if (!NT_STATUS_IS_OK(status)) {
1034 DEBUG(0, ("gssapi_unseal_packet(hdr_signing=%u,sig_size=%zu,"
1035 "data=%zu,pdu=%zu) failed: %s\n",
1036 hdr_signing, sig->length, length, pdu_length,
1037 nt_errstr(status)));
1038 return status;
1041 return NT_STATUS_OK;
1044 static NTSTATUS gensec_gse_sign_packet(struct gensec_security *gensec_security,
1045 TALLOC_CTX *mem_ctx,
1046 const uint8_t *data, size_t length,
1047 const uint8_t *whole_pdu, size_t pdu_length,
1048 DATA_BLOB *sig)
1050 struct gse_context *gse_ctx =
1051 talloc_get_type_abort(gensec_security->private_data,
1052 struct gse_context);
1053 bool hdr_signing = false;
1054 NTSTATUS status;
1056 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
1057 hdr_signing = true;
1060 status = gssapi_sign_packet(gse_ctx->gssapi_context,
1061 &gse_ctx->gss_mech,
1062 hdr_signing,
1063 data, length,
1064 whole_pdu, pdu_length,
1065 mem_ctx, sig);
1066 if (!NT_STATUS_IS_OK(status)) {
1067 DEBUG(0, ("gssapi_sign_packet(hdr_signing=%u,"
1068 "data=%zu,pdu=%zu) failed: %s\n",
1069 hdr_signing, length, pdu_length,
1070 nt_errstr(status)));
1071 return status;
1074 return NT_STATUS_OK;
1077 static NTSTATUS gensec_gse_check_packet(struct gensec_security *gensec_security,
1078 const uint8_t *data, size_t length,
1079 const uint8_t *whole_pdu, size_t pdu_length,
1080 const DATA_BLOB *sig)
1082 struct gse_context *gse_ctx =
1083 talloc_get_type_abort(gensec_security->private_data,
1084 struct gse_context);
1085 bool hdr_signing = false;
1086 NTSTATUS status;
1088 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
1089 hdr_signing = true;
1092 status = gssapi_check_packet(gse_ctx->gssapi_context,
1093 &gse_ctx->gss_mech,
1094 hdr_signing,
1095 data, length,
1096 whole_pdu, pdu_length,
1097 sig);
1098 if (!NT_STATUS_IS_OK(status)) {
1099 DEBUG(0, ("gssapi_check_packet(hdr_signing=%u,sig_size=%zu"
1100 "data=%zu,pdu=%zu) failed: %s\n",
1101 hdr_signing, sig->length, length, pdu_length,
1102 nt_errstr(status)));
1103 return status;
1106 return NT_STATUS_OK;
1109 /* Try to figure out what features we actually got on the connection */
1110 static bool gensec_gse_have_feature(struct gensec_security *gensec_security,
1111 uint32_t feature)
1113 struct gse_context *gse_ctx =
1114 talloc_get_type_abort(gensec_security->private_data,
1115 struct gse_context);
1117 if (feature & GENSEC_FEATURE_SESSION_KEY) {
1118 return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
1120 if (feature & GENSEC_FEATURE_SIGN) {
1121 return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
1123 if (feature & GENSEC_FEATURE_SEAL) {
1124 return gse_ctx->gss_got_flags & GSS_C_CONF_FLAG;
1126 if (feature & GENSEC_FEATURE_DCE_STYLE) {
1127 return gse_ctx->gss_got_flags & GSS_C_DCE_STYLE;
1129 if (feature & GENSEC_FEATURE_NEW_SPNEGO) {
1130 NTSTATUS status;
1131 uint32_t keytype;
1133 if (!(gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG)) {
1134 return false;
1137 status = gssapi_get_session_key(talloc_tos(),
1138 gse_ctx->gssapi_context, NULL, &keytype);
1140 * We should do a proper sig on the mechListMic unless
1141 * we know we have to be backwards compatible with
1142 * earlier windows versions.
1144 * Negotiating a non-krb5
1145 * mech for example should be regarded as having
1146 * NEW_SPNEGO
1148 if (NT_STATUS_IS_OK(status)) {
1149 switch (keytype) {
1150 case ENCTYPE_DES_CBC_CRC:
1151 case ENCTYPE_DES_CBC_MD5:
1152 case ENCTYPE_ARCFOUR_HMAC:
1153 case ENCTYPE_DES3_CBC_SHA1:
1154 return false;
1157 return true;
1159 /* We can always do async (rather than strict request/reply) packets. */
1160 if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
1161 return true;
1163 if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
1164 return true;
1166 return false;
1169 static NTTIME gensec_gse_expire_time(struct gensec_security *gensec_security)
1171 struct gse_context *gse_ctx =
1172 talloc_get_type_abort(gensec_security->private_data,
1173 struct gse_context);
1175 return gse_ctx->expire_time;
1179 * Extract the 'sesssion key' needed by SMB signing and ncacn_np
1180 * (for encrypting some passwords).
1182 * This breaks all the abstractions, but what do you expect...
1184 static NTSTATUS gensec_gse_session_key(struct gensec_security *gensec_security,
1185 TALLOC_CTX *mem_ctx,
1186 DATA_BLOB *session_key)
1188 struct gse_context *gse_ctx =
1189 talloc_get_type_abort(gensec_security->private_data,
1190 struct gse_context);
1192 return gssapi_get_session_key(mem_ctx, gse_ctx->gssapi_context, session_key, NULL);
1195 /* Get some basic (and authorization) information about the user on
1196 * this session. This uses either the PAC (if present) or a local
1197 * database lookup */
1198 static NTSTATUS gensec_gse_session_info(struct gensec_security *gensec_security,
1199 TALLOC_CTX *mem_ctx,
1200 struct auth_session_info **_session_info)
1202 struct gse_context *gse_ctx =
1203 talloc_get_type_abort(gensec_security->private_data,
1204 struct gse_context);
1205 NTSTATUS nt_status;
1206 TALLOC_CTX *tmp_ctx;
1207 struct auth_session_info *session_info = NULL;
1208 OM_uint32 maj_stat, min_stat;
1209 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
1211 gss_buffer_desc name_token;
1212 char *principal_string;
1214 tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gse_session_info context");
1215 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1217 maj_stat = gss_display_name(&min_stat,
1218 gse_ctx->client_name,
1219 &name_token,
1220 NULL);
1221 if (GSS_ERROR(maj_stat)) {
1222 DEBUG(1, ("GSS display_name failed: %s\n",
1223 gse_errstr(talloc_tos(), maj_stat, min_stat)));
1224 talloc_free(tmp_ctx);
1225 return NT_STATUS_FOOBAR;
1228 principal_string = talloc_strndup(tmp_ctx,
1229 (const char *)name_token.value,
1230 name_token.length);
1232 gss_release_buffer(&min_stat, &name_token);
1234 if (!principal_string) {
1235 talloc_free(tmp_ctx);
1236 return NT_STATUS_NO_MEMORY;
1239 nt_status = gssapi_obtain_pac_blob(tmp_ctx, gse_ctx->gssapi_context,
1240 gse_ctx->client_name,
1241 &pac_blob);
1243 /* IF we have the PAC - otherwise we need to get this
1244 * data from elsewere
1246 if (NT_STATUS_IS_OK(nt_status)) {
1247 pac_blob_ptr = &pac_blob;
1249 nt_status = gensec_generate_session_info_pac(tmp_ctx,
1250 gensec_security,
1251 NULL,
1252 pac_blob_ptr, principal_string,
1253 gensec_get_remote_address(gensec_security),
1254 &session_info);
1255 if (!NT_STATUS_IS_OK(nt_status)) {
1256 talloc_free(tmp_ctx);
1257 return nt_status;
1260 nt_status = gensec_gse_session_key(gensec_security, session_info,
1261 &session_info->session_key);
1262 if (!NT_STATUS_IS_OK(nt_status)) {
1263 talloc_free(tmp_ctx);
1264 return nt_status;
1267 *_session_info = talloc_move(mem_ctx, &session_info);
1268 talloc_free(tmp_ctx);
1270 return NT_STATUS_OK;
1273 static size_t gensec_gse_max_input_size(struct gensec_security *gensec_security)
1275 struct gse_context *gse_ctx =
1276 talloc_get_type_abort(gensec_security->private_data,
1277 struct gse_context);
1278 OM_uint32 maj_stat, min_stat;
1279 OM_uint32 max_input_size;
1281 maj_stat = gss_wrap_size_limit(&min_stat,
1282 gse_ctx->gssapi_context,
1283 gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
1284 GSS_C_QOP_DEFAULT,
1285 gse_ctx->max_wrap_buf_size,
1286 &max_input_size);
1287 if (GSS_ERROR(maj_stat)) {
1288 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1289 DEBUG(1, ("gensec_gssapi_max_input_size: determining signature size with gss_wrap_size_limit failed: %s\n",
1290 gse_errstr(mem_ctx, maj_stat, min_stat)));
1291 talloc_free(mem_ctx);
1292 return 0;
1295 return max_input_size;
1298 /* Find out the maximum output size negotiated on this connection */
1299 static size_t gensec_gse_max_wrapped_size(struct gensec_security *gensec_security)
1301 struct gse_context *gse_ctx =
1302 talloc_get_type_abort(gensec_security->private_data,
1303 struct gse_context);
1304 return gse_ctx->max_wrap_buf_size;
1307 static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
1308 size_t data_size)
1310 struct gse_context *gse_ctx =
1311 talloc_get_type_abort(gensec_security->private_data,
1312 struct gse_context);
1314 if (gse_ctx->sig_size > 0) {
1315 return gse_ctx->sig_size;
1318 gse_ctx->sig_size = gssapi_get_sig_size(gse_ctx->gssapi_context,
1319 &gse_ctx->gss_mech,
1320 gse_ctx->gss_got_flags,
1321 data_size);
1322 return gse_ctx->sig_size;
1325 static const char *gensec_gse_final_auth_type(struct gensec_security *gensec_security)
1327 struct gse_context *gse_ctx =
1328 talloc_get_type_abort(gensec_security->private_data,
1329 struct gse_context);
1331 /* Only return the string for GSSAPI/Krb5 */
1332 if (smb_gss_oid_equal(&gse_ctx->gss_mech,
1333 gss_mech_krb5)) {
1334 return GENSEC_FINAL_AUTH_TYPE_KRB5;
1335 } else {
1336 return "gensec_gse: UNKNOWN MECH";
1340 static const char *gensec_gse_krb5_oids[] = {
1341 GENSEC_OID_KERBEROS5_OLD,
1342 GENSEC_OID_KERBEROS5,
1343 NULL
1346 const struct gensec_security_ops gensec_gse_krb5_security_ops = {
1347 .name = "gse_krb5",
1348 .auth_type = DCERPC_AUTH_TYPE_KRB5,
1349 .oid = gensec_gse_krb5_oids,
1350 .client_start = gensec_gse_client_start,
1351 .server_start = gensec_gse_server_start,
1352 .magic = gensec_magic_check_krb5_oid,
1353 .update_send = gensec_gse_update_send,
1354 .update_recv = gensec_gse_update_recv,
1355 .session_key = gensec_gse_session_key,
1356 .session_info = gensec_gse_session_info,
1357 .sig_size = gensec_gse_sig_size,
1358 .sign_packet = gensec_gse_sign_packet,
1359 .check_packet = gensec_gse_check_packet,
1360 .seal_packet = gensec_gse_seal_packet,
1361 .unseal_packet = gensec_gse_unseal_packet,
1362 .max_input_size = gensec_gse_max_input_size,
1363 .max_wrapped_size = gensec_gse_max_wrapped_size,
1364 .wrap = gensec_gse_wrap,
1365 .unwrap = gensec_gse_unwrap,
1366 .have_feature = gensec_gse_have_feature,
1367 .expire_time = gensec_gse_expire_time,
1368 .final_auth_type = gensec_gse_final_auth_type,
1369 .enabled = true,
1370 .kerberos = true,
1371 .priority = GENSEC_GSSAPI
1374 #endif /* HAVE_KRB5 */