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 */
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"
33 #if defined(HAVE_KRB5)
35 #include "auth/kerberos/pac_utils.h"
36 #include "auth/kerberos/gssapi_helper.h"
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,
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;
51 gss_cred_id_t delegated_cred_handle;
60 gss_OID_desc gss_mech;
66 /* free non talloc dependent contexts */
67 static int gse_context_destructor(void *ptr)
69 struct gse_context *gse_ctx;
72 gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
74 if (gse_ctx->ccache) {
75 krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
76 gse_ctx->ccache = NULL;
78 if (gse_ctx->keytab) {
79 krb5_kt_close(gse_ctx->k5ctx, gse_ctx->keytab);
80 gse_ctx->keytab = NULL;
82 krb5_free_context(gse_ctx->k5ctx);
83 gse_ctx->k5ctx = NULL;
85 if (gse_ctx->gssapi_context != GSS_C_NO_CONTEXT) {
86 (void)gss_delete_sec_context(&gss_min,
87 &gse_ctx->gssapi_context,
90 if (gse_ctx->server_name) {
91 (void)gss_release_name(&gss_min,
92 &gse_ctx->server_name);
94 if (gse_ctx->client_name) {
95 (void)gss_release_name(&gss_min,
96 &gse_ctx->client_name);
99 (void)gss_release_cred(&gss_min,
102 if (gse_ctx->delegated_cred_handle) {
103 (void)gss_release_cred(&gss_min,
104 &gse_ctx->delegated_cred_handle);
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
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));
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)
129 struct gse_context *gse_ctx;
130 krb5_error_code k5ret;
133 gse_ctx = talloc_zero(mem_ctx, struct gse_context);
135 return NT_STATUS_NO_MEMORY;
137 talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
139 gse_ctx->expire_time = GENSEC_EXPIRE_TIME_INFINITY;
140 gse_ctx->max_wrap_buf_size = UINT16_MAX;
142 memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
144 gse_ctx->gss_want_flags = GSS_C_MUTUAL_FLAG |
145 GSS_C_DELEG_POLICY_FLAG |
149 gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
152 gse_ctx->gss_want_flags |= GSS_C_INTEG_FLAG;
153 gse_ctx->gss_want_flags |= GSS_C_CONF_FLAG;
156 gse_ctx->gss_want_flags |= add_gss_c_flags;
158 /* Initialize Kerberos Context */
159 initialize_krb5_error_table();
161 k5ret = krb5_init_context(&gse_ctx->k5ctx);
163 DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
164 error_message(k5ret)));
165 status = NT_STATUS_INTERNAL_ERROR;
170 ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
172 k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
175 DEBUG(1, ("Failed to resolve credential cache '%s'! (%s)\n",
176 ccache_name, error_message(k5ret)));
177 status = NT_STATUS_INTERNAL_ERROR;
181 /* TODO: Should we enforce a enc_types list ?
182 ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
189 TALLOC_FREE(gse_ctx);
193 static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
194 bool do_sign, bool do_seal,
195 const char *ccache_name,
199 const char *username,
200 const char *password,
201 uint32_t add_gss_c_flags,
202 struct gse_context **_gse_ctx)
204 struct gse_context *gse_ctx;
205 OM_uint32 gss_maj, gss_min;
206 gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
207 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
208 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
209 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
213 if (!server || !service) {
214 return NT_STATUS_INVALID_PARAMETER;
217 status = gse_context_init(mem_ctx, do_sign, do_seal,
218 ccache_name, add_gss_c_flags,
220 if (!NT_STATUS_IS_OK(status)) {
221 return NT_STATUS_NO_MEMORY;
224 /* Guess the realm based on the supplied service, and avoid the GSS libs
225 doing DNS lookups which may fail.
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
232 smb_krb5_get_principal_from_service_hostname(gse_ctx,
236 if (!name_buffer.value) {
237 status = NT_STATUS_NO_MEMORY;
240 name_buffer.length = strlen((char *)name_buffer.value);
241 gss_maj = gss_import_name(&gss_min, &name_buffer,
243 &gse_ctx->server_name);
245 DEBUG(5, ("gss_import_name failed for %s, with [%s]\n",
246 (char *)name_buffer.value,
247 gse_errstr(gse_ctx, gss_maj, gss_min)));
248 status = NT_STATUS_INTERNAL_ERROR;
252 /* TODO: get krb5 ticket using username/password, if no valid
253 * one already available in ccache */
255 gss_maj = smb_gss_krb5_import_cred(&gss_min,
258 NULL, /* keytab_principal */
265 kret = krb5_cc_get_full_name(gse_ctx->k5ctx,
272 DEBUG(5, ("smb_gss_krb5_import_cred ccache[%s] failed with [%s] -"
273 "the caller may retry after a kinit.\n",
274 ccache, gse_errstr(gse_ctx, gss_maj, gss_min)));
276 status = NT_STATUS_INTERNAL_ERROR;
280 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
282 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
284 * This allows us to disable SIGN and SEAL for
285 * AUTH_LEVEL_CONNECT and AUTH_LEVEL_INTEGRITY.
287 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
288 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
290 gss_maj = gss_set_cred_option(&gss_min, &gse_ctx->creds,
294 DEBUG(0, ("gss_set_cred_option(GSS_KRB5_CRED_NO_CI_FLAGS_X), "
295 "failed with [%s]\n",
296 gse_errstr(gse_ctx, gss_maj, gss_min)));
297 status = NT_STATUS_INTERNAL_ERROR;
303 TALLOC_FREE(name_buffer.value);
307 TALLOC_FREE(name_buffer.value);
308 TALLOC_FREE(gse_ctx);
312 static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
313 struct gse_context *gse_ctx,
314 const DATA_BLOB *token_in,
315 DATA_BLOB *token_out)
317 OM_uint32 gss_maj, gss_min;
318 gss_buffer_desc in_data;
319 gss_buffer_desc out_data;
320 DATA_BLOB blob = data_blob_null;
322 OM_uint32 time_rec = 0;
325 in_data.value = token_in->data;
326 in_data.length = token_in->length;
328 gss_maj = gss_init_sec_context(&gss_min,
330 &gse_ctx->gssapi_context,
331 gse_ctx->server_name,
333 gse_ctx->gss_want_flags,
334 0, GSS_C_NO_CHANNEL_BINDINGS,
335 &in_data, NULL, &out_data,
336 &gse_ctx->gss_got_flags, &time_rec);
339 /* we are done with it */
340 tv = timeval_current_ofs(time_rec, 0);
341 gse_ctx->expire_time = timeval_to_nttime(&tv);
343 status = NT_STATUS_OK;
345 case GSS_S_CONTINUE_NEEDED:
346 /* we will need a third leg */
347 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
349 case GSS_S_CONTEXT_EXPIRED:
350 /* Make SPNEGO ignore us, we can't go any further here */
351 DBG_NOTICE("Context expired\n");
352 status = NT_STATUS_INVALID_PARAMETER;
356 case (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
357 DBG_NOTICE("Server principal not found\n");
358 /* Make SPNEGO ignore us, we can't go any further here */
359 status = NT_STATUS_INVALID_PARAMETER;
361 case (OM_uint32)KRB5KRB_AP_ERR_TKT_EXPIRED:
362 DBG_NOTICE("Ticket expired\n");
363 /* Make SPNEGO ignore us, we can't go any further here */
364 status = NT_STATUS_INVALID_PARAMETER;
366 case (OM_uint32)KRB5KRB_AP_ERR_TKT_NYV:
367 DBG_NOTICE("Clockskew\n");
368 /* Make SPNEGO ignore us, we can't go any further here */
369 status = NT_STATUS_TIME_DIFFERENCE_AT_DC;
371 case (OM_uint32)KRB5_KDC_UNREACH:
372 DBG_NOTICE("KDC unreachable\n");
373 /* Make SPNEGO ignore us, we can't go any further here */
374 status = NT_STATUS_NO_LOGON_SERVERS;
376 case (OM_uint32)KRB5KRB_AP_ERR_MSG_TYPE:
377 /* Garbage input, possibly from the auto-mech detection */
378 status = NT_STATUS_INVALID_PARAMETER;
381 DBG_ERR("gss_init_sec_context failed with [%s](%u)\n",
382 gse_errstr(talloc_tos(), gss_maj, gss_min),
384 status = NT_STATUS_LOGON_FAILURE;
389 DBG_ERR("gss_init_sec_context failed with [%s]\n",
390 gse_errstr(talloc_tos(), gss_maj, gss_min));
391 status = NT_STATUS_INTERNAL_ERROR;
395 /* we may be told to return nothing */
396 if (out_data.length) {
397 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
399 status = NT_STATUS_NO_MEMORY;
402 gss_maj = gss_release_buffer(&gss_min, &out_data);
410 static NTSTATUS gse_init_server(TALLOC_CTX *mem_ctx,
411 bool do_sign, bool do_seal,
412 uint32_t add_gss_c_flags,
413 struct gse_context **_gse_ctx)
415 struct gse_context *gse_ctx;
416 OM_uint32 gss_maj, gss_min;
420 status = gse_context_init(mem_ctx, do_sign, do_seal,
421 NULL, add_gss_c_flags, &gse_ctx);
422 if (!NT_STATUS_IS_OK(status)) {
423 return NT_STATUS_NO_MEMORY;
426 ret = gse_krb5_get_server_keytab(gse_ctx->k5ctx,
429 status = NT_STATUS_INTERNAL_ERROR;
433 /* This creates a GSSAPI cred_id_t with the keytab set */
434 gss_maj = smb_gss_krb5_import_cred(&gss_min, gse_ctx->k5ctx,
435 NULL, NULL, gse_ctx->keytab,
439 DEBUG(0, ("smb_gss_krb5_import_cred failed with [%s]\n",
440 gse_errstr(gse_ctx, gss_maj, gss_min)));
441 status = NT_STATUS_INTERNAL_ERROR;
445 status = NT_STATUS_OK;
448 if (!NT_STATUS_IS_OK(status)) {
449 TALLOC_FREE(gse_ctx);
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)
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;
466 OM_uint32 time_rec = 0;
469 in_data.value = token_in->data;
470 in_data.length = token_in->length;
472 gss_maj = gss_accept_sec_context(&gss_min,
473 &gse_ctx->gssapi_context,
476 GSS_C_NO_CHANNEL_BINDINGS,
477 &gse_ctx->client_name,
480 &gse_ctx->gss_got_flags,
482 &gse_ctx->delegated_cred_handle);
485 /* we are done with it */
486 tv = timeval_current_ofs(time_rec, 0);
487 gse_ctx->expire_time = timeval_to_nttime(&tv);
489 status = NT_STATUS_OK;
491 case GSS_S_CONTINUE_NEEDED:
492 /* we will need a third leg */
493 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
496 DEBUG(1, ("gss_accept_sec_context failed with [%s]\n",
497 gse_errstr(talloc_tos(), gss_maj, gss_min)));
499 if (gse_ctx->gssapi_context) {
500 gss_delete_sec_context(&gss_min,
501 &gse_ctx->gssapi_context,
506 * If we got an output token, make Windows aware of it
507 * by telling it that more processing is needed
509 if (out_data.length > 0) {
510 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
511 /* Fall through to handle the out token */
513 status = NT_STATUS_LOGON_FAILURE;
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);
522 status = NT_STATUS_NO_MEMORY;
524 gss_maj = gss_release_buffer(&gss_min, &out_data);
533 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
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;
542 ZERO_STRUCT(msg_min);
543 ZERO_STRUCT(msg_maj);
545 gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
546 GSS_C_NO_OID, &msg_ctx, &msg_maj);
550 errstr = talloc_strndup(mem_ctx,
551 (char *)msg_maj.value,
556 gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
557 (gss_OID)discard_const(gss_mech_krb5),
563 errstr = talloc_strdup_append_buffer(errstr, ": ");
567 errstr = talloc_strndup_append_buffer(errstr,
568 (char *)msg_min.value,
576 gss_maj = gss_release_buffer(&gss_min, &msg_min);
579 gss_maj = gss_release_buffer(&gss_min, &msg_maj);
584 static NTSTATUS gensec_gse_client_start(struct gensec_security *gensec_security)
586 struct gse_context *gse_ctx;
587 struct cli_credentials *creds = gensec_get_credentials(gensec_security);
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 const char *realm = cli_credentials_get_realm(creds);
598 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
599 return NT_STATUS_INVALID_PARAMETER;
601 if (is_ipaddress(hostname)) {
602 DEBUG(2, ("Cannot do GSE to an IP address\n"));
603 return NT_STATUS_INVALID_PARAMETER;
605 if (strcmp(hostname, "localhost") == 0) {
606 DEBUG(2, ("GSE to 'localhost' does not make sense\n"));
607 return NT_STATUS_INVALID_PARAMETER;
610 if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
613 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
616 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
619 if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
620 want_flags |= GSS_C_DCE_STYLE;
623 nt_status = gse_init_client(gensec_security, do_sign, do_seal, NULL,
624 hostname, service, realm,
625 username, password, want_flags,
627 if (!NT_STATUS_IS_OK(nt_status)) {
630 gensec_security->private_data = gse_ctx;
634 static NTSTATUS gensec_gse_server_start(struct gensec_security *gensec_security)
636 struct gse_context *gse_ctx;
638 OM_uint32 want_flags = 0;
639 bool do_sign = false, do_seal = false;
641 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
644 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
647 if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
648 want_flags |= GSS_C_DCE_STYLE;
651 nt_status = gse_init_server(gensec_security, do_sign, do_seal, want_flags,
653 if (!NT_STATUS_IS_OK(nt_status)) {
656 gensec_security->private_data = gse_ctx;
661 * Next state function for the GSE GENSEC mechanism
663 * @param gensec_gse_state GSE State
664 * @param mem_ctx The TALLOC_CTX for *out to be allocated on
665 * @param in The request, as a DATA_BLOB
666 * @param out The reply, as an talloc()ed DATA_BLOB, on *mem_ctx
667 * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
668 * or NT_STATUS_OK if the user is authenticated.
671 static NTSTATUS gensec_gse_update(struct gensec_security *gensec_security,
673 struct tevent_context *ev,
674 const DATA_BLOB in, DATA_BLOB *out)
677 struct gse_context *gse_ctx =
678 talloc_get_type_abort(gensec_security->private_data,
681 switch (gensec_security->gensec_role) {
683 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
687 status = gse_get_server_auth_token(mem_ctx, gse_ctx,
691 if (!NT_STATUS_IS_OK(status)) {
698 static NTSTATUS gensec_gse_wrap(struct gensec_security *gensec_security,
703 struct gse_context *gse_ctx =
704 talloc_get_type_abort(gensec_security->private_data,
706 OM_uint32 maj_stat, min_stat;
707 gss_buffer_desc input_token, output_token;
709 input_token.length = in->length;
710 input_token.value = in->data;
712 maj_stat = gss_wrap(&min_stat,
713 gse_ctx->gssapi_context,
714 gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
719 if (GSS_ERROR(maj_stat)) {
720 DEBUG(0, ("gensec_gse_wrap: GSS Wrap failed: %s\n",
721 gse_errstr(talloc_tos(), maj_stat, min_stat)));
722 return NT_STATUS_ACCESS_DENIED;
725 *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
726 gss_release_buffer(&min_stat, &output_token);
728 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
730 return NT_STATUS_ACCESS_DENIED;
735 static NTSTATUS gensec_gse_unwrap(struct gensec_security *gensec_security,
740 struct gse_context *gse_ctx =
741 talloc_get_type_abort(gensec_security->private_data,
743 OM_uint32 maj_stat, min_stat;
744 gss_buffer_desc input_token, output_token;
747 input_token.length = in->length;
748 input_token.value = in->data;
750 maj_stat = gss_unwrap(&min_stat,
751 gse_ctx->gssapi_context,
756 if (GSS_ERROR(maj_stat)) {
757 DEBUG(0, ("gensec_gse_unwrap: GSS UnWrap failed: %s\n",
758 gse_errstr(talloc_tos(), maj_stat, min_stat)));
759 return NT_STATUS_ACCESS_DENIED;
762 *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
763 gss_release_buffer(&min_stat, &output_token);
765 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
767 return NT_STATUS_ACCESS_DENIED;
772 static NTSTATUS gensec_gse_seal_packet(struct gensec_security *gensec_security,
774 uint8_t *data, size_t length,
775 const uint8_t *whole_pdu, size_t pdu_length,
778 struct gse_context *gse_ctx =
779 talloc_get_type_abort(gensec_security->private_data,
781 bool hdr_signing = false;
785 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
789 sig_size = gensec_gse_sig_size(gensec_security, length);
791 status = gssapi_seal_packet(gse_ctx->gssapi_context,
793 hdr_signing, sig_size,
795 whole_pdu, pdu_length,
797 if (!NT_STATUS_IS_OK(status)) {
798 DEBUG(0, ("gssapi_seal_packet(hdr_signing=%u,sig_size=%zu,"
799 "data=%zu,pdu=%zu) failed: %s\n",
800 hdr_signing, sig_size, length, pdu_length,
808 static NTSTATUS gensec_gse_unseal_packet(struct gensec_security *gensec_security,
809 uint8_t *data, size_t length,
810 const uint8_t *whole_pdu, size_t pdu_length,
811 const DATA_BLOB *sig)
813 struct gse_context *gse_ctx =
814 talloc_get_type_abort(gensec_security->private_data,
816 bool hdr_signing = false;
819 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
823 status = gssapi_unseal_packet(gse_ctx->gssapi_context,
827 whole_pdu, pdu_length,
829 if (!NT_STATUS_IS_OK(status)) {
830 DEBUG(0, ("gssapi_unseal_packet(hdr_signing=%u,sig_size=%zu,"
831 "data=%zu,pdu=%zu) failed: %s\n",
832 hdr_signing, sig->length, length, pdu_length,
840 static NTSTATUS gensec_gse_sign_packet(struct gensec_security *gensec_security,
842 const uint8_t *data, size_t length,
843 const uint8_t *whole_pdu, size_t pdu_length,
846 struct gse_context *gse_ctx =
847 talloc_get_type_abort(gensec_security->private_data,
849 bool hdr_signing = false;
852 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
856 status = gssapi_sign_packet(gse_ctx->gssapi_context,
860 whole_pdu, pdu_length,
862 if (!NT_STATUS_IS_OK(status)) {
863 DEBUG(0, ("gssapi_sign_packet(hdr_signing=%u,"
864 "data=%zu,pdu=%zu) failed: %s\n",
865 hdr_signing, length, pdu_length,
873 static NTSTATUS gensec_gse_check_packet(struct gensec_security *gensec_security,
874 const uint8_t *data, size_t length,
875 const uint8_t *whole_pdu, size_t pdu_length,
876 const DATA_BLOB *sig)
878 struct gse_context *gse_ctx =
879 talloc_get_type_abort(gensec_security->private_data,
881 bool hdr_signing = false;
884 if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
888 status = gssapi_check_packet(gse_ctx->gssapi_context,
892 whole_pdu, pdu_length,
894 if (!NT_STATUS_IS_OK(status)) {
895 DEBUG(0, ("gssapi_check_packet(hdr_signing=%u,sig_size=%zu"
896 "data=%zu,pdu=%zu) failed: %s\n",
897 hdr_signing, sig->length, length, pdu_length,
905 /* Try to figure out what features we actually got on the connection */
906 static bool gensec_gse_have_feature(struct gensec_security *gensec_security,
909 struct gse_context *gse_ctx =
910 talloc_get_type_abort(gensec_security->private_data,
913 if (feature & GENSEC_FEATURE_SESSION_KEY) {
914 return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
916 if (feature & GENSEC_FEATURE_SIGN) {
917 return gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG;
919 if (feature & GENSEC_FEATURE_SEAL) {
920 return gse_ctx->gss_got_flags & GSS_C_CONF_FLAG;
922 if (feature & GENSEC_FEATURE_DCE_STYLE) {
923 return gse_ctx->gss_got_flags & GSS_C_DCE_STYLE;
925 if (feature & GENSEC_FEATURE_NEW_SPNEGO) {
929 if (!(gse_ctx->gss_got_flags & GSS_C_INTEG_FLAG)) {
933 status = gssapi_get_session_key(talloc_tos(),
934 gse_ctx->gssapi_context, NULL, &keytype);
936 * We should do a proper sig on the mechListMic unless
937 * we know we have to be backwards compatible with
938 * earlier windows versions.
940 * Negotiating a non-krb5
941 * mech for example should be regarded as having
944 if (NT_STATUS_IS_OK(status)) {
946 case ENCTYPE_DES_CBC_CRC:
947 case ENCTYPE_DES_CBC_MD5:
948 case ENCTYPE_ARCFOUR_HMAC:
949 case ENCTYPE_DES3_CBC_SHA1:
955 /* We can always do async (rather than strict request/reply) packets. */
956 if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
959 if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
960 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
964 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
973 static NTTIME gensec_gse_expire_time(struct gensec_security *gensec_security)
975 struct gse_context *gse_ctx =
976 talloc_get_type_abort(gensec_security->private_data,
979 return gse_ctx->expire_time;
983 * Extract the 'sesssion key' needed by SMB signing and ncacn_np
984 * (for encrypting some passwords).
986 * This breaks all the abstractions, but what do you expect...
988 static NTSTATUS gensec_gse_session_key(struct gensec_security *gensec_security,
990 DATA_BLOB *session_key)
992 struct gse_context *gse_ctx =
993 talloc_get_type_abort(gensec_security->private_data,
996 return gssapi_get_session_key(mem_ctx, gse_ctx->gssapi_context, session_key, NULL);
999 /* Get some basic (and authorization) information about the user on
1000 * this session. This uses either the PAC (if present) or a local
1001 * database lookup */
1002 static NTSTATUS gensec_gse_session_info(struct gensec_security *gensec_security,
1003 TALLOC_CTX *mem_ctx,
1004 struct auth_session_info **_session_info)
1006 struct gse_context *gse_ctx =
1007 talloc_get_type_abort(gensec_security->private_data,
1008 struct gse_context);
1010 TALLOC_CTX *tmp_ctx;
1011 struct auth_session_info *session_info = NULL;
1012 OM_uint32 maj_stat, min_stat;
1013 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
1015 gss_buffer_desc name_token;
1016 char *principal_string;
1018 tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gse_session_info context");
1019 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
1021 maj_stat = gss_display_name(&min_stat,
1022 gse_ctx->client_name,
1025 if (GSS_ERROR(maj_stat)) {
1026 DEBUG(1, ("GSS display_name failed: %s\n",
1027 gse_errstr(talloc_tos(), maj_stat, min_stat)));
1028 talloc_free(tmp_ctx);
1029 return NT_STATUS_FOOBAR;
1032 principal_string = talloc_strndup(tmp_ctx,
1033 (const char *)name_token.value,
1036 gss_release_buffer(&min_stat, &name_token);
1038 if (!principal_string) {
1039 talloc_free(tmp_ctx);
1040 return NT_STATUS_NO_MEMORY;
1043 nt_status = gssapi_obtain_pac_blob(tmp_ctx, gse_ctx->gssapi_context,
1044 gse_ctx->client_name,
1047 /* IF we have the PAC - otherwise we need to get this
1048 * data from elsewere
1050 if (NT_STATUS_IS_OK(nt_status)) {
1051 pac_blob_ptr = &pac_blob;
1053 nt_status = gensec_generate_session_info_pac(tmp_ctx,
1056 pac_blob_ptr, principal_string,
1057 gensec_get_remote_address(gensec_security),
1059 if (!NT_STATUS_IS_OK(nt_status)) {
1060 talloc_free(tmp_ctx);
1064 nt_status = gensec_gse_session_key(gensec_security, session_info,
1065 &session_info->session_key);
1066 if (!NT_STATUS_IS_OK(nt_status)) {
1067 talloc_free(tmp_ctx);
1071 *_session_info = talloc_move(mem_ctx, &session_info);
1072 talloc_free(tmp_ctx);
1074 return NT_STATUS_OK;
1077 static size_t gensec_gse_max_input_size(struct gensec_security *gensec_security)
1079 struct gse_context *gse_ctx =
1080 talloc_get_type_abort(gensec_security->private_data,
1081 struct gse_context);
1082 OM_uint32 maj_stat, min_stat;
1083 OM_uint32 max_input_size;
1085 maj_stat = gss_wrap_size_limit(&min_stat,
1086 gse_ctx->gssapi_context,
1087 gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
1089 gse_ctx->max_wrap_buf_size,
1091 if (GSS_ERROR(maj_stat)) {
1092 TALLOC_CTX *mem_ctx = talloc_new(NULL);
1093 DEBUG(1, ("gensec_gssapi_max_input_size: determining signature size with gss_wrap_size_limit failed: %s\n",
1094 gse_errstr(mem_ctx, maj_stat, min_stat)));
1095 talloc_free(mem_ctx);
1099 return max_input_size;
1102 /* Find out the maximum output size negotiated on this connection */
1103 static size_t gensec_gse_max_wrapped_size(struct gensec_security *gensec_security)
1105 struct gse_context *gse_ctx =
1106 talloc_get_type_abort(gensec_security->private_data,
1107 struct gse_context);
1108 return gse_ctx->max_wrap_buf_size;
1111 static size_t gensec_gse_sig_size(struct gensec_security *gensec_security,
1114 struct gse_context *gse_ctx =
1115 talloc_get_type_abort(gensec_security->private_data,
1116 struct gse_context);
1118 if (gse_ctx->sig_size > 0) {
1119 return gse_ctx->sig_size;
1122 gse_ctx->sig_size = gssapi_get_sig_size(gse_ctx->gssapi_context,
1124 gse_ctx->gss_got_flags,
1126 return gse_ctx->sig_size;
1129 static const char *gensec_gse_krb5_oids[] = {
1130 GENSEC_OID_KERBEROS5_OLD,
1131 GENSEC_OID_KERBEROS5,
1135 const struct gensec_security_ops gensec_gse_krb5_security_ops = {
1137 .auth_type = DCERPC_AUTH_TYPE_KRB5,
1138 .oid = gensec_gse_krb5_oids,
1139 .client_start = gensec_gse_client_start,
1140 .server_start = gensec_gse_server_start,
1141 .magic = gensec_magic_check_krb5_oid,
1142 .update = gensec_gse_update,
1143 .session_key = gensec_gse_session_key,
1144 .session_info = gensec_gse_session_info,
1145 .sig_size = gensec_gse_sig_size,
1146 .sign_packet = gensec_gse_sign_packet,
1147 .check_packet = gensec_gse_check_packet,
1148 .seal_packet = gensec_gse_seal_packet,
1149 .unseal_packet = gensec_gse_unseal_packet,
1150 .max_input_size = gensec_gse_max_input_size,
1151 .max_wrapped_size = gensec_gse_max_wrapped_size,
1152 .wrap = gensec_gse_wrap,
1153 .unwrap = gensec_gse_unwrap,
1154 .have_feature = gensec_gse_have_feature,
1155 .expire_time = gensec_gse_expire_time,
1158 .priority = GENSEC_GSSAPI
1161 #endif /* HAVE_KRB5 */