📄 gensec_gssapi.c
字号:
/* Unix SMB/CIFS implementation. Kerberos backend for GENSEC Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005 Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.*/#include "includes.h"#include "lib/events/events.h"#include "system/kerberos.h"#include "heimdal/lib/gssapi/gssapi/gssapi.h"#include "auth/kerberos/kerberos.h"#include "librpc/gen_ndr/krb5pac.h"#include "auth/auth.h"#include "lib/ldb/include/ldb.h"#include "auth/auth_sam.h"#include "librpc/rpc/dcerpc.h"#include "auth/credentials/credentials.h"#include "auth/credentials/credentials_krb5.h"#include "auth/gensec/gensec.h"#include "auth/gensec/gensec_proto.h"#include "param/param.h"#include "auth/session_proto.h"enum gensec_gssapi_sasl_state { STAGE_GSS_NEG, STAGE_SASL_SSF_NEG, STAGE_SASL_SSF_ACCEPT, STAGE_DONE};#define NEG_SEAL 0x4#define NEG_SIGN 0x2#define NEG_NONE 0x1struct gensec_gssapi_state { gss_ctx_id_t gssapi_context; struct gss_channel_bindings_struct *input_chan_bindings; gss_name_t server_name; gss_name_t client_name; OM_uint32 want_flags, got_flags; gss_OID gss_oid; DATA_BLOB session_key; DATA_BLOB pac; struct smb_krb5_context *smb_krb5_context; struct gssapi_creds_container *client_cred; struct gssapi_creds_container *server_cred; gss_cred_id_t delegated_cred_handle; bool sasl; /* We have two different mechs in this file: One * for SASL wrapped GSSAPI and another for normal * GSSAPI */ enum gensec_gssapi_sasl_state sasl_state; uint8_t sasl_protection; /* What was negotiated at the SASL * layer, independent of the GSSAPI * layer... */ size_t max_wrap_buf_size; int gss_exchange_count;};static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security);static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security);static char *gssapi_error_string(TALLOC_CTX *mem_ctx, OM_uint32 maj_stat, OM_uint32 min_stat, const gss_OID mech){ OM_uint32 disp_min_stat, disp_maj_stat; gss_buffer_desc maj_error_message; gss_buffer_desc min_error_message; char *maj_error_string, *min_error_string; OM_uint32 msg_ctx = 0; char *ret; maj_error_message.value = NULL; min_error_message.value = NULL; maj_error_message.length = 0; min_error_message.length = 0; disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE, mech, &msg_ctx, &maj_error_message); disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE, mech, &msg_ctx, &min_error_message); maj_error_string = talloc_strndup(mem_ctx, (char *)maj_error_message.value, maj_error_message.length); min_error_string = talloc_strndup(mem_ctx, (char *)min_error_message.value, min_error_message.length); ret = talloc_asprintf(mem_ctx, "%s: %s", maj_error_string, min_error_string); talloc_free(maj_error_string); talloc_free(min_error_string); gss_release_buffer(&disp_min_stat, &maj_error_message); gss_release_buffer(&disp_min_stat, &min_error_message); return ret;}static int gensec_gssapi_destructor(struct gensec_gssapi_state *gensec_gssapi_state){ OM_uint32 maj_stat, min_stat; if (gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { maj_stat = gss_release_cred(&min_stat, &gensec_gssapi_state->delegated_cred_handle); } if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) { maj_stat = gss_delete_sec_context (&min_stat, &gensec_gssapi_state->gssapi_context, GSS_C_NO_BUFFER); } if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) { maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name); } if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) { maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name); } return 0;}static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security){ struct gensec_gssapi_state *gensec_gssapi_state; krb5_error_code ret; struct gsskrb5_send_to_kdc send_to_kdc; gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state); if (!gensec_gssapi_state) { return NT_STATUS_NO_MEMORY; } gensec_gssapi_state->gss_exchange_count = 0; gensec_gssapi_state->max_wrap_buf_size = lp_parm_int(gensec_security->lp_ctx, NULL, "gensec_gssapi", "max wrap buf size", 65536); gensec_gssapi_state->sasl = false; gensec_gssapi_state->sasl_state = STAGE_GSS_NEG; gensec_security->private_data = gensec_gssapi_state; gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT; gensec_gssapi_state->server_name = GSS_C_NO_NAME; gensec_gssapi_state->client_name = GSS_C_NO_NAME; /* TODO: Fill in channel bindings */ gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; gensec_gssapi_state->want_flags = 0; if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "mutual", true)) { gensec_gssapi_state->want_flags |= GSS_C_MUTUAL_FLAG; } if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "delegation", true)) { gensec_gssapi_state->want_flags |= GSS_C_DELEG_FLAG; } if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "replay", true)) { gensec_gssapi_state->want_flags |= GSS_C_REPLAY_FLAG; } if (lp_parm_bool(gensec_security->lp_ctx, NULL, "gensec_gssapi", "sequence", true)) { gensec_gssapi_state->want_flags |= GSS_C_SEQUENCE_FLAG; } gensec_gssapi_state->got_flags = 0; gensec_gssapi_state->session_key = data_blob(NULL, 0); gensec_gssapi_state->pac = data_blob(NULL, 0); gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL; talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destructor); if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG; } if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG; } if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) { gensec_gssapi_state->want_flags |= GSS_C_DCE_STYLE; } gensec_gssapi_state->gss_oid = GSS_C_NULL_OID; send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = gensec_security->event_ctx; ret = gsskrb5_set_send_to_kdc(&send_to_kdc); if (ret) { DEBUG(1,("gensec_krb5_start: gsskrb5_set_send_to_kdc failed\n")); talloc_free(gensec_gssapi_state); return NT_STATUS_INTERNAL_ERROR; } if (lp_realm(gensec_security->lp_ctx) && *lp_realm(gensec_security->lp_ctx)) { char *upper_realm = strupper_talloc(gensec_gssapi_state, lp_realm(gensec_security->lp_ctx)); if (!upper_realm) { DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm(gensec_security->lp_ctx))); talloc_free(gensec_gssapi_state); return NT_STATUS_NO_MEMORY; } ret = gsskrb5_set_default_realm(upper_realm); talloc_free(upper_realm); if (ret) { DEBUG(1,("gensec_krb5_start: gsskrb5_set_default_realm failed\n")); talloc_free(gensec_gssapi_state); return NT_STATUS_INTERNAL_ERROR; } } /* don't do DNS lookups of any kind, it might/will fail for a netbios name */ ret = gsskrb5_set_dns_canonicalize(lp_parm_bool(gensec_security->lp_ctx, NULL, "krb5", "set_dns_canonicalize", false)); if (ret) { DEBUG(1,("gensec_krb5_start: gsskrb5_set_dns_canonicalize failed\n")); talloc_free(gensec_gssapi_state); return NT_STATUS_INTERNAL_ERROR; } ret = smb_krb5_init_context(gensec_gssapi_state, gensec_security->event_ctx, gensec_security->lp_ctx, &gensec_gssapi_state->smb_krb5_context); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret))); talloc_free(gensec_gssapi_state); return NT_STATUS_INTERNAL_ERROR; } return NT_STATUS_OK;}static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security){ NTSTATUS nt_status; int ret; struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *machine_account; struct gssapi_creds_container *gcc; nt_status = gensec_gssapi_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); machine_account = gensec_get_credentials(gensec_security); if (!machine_account) { DEBUG(3, ("No machine account credentials specified\n")); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } else { ret = cli_credentials_get_server_gss_creds(machine_account, gensec_security->event_ctx, gensec_security->lp_ctx, &gcc); if (ret) { DEBUG(1, ("Aquiring acceptor credentials failed: %s\n", error_message(ret))); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } } gensec_gssapi_state->server_cred = gcc; return NT_STATUS_OK;}static NTSTATUS gensec_gssapi_sasl_server_start(struct gensec_security *gensec_security){ NTSTATUS nt_status; struct gensec_gssapi_state *gensec_gssapi_state; nt_status = gensec_gssapi_server_start(gensec_security); if (NT_STATUS_IS_OK(nt_status)) { gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); gensec_gssapi_state->sasl = true; } return nt_status;}static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security){ struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *creds = gensec_get_credentials(gensec_security); krb5_error_code ret; NTSTATUS nt_status; gss_buffer_desc name_token; gss_OID name_type; OM_uint32 maj_stat, min_stat; const char *hostname = gensec_get_target_hostname(gensec_security); const char *principal; struct gssapi_creds_container *gcc; if (!hostname) { DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); return NT_STATUS_INVALID_PARAMETER; } if (is_ipaddress(hostname)) { DEBUG(2, ("Cannot do GSSAPI to an IP address\n")); return NT_STATUS_INVALID_PARAMETER; } if (strcmp(hostname, "localhost") == 0) { DEBUG(2, ("GSSAPI to 'localhost' does not make sense\n")); return NT_STATUS_INVALID_PARAMETER; } nt_status = gensec_gssapi_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); gensec_gssapi_state->gss_oid = gss_mech_krb5; principal = gensec_get_target_principal(gensec_security); if (principal && lp_client_use_spnego_principal(gensec_security->lp_ctx)) { name_type = GSS_C_NULL_OID; } else { principal = talloc_asprintf(gensec_gssapi_state, "%s@%s", gensec_get_target_service(gensec_security), hostname); name_type = GSS_C_NT_HOSTBASED_SERVICE; } name_token.value = discard_const_p(uint8_t, principal); name_token.length = strlen(principal); maj_stat = gss_import_name (&min_stat, &name_token, name_type, &gensec_gssapi_state->server_name); if (maj_stat) { DEBUG(2, ("GSS Import name of %s failed: %s\n", (char *)name_token.value, gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; } ret = cli_credentials_get_client_gss_creds(creds, gensec_security->event_ctx, gensec_security->lp_ctx, &gcc); switch (ret) { case 0: break; case KRB5KDC_ERR_PREAUTH_FAILED: return NT_STATUS_LOGON_FAILURE; case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact %s\n", principal)); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ default:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -