mirror of
https://github.com/samba-team/samba.git
synced 2025-03-30 06:50:24 +03:00
s3-libcli Change krb5 smb sealing to call via gensec and gensec_gse
This also fixes the support for smb sealing with krb5 in make test, as this now relies on secrets.tdb rather than /etc/krb5.keytab. Andrew Bartlett Signed-off-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
30b1e72556
commit
06f7105490
@ -167,150 +167,6 @@ static NTSTATUS common_gensec_encrypt_buffer(struct gensec_security *gensec,
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Generic code for client and server.
|
||||
gss-api decrypt an incoming buffer. We insist that the size of the
|
||||
unwrapped buffer must be smaller or identical to the incoming buffer.
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
static NTSTATUS common_gss_decrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf)
|
||||
{
|
||||
gss_ctx_id_t gss_ctx = gss_state->gss_ctx;
|
||||
OM_uint32 ret = 0;
|
||||
OM_uint32 minor = 0;
|
||||
int flags_got = 0;
|
||||
gss_buffer_desc in_buf, out_buf;
|
||||
size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */
|
||||
|
||||
if (buf_len < 8) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
in_buf.value = buf + 8;
|
||||
in_buf.length = buf_len - 8;
|
||||
|
||||
ret = gss_unwrap(&minor,
|
||||
gss_ctx,
|
||||
&in_buf,
|
||||
&out_buf,
|
||||
&flags_got, /* did we get sign+seal ? */
|
||||
(gss_qop_t *) NULL);
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
NTSTATUS status = NT_STATUS_ACCESS_DENIED;
|
||||
char *gss_err;
|
||||
|
||||
gss_err = gssapi_error_string(talloc_tos(),
|
||||
ret, minor,
|
||||
GSS_C_NULL_OID);
|
||||
DEBUG(0,("common_gss_decrypt_buffer: gss_unwrap failed. "
|
||||
"Error [%d/%d] - %s - %s\n",
|
||||
ret, minor, nt_errstr(status),
|
||||
gss_err ? gss_err : "<unknown>"));
|
||||
talloc_free(gss_err);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
if (out_buf.length > in_buf.length) {
|
||||
DEBUG(0,("common_gss_decrypt_buffer: gss_unwrap size (%u) too large (%u) !\n",
|
||||
(unsigned int)out_buf.length,
|
||||
(unsigned int)in_buf.length ));
|
||||
gss_release_buffer(&minor, &out_buf);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
memcpy(buf + 8, out_buf.value, out_buf.length);
|
||||
/* Reset the length and overwrite the header. */
|
||||
smb_setlen_nbt(buf, out_buf.length + 4);
|
||||
|
||||
gss_release_buffer(&minor, &out_buf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Generic code for client and server.
|
||||
gss-api encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out.
|
||||
******************************************************************************/
|
||||
|
||||
static NTSTATUS common_gss_encrypt_buffer(struct smb_tran_enc_state_gss *gss_state,
|
||||
uint16_t enc_ctx_num,
|
||||
char *buf,
|
||||
char **ppbuf_out)
|
||||
{
|
||||
gss_ctx_id_t gss_ctx = gss_state->gss_ctx;
|
||||
OM_uint32 ret = 0;
|
||||
OM_uint32 minor = 0;
|
||||
int flags_got = 0;
|
||||
gss_buffer_desc in_buf, out_buf;
|
||||
size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */
|
||||
|
||||
*ppbuf_out = NULL;
|
||||
|
||||
if (buf_len < 8) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
in_buf.value = buf + 8;
|
||||
in_buf.length = buf_len - 8;
|
||||
|
||||
ret = gss_wrap(&minor,
|
||||
gss_ctx,
|
||||
true, /* we want sign+seal. */
|
||||
GSS_C_QOP_DEFAULT,
|
||||
&in_buf,
|
||||
&flags_got, /* did we get sign+seal ? */
|
||||
&out_buf);
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
NTSTATUS status = NT_STATUS_ACCESS_DENIED;
|
||||
char *gss_err;
|
||||
|
||||
gss_err = gssapi_error_string(talloc_tos(),
|
||||
ret, minor,
|
||||
GSS_C_NULL_OID);
|
||||
DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap failed. "
|
||||
"Error [%d/%d] - %s - %s\n",
|
||||
ret, minor, nt_errstr(status),
|
||||
gss_err ? gss_err : "<unknown>"));
|
||||
talloc_free(gss_err);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!flags_got) {
|
||||
/* Sign+seal not supported. */
|
||||
gss_release_buffer(&minor, &out_buf);
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* Ya see - this is why I *hate* gss-api. I don't
|
||||
* want to have to malloc another buffer of the
|
||||
* same size + 8 bytes just to get a continuous
|
||||
* header + buffer, but gss won't let me pass in
|
||||
* a pre-allocated buffer. Bastards (and you know
|
||||
* who you are....). I might fix this by
|
||||
* going to "encrypt_and_send" passing in a file
|
||||
* descriptor and doing scatter-gather write with
|
||||
* TCP cork on Linux. But I shouldn't have to
|
||||
* bother :-*(. JRA.
|
||||
*/
|
||||
|
||||
*ppbuf_out = (char *)malloc(out_buf.length + 8); /* We know this can't wrap. */
|
||||
if (!*ppbuf_out) {
|
||||
gss_release_buffer(&minor, &out_buf);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(*ppbuf_out+8, out_buf.value, out_buf.length);
|
||||
smb_set_enclen(*ppbuf_out, out_buf.length + 4, enc_ctx_num);
|
||||
|
||||
gss_release_buffer(&minor, &out_buf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
Generic code for client and server.
|
||||
Encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out.
|
||||
@ -324,16 +180,7 @@ NTSTATUS common_encrypt_buffer(struct smb_trans_enc_state *es, char *buffer, cha
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
switch (es->smb_enc_type) {
|
||||
case SMB_TRANS_ENC_NTLM:
|
||||
return common_gensec_encrypt_buffer(es->s.gensec_security, es->enc_ctx_num, buffer, buf_out);
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
case SMB_TRANS_ENC_GSS:
|
||||
return common_gss_encrypt_buffer(es->s.gss_state, es->enc_ctx_num, buffer, buf_out);
|
||||
#endif
|
||||
default:
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
return common_gensec_encrypt_buffer(es->s.gensec_security, es->enc_ctx_num, buffer, buf_out);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@ -349,38 +196,9 @@ NTSTATUS common_decrypt_buffer(struct smb_trans_enc_state *es, char *buf)
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
switch (es->smb_enc_type) {
|
||||
case SMB_TRANS_ENC_NTLM:
|
||||
return common_gensec_decrypt_buffer(es->s.gensec_security, buf);
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
case SMB_TRANS_ENC_GSS:
|
||||
return common_gss_decrypt_buffer(es->s.gss_state, buf);
|
||||
#endif
|
||||
default:
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
return common_gensec_decrypt_buffer(es->s.gensec_security, buf);
|
||||
}
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
/******************************************************************************
|
||||
Shutdown a gss encryption state.
|
||||
******************************************************************************/
|
||||
|
||||
static void common_free_gss_state(struct smb_tran_enc_state_gss **pp_gss_state)
|
||||
{
|
||||
OM_uint32 minor = 0;
|
||||
struct smb_tran_enc_state_gss *gss_state = *pp_gss_state;
|
||||
|
||||
if (gss_state->creds != GSS_C_NO_CREDENTIAL) {
|
||||
gss_release_cred(&minor, &gss_state->creds);
|
||||
}
|
||||
if (gss_state->gss_ctx != GSS_C_NO_CONTEXT) {
|
||||
gss_delete_sec_context(&minor, &gss_state->gss_ctx, NULL);
|
||||
}
|
||||
SAFE_FREE(*pp_gss_state);
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
Shutdown an encryption state.
|
||||
******************************************************************************/
|
||||
@ -393,19 +211,9 @@ void common_free_encryption_state(struct smb_trans_enc_state **pp_es)
|
||||
return;
|
||||
}
|
||||
|
||||
if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
|
||||
if (es->s.gensec_security) {
|
||||
TALLOC_FREE(es->s.gensec_security);
|
||||
}
|
||||
if (es->s.gensec_security) {
|
||||
TALLOC_FREE(es->s.gensec_security);
|
||||
}
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
if (es->smb_enc_type == SMB_TRANS_ENC_GSS) {
|
||||
/* Free the gss context handle. */
|
||||
if (es->s.gss_state) {
|
||||
common_free_gss_state(&es->s.gss_state);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
SAFE_FREE(es);
|
||||
*pp_es = NULL;
|
||||
}
|
||||
|
@ -49,9 +49,6 @@ struct smb_trans_enc_state {
|
||||
bool enc_on;
|
||||
union {
|
||||
struct gensec_security *gensec_security;
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
struct smb_tran_enc_state_gss *gss_state;
|
||||
#endif
|
||||
} s;
|
||||
};
|
||||
|
||||
|
@ -20,9 +20,6 @@
|
||||
^samba3.blackbox.rpcclient over ncacn_np with \[spnego,smb2,bigendian\]
|
||||
^samba3.blackbox.rpcclient over ncacn_np with \[spnego,connect,smb2\]
|
||||
^samba3.blackbox.rpcclient over ncacn_np with \[spnego,connect,smb2,bigendian\]
|
||||
# GSSAPI/krb5 encrypted CIFS fails in the test environment at the moment
|
||||
^samba3.blackbox.smbclient_krb5 -e.smbclient
|
||||
^samba3.blackbox.smbclient_krb5 old ccache -e.smbclient
|
||||
# these show that we still have some differences between our system
|
||||
# with our internal iconv because it passes except when we bypass our
|
||||
# internal iconv modules
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "auth_generic.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "../libcli/smb/smbXcli_base.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
/****************************************************************************
|
||||
Get UNIX extensions version info.
|
||||
@ -585,16 +586,6 @@ static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type sm
|
||||
ZERO_STRUCTP(es);
|
||||
es->smb_enc_type = smb_enc_type;
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
if (smb_enc_type == SMB_TRANS_ENC_GSS) {
|
||||
es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
|
||||
if (!es->s.gss_state) {
|
||||
SAFE_FREE(es);
|
||||
return NULL;
|
||||
}
|
||||
ZERO_STRUCTP(es->s.gss_state);
|
||||
}
|
||||
#endif
|
||||
return es;
|
||||
}
|
||||
|
||||
@ -683,99 +674,33 @@ NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli,
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
|
||||
#ifndef SMB_GSS_REQUIRED_FLAGS
|
||||
#define SMB_GSS_REQUIRED_FLAGS (GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
Get client gss blob to send to a server.
|
||||
******************************************************************************/
|
||||
|
||||
static NTSTATUS make_cli_gss_blob(TALLOC_CTX *ctx,
|
||||
struct smb_trans_enc_state *es,
|
||||
const char *service,
|
||||
const char *host,
|
||||
struct gensec_security *gensec_security,
|
||||
NTSTATUS status_in,
|
||||
DATA_BLOB spnego_blob_in,
|
||||
DATA_BLOB *p_blob_out)
|
||||
{
|
||||
const char *krb_mechs[] = {OID_KERBEROS5, NULL};
|
||||
OM_uint32 ret;
|
||||
OM_uint32 min;
|
||||
gss_name_t srv_name;
|
||||
gss_buffer_desc input_name;
|
||||
gss_buffer_desc *p_tok_in;
|
||||
gss_buffer_desc tok_out, tok_in;
|
||||
DATA_BLOB blob_out = data_blob_null;
|
||||
DATA_BLOB blob_in = data_blob_null;
|
||||
OM_uint32 ret_flags = 0;
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
|
||||
memset(&tok_out, '\0', sizeof(tok_out));
|
||||
|
||||
/* Guess the realm based on the supplied service, and avoid the GSS libs
|
||||
doing DNS lookups which may fail.
|
||||
|
||||
TODO: Loop with the KDC on some more combinations (local
|
||||
realm in particular), possibly falling back to
|
||||
GSS_C_NT_HOSTBASED_SERVICE
|
||||
*/
|
||||
input_name.value = kerberos_get_principal_from_service_hostname(talloc_tos(),
|
||||
service, host);
|
||||
if (!input_name.value) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
input_name.length = strlen((char *)input_name.value);
|
||||
ret = gss_import_name(&min, &input_name,
|
||||
GSS_C_NT_USER_NAME,
|
||||
&srv_name);
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
TALLOC_FREE(input_name.value);
|
||||
return map_nt_error_from_gss(ret, min);
|
||||
}
|
||||
|
||||
if (spnego_blob_in.length == 0) {
|
||||
p_tok_in = GSS_C_NO_BUFFER;
|
||||
blob_in = spnego_blob_in;
|
||||
} else {
|
||||
/* Remove the SPNEGO wrapper */
|
||||
if (!spnego_parse_auth_response(ctx, spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto fail;
|
||||
}
|
||||
tok_in.value = blob_in.data;
|
||||
tok_in.length = blob_in.length;
|
||||
p_tok_in = &tok_in;
|
||||
}
|
||||
|
||||
ret = gss_init_sec_context(&min,
|
||||
GSS_C_NO_CREDENTIAL, /* Use our default cred. */
|
||||
&es->s.gss_state->gss_ctx,
|
||||
srv_name,
|
||||
GSS_C_NO_OID, /* default OID. */
|
||||
GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG,
|
||||
GSS_C_INDEFINITE, /* requested ticket lifetime. */
|
||||
NULL, /* no channel bindings */
|
||||
p_tok_in,
|
||||
NULL, /* ignore mech type */
|
||||
&tok_out,
|
||||
&ret_flags,
|
||||
NULL); /* ignore time_rec */
|
||||
|
||||
status = map_nt_error_from_gss(ret, min);
|
||||
if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
|
||||
DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
|
||||
ads_errstr(adss)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
|
||||
status = NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
blob_out = data_blob_talloc(ctx, tok_out.value, tok_out.length);
|
||||
status = gensec_update(gensec_security, ctx,
|
||||
NULL, blob_in, &blob_out);
|
||||
|
||||
/* Wrap in an SPNEGO wrapper */
|
||||
*p_blob_out = spnego_gen_negTokenInit(ctx, krb_mechs, &blob_out, NULL);
|
||||
@ -784,11 +709,6 @@ static NTSTATUS make_cli_gss_blob(TALLOC_CTX *ctx,
|
||||
|
||||
data_blob_free(&blob_out);
|
||||
data_blob_free(&blob_in);
|
||||
TALLOC_FREE(input_name.value);
|
||||
gss_release_name(&min, &srv_name);
|
||||
if (tok_out.value) {
|
||||
gss_release_buffer(&min, &tok_out);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -802,16 +722,41 @@ NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
|
||||
DATA_BLOB blob_send = data_blob_null;
|
||||
DATA_BLOB param_out = data_blob_null;
|
||||
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
|
||||
fstring fqdn;
|
||||
const char *servicename;
|
||||
struct auth_generic_state *auth_generic_state;
|
||||
struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
|
||||
|
||||
if (!es) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
servicename = "cifs";
|
||||
status = make_cli_gss_blob(talloc_tos(), es, servicename, cli_state_remote_name(cli), NT_STATUS_OK, blob_recv, &blob_send);
|
||||
status = auth_generic_client_prepare(NULL,
|
||||
&auth_generic_state);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SESSION_KEY);
|
||||
gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
|
||||
|
||||
cli_credentials_set_kerberos_state(auth_generic_state->credentials,
|
||||
CRED_MUST_USE_KERBEROS);
|
||||
|
||||
status = gensec_set_target_service(auth_generic_state->gensec_security, "cifs");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = gensec_set_target_hostname(auth_generic_state->gensec_security,
|
||||
cli_state_remote_name(cli));
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status = auth_generic_client_start(auth_generic_state, GENSEC_OID_KERBEROS5))) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = make_cli_gss_blob(talloc_tos(), auth_generic_state->gensec_security, NT_STATUS_OK, blob_recv, &blob_send);
|
||||
do {
|
||||
data_blob_free(&blob_recv);
|
||||
status = enc_blob_send_receive(cli, &blob_send, &blob_recv, ¶m_out);
|
||||
@ -819,25 +764,34 @@ NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
|
||||
es->enc_ctx_num = SVAL(param_out.data, 0);
|
||||
}
|
||||
data_blob_free(&blob_send);
|
||||
status = make_cli_gss_blob(talloc_tos(), es, servicename, fqdn, status, blob_recv, &blob_send);
|
||||
status = make_cli_gss_blob(talloc_tos(), auth_generic_state->gensec_security, status, blob_recv, &blob_send);
|
||||
} while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
|
||||
data_blob_free(&blob_recv);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
if (!gensec_have_feature(auth_generic_state->gensec_security,
|
||||
GENSEC_FEATURE_SIGN) ||
|
||||
!gensec_have_feature(auth_generic_state->gensec_security,
|
||||
GENSEC_FEATURE_SEAL)) {
|
||||
status = NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
es->enc_on = true;
|
||||
/* Replace the old state, if any. */
|
||||
/* We only need the gensec_security part from here.
|
||||
* es is a malloc()ed pointer, so we cannot make
|
||||
* gensec_security a talloc child */
|
||||
es->s.gensec_security = talloc_move(NULL,
|
||||
&auth_generic_state->gensec_security);
|
||||
smb1cli_conn_set_encryption(cli->conn, es);
|
||||
es = NULL;
|
||||
}
|
||||
fail:
|
||||
common_free_encryption_state(&es);
|
||||
return status;
|
||||
}
|
||||
#else
|
||||
NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
|
||||
{
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/********************************************************************
|
||||
Ensure a connection is encrypted.
|
||||
|
@ -86,8 +86,16 @@ static NTSTATUS make_auth_gensec(const struct tsocket_address *remote_address,
|
||||
|
||||
gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
|
||||
|
||||
/*
|
||||
* We could be accessing the secrets.tdb or krb5.keytab file here.
|
||||
* ensure we have permissions to do so.
|
||||
*/
|
||||
become_root();
|
||||
|
||||
status = gensec_start_mech_by_oid(gensec_security, oid);
|
||||
|
||||
unbecome_root();
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(gensec_security);
|
||||
return nt_status_squash(status);
|
||||
@ -98,114 +106,6 @@ static NTSTATUS make_auth_gensec(const struct tsocket_address *remote_address,
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
|
||||
/******************************************************************************
|
||||
Import a name.
|
||||
******************************************************************************/
|
||||
|
||||
static NTSTATUS get_srv_gss_creds(const char *service,
|
||||
const char *name,
|
||||
gss_cred_usage_t cred_type,
|
||||
gss_cred_id_t *p_srv_cred)
|
||||
{
|
||||
OM_uint32 ret;
|
||||
OM_uint32 min;
|
||||
gss_name_t srv_name;
|
||||
gss_buffer_desc input_name;
|
||||
char *host_princ_s = NULL;
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
|
||||
gss_OID_desc nt_hostbased_service =
|
||||
{10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
|
||||
|
||||
if (asprintf(&host_princ_s, "%s@%s", service, name) == -1) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
input_name.value = host_princ_s;
|
||||
input_name.length = strlen(host_princ_s) + 1;
|
||||
|
||||
ret = gss_import_name(&min,
|
||||
&input_name,
|
||||
&nt_hostbased_service,
|
||||
&srv_name);
|
||||
|
||||
DEBUG(10,("get_srv_gss_creds: imported name %s\n",
|
||||
host_princ_s ));
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
SAFE_FREE(host_princ_s);
|
||||
return map_nt_error_from_gss(ret, min);
|
||||
}
|
||||
|
||||
/*
|
||||
* We're accessing the krb5.keytab file here.
|
||||
* ensure we have permissions to do so.
|
||||
*/
|
||||
become_root();
|
||||
|
||||
ret = gss_acquire_cred(&min,
|
||||
srv_name,
|
||||
GSS_C_INDEFINITE,
|
||||
GSS_C_NULL_OID_SET,
|
||||
cred_type,
|
||||
p_srv_cred,
|
||||
NULL,
|
||||
NULL);
|
||||
unbecome_root();
|
||||
|
||||
if (ret != GSS_S_COMPLETE) {
|
||||
ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
|
||||
DEBUG(10,("get_srv_gss_creds: gss_acquire_cred failed with %s\n",
|
||||
ads_errstr(adss)));
|
||||
status = map_nt_error_from_gss(ret, min);
|
||||
}
|
||||
|
||||
SAFE_FREE(host_princ_s);
|
||||
gss_release_name(&min, &srv_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Create a gss state.
|
||||
Try and get the cifs/server@realm principal first, then fall back to
|
||||
host/server@realm.
|
||||
******************************************************************************/
|
||||
|
||||
static NTSTATUS make_auth_gss(struct smb_trans_enc_state *es)
|
||||
{
|
||||
NTSTATUS status;
|
||||
gss_cred_id_t srv_cred;
|
||||
fstring fqdn;
|
||||
|
||||
name_to_fqdn(fqdn, lp_netbios_name());
|
||||
strlower_m(fqdn);
|
||||
|
||||
status = get_srv_gss_creds("cifs", fqdn, GSS_C_ACCEPT, &srv_cred);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
status = get_srv_gss_creds("host", fqdn, GSS_C_ACCEPT, &srv_cred);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return nt_status_squash(status);
|
||||
}
|
||||
}
|
||||
|
||||
es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
|
||||
if (!es->s.gss_state) {
|
||||
OM_uint32 min;
|
||||
gss_release_cred(&min, &srv_cred);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
ZERO_STRUCTP(es->s.gss_state);
|
||||
es->s.gss_state->creds = srv_cred;
|
||||
|
||||
/* No context yet. */
|
||||
es->s.gss_state->gss_ctx = GSS_C_NO_CONTEXT;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
Shutdown a server encryption context.
|
||||
******************************************************************************/
|
||||
@ -232,6 +132,8 @@ static NTSTATUS make_srv_encryption_context(const struct tsocket_address *remote
|
||||
enum smb_trans_enc_type smb_enc_type,
|
||||
struct smb_trans_enc_state **pp_es)
|
||||
{
|
||||
NTSTATUS status;
|
||||
const char *oid;
|
||||
struct smb_trans_enc_state *es;
|
||||
|
||||
*pp_es = NULL;
|
||||
@ -245,32 +147,21 @@ static NTSTATUS make_srv_encryption_context(const struct tsocket_address *remote
|
||||
es->smb_enc_type = smb_enc_type;
|
||||
switch (smb_enc_type) {
|
||||
case SMB_TRANS_ENC_NTLM:
|
||||
{
|
||||
NTSTATUS status = make_auth_gensec(remote_address,
|
||||
es, GENSEC_OID_NTLMSSP);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
srv_free_encryption_context(&es);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
oid = GENSEC_OID_NTLMSSP;
|
||||
break;
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
case SMB_TRANS_ENC_GSS:
|
||||
/* Acquire our credentials by calling gss_acquire_cred here. */
|
||||
{
|
||||
NTSTATUS status = make_auth_gss(es);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
srv_free_encryption_context(&es);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
oid = GENSEC_OID_KERBEROS5;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
srv_free_encryption_context(&es);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
status = make_auth_gensec(remote_address,
|
||||
es, oid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
srv_free_encryption_context(&es);
|
||||
return status;
|
||||
}
|
||||
*pp_es = es;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@ -338,75 +229,35 @@ NTSTATUS srv_encrypt_buffer(struct smbd_server_connection *sconn, char *buf,
|
||||
Until success we do everything on the partial enc ctx.
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
static NTSTATUS srv_enc_spnego_gss_negotiate(const struct tsocket_address *remote_address,
|
||||
unsigned char **ppdata,
|
||||
size_t *p_data_size,
|
||||
DATA_BLOB secblob)
|
||||
{
|
||||
OM_uint32 ret;
|
||||
OM_uint32 min;
|
||||
OM_uint32 flags = 0;
|
||||
gss_buffer_desc in_buf, out_buf;
|
||||
struct smb_tran_enc_state_gss *gss_state;
|
||||
DATA_BLOB auth_reply = data_blob_null;
|
||||
DATA_BLOB response = data_blob_null;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB unwrapped_response = data_blob_null;
|
||||
DATA_BLOB response = data_blob_null;
|
||||
|
||||
if (!partial_srv_trans_enc_ctx) {
|
||||
status = make_srv_encryption_context(remote_address,
|
||||
SMB_TRANS_ENC_GSS,
|
||||
&partial_srv_trans_enc_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
gss_state = partial_srv_trans_enc_ctx->s.gss_state;
|
||||
|
||||
in_buf.value = secblob.data;
|
||||
in_buf.length = secblob.length;
|
||||
|
||||
out_buf.value = NULL;
|
||||
out_buf.length = 0;
|
||||
|
||||
become_root();
|
||||
|
||||
ret = gss_accept_sec_context(&min,
|
||||
&gss_state->gss_ctx,
|
||||
gss_state->creds,
|
||||
&in_buf,
|
||||
GSS_C_NO_CHANNEL_BINDINGS,
|
||||
NULL,
|
||||
NULL, /* Ignore oids. */
|
||||
&out_buf, /* To return. */
|
||||
&flags,
|
||||
NULL, /* Ingore time. */
|
||||
NULL); /* Ignore delegated creds. */
|
||||
unbecome_root();
|
||||
|
||||
status = gss_err_to_ntstatus(ret, min);
|
||||
if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
|
||||
status = make_srv_encryption_context(remote_address,
|
||||
SMB_TRANS_ENC_GSS,
|
||||
&partial_srv_trans_enc_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Ensure we've got sign+seal available. */
|
||||
if (ret == GSS_S_COMPLETE) {
|
||||
if ((flags & (GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) !=
|
||||
(GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) {
|
||||
DEBUG(0,("srv_enc_spnego_gss_negotiate: quality of service not good enough "
|
||||
"for SMB sealing.\n"));
|
||||
gss_release_buffer(&min, &out_buf);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
become_root();
|
||||
|
||||
auth_reply = data_blob(out_buf.value, out_buf.length);
|
||||
gss_release_buffer(&min, &out_buf);
|
||||
status = gensec_update(partial_srv_trans_enc_ctx->s.gensec_security,
|
||||
talloc_tos(), NULL,
|
||||
secblob, &unwrapped_response);
|
||||
|
||||
/* Wrap in SPNEGO. */
|
||||
response = spnego_gen_auth_response(talloc_tos(), &auth_reply, status, OID_KERBEROS5);
|
||||
data_blob_free(&auth_reply);
|
||||
unbecome_root();
|
||||
|
||||
/* status here should be NT_STATUS_MORE_PROCESSING_REQUIRED
|
||||
* for success ... */
|
||||
|
||||
response = spnego_gen_auth_response(talloc_tos(), &unwrapped_response, status, OID_KERBEROS5);
|
||||
data_blob_free(&unwrapped_response);
|
||||
|
||||
SAFE_FREE(*ppdata);
|
||||
*ppdata = (unsigned char *)memdup(response.data, response.length);
|
||||
@ -414,12 +265,10 @@ static NTSTATUS srv_enc_spnego_gss_negotiate(const struct tsocket_address *remot
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
*p_data_size = response.length;
|
||||
|
||||
data_blob_free(&response);
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
Do the NTLM SPNEGO (or raw) encryption negotiation. Parameters are in/out.
|
||||
@ -500,16 +349,10 @@ static NTSTATUS srv_enc_spnego_negotiate(connection_struct *conn,
|
||||
if (kerb_mech) {
|
||||
TALLOC_FREE(kerb_mech);
|
||||
|
||||
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
|
||||
status = srv_enc_spnego_gss_negotiate(conn->sconn->remote_address,
|
||||
ppdata,
|
||||
p_data_size,
|
||||
secblob);
|
||||
#else
|
||||
/* Currently we don't SPNEGO negotiate
|
||||
* back to NTLMSSP as we do in sessionsetupX. We should... */
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
#endif
|
||||
} else {
|
||||
status = srv_enc_ntlm_negotiate(conn->sconn->remote_address,
|
||||
ppdata,
|
||||
|
Loading…
x
Reference in New Issue
Block a user