mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
r7827: Add in-memory keytab to Samba4, using the new MEMORY_WILDCARD keytab
support in Heimdal.
This removes the 'ext_keytab' step from my Samba4/WinXP client howto.
In doing this work, I realised that the replay cache in Heimdal is
currently a no-op, so I have removed the calls to it, and therefore
the mutex calls from passdb/secrets.c.
This patch also includes a replacement 'magic' mechanism detection,
that does not issue extra error messages from deep inside the GSSAPI
code.
Andrew Bartlett
(This used to be commit c19d5706f4
)
This commit is contained in:
parent
cc98a92bb0
commit
8a68f96f8c
@ -57,6 +57,11 @@ struct gensec_security_ops {
|
||||
const char **oid; /* NULL if not offered by SPNEGO */
|
||||
NTSTATUS (*client_start)(struct gensec_security *gensec_security);
|
||||
NTSTATUS (*server_start)(struct gensec_security *gensec_security);
|
||||
/**
|
||||
Determine if a packet has the right 'magic' for this mechanism
|
||||
*/
|
||||
NTSTATUS (*magic)(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *first_packet);
|
||||
NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out);
|
||||
NTSTATUS (*seal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
|
@ -46,6 +46,7 @@ struct gensec_gssapi_state {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_ccache ccache;
|
||||
const char *ccache_name;
|
||||
krb5_keytab keytab;
|
||||
|
||||
gss_cred_id_t cred;
|
||||
};
|
||||
@ -170,6 +171,7 @@ static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_securi
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_gssapi_state *gensec_gssapi_state;
|
||||
struct cli_credentials *machine_account;
|
||||
|
||||
nt_status = gensec_gssapi_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
@ -178,7 +180,30 @@ static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_securi
|
||||
|
||||
gensec_gssapi_state = gensec_security->private_data;
|
||||
|
||||
machine_account = cli_credentials_init(gensec_gssapi_state);
|
||||
cli_credentials_set_conf(machine_account);
|
||||
nt_status = cli_credentials_set_machine_account(machine_account);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(3, ("Could not obtain machine account credentials from the local database\n"));
|
||||
talloc_free(machine_account);
|
||||
return nt_status;
|
||||
} else {
|
||||
nt_status = create_memory_keytab(gensec_gssapi_state,
|
||||
machine_account,
|
||||
gensec_gssapi_state->smb_krb5_context,
|
||||
&gensec_gssapi_state->keytab);
|
||||
talloc_free(machine_account);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(3, ("Could not create memory keytab!\n"));
|
||||
talloc_free(machine_account);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
gsskrb5_register_acceptor_keytab(gensec_gssapi_state->keytab);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security)
|
||||
@ -236,7 +261,6 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSS_KRB5_CCACHE_NAME /* FIXME, we need an alternate function */
|
||||
maj_stat = gss_krb5_ccache_name(&min_stat,
|
||||
gensec_gssapi_state->ccache_name,
|
||||
NULL);
|
||||
@ -246,7 +270,6 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
|
||||
gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
#endif
|
||||
|
||||
maj_stat = gss_acquire_cred(&min_stat,
|
||||
gensec_gssapi_state->client_name,
|
||||
@ -266,6 +289,25 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the packet is one for this mechansim
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @return Error, INVALID_PARAMETER if it's not a packet for us
|
||||
* or NT_STATUS_OK if the packet is ok.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_gssapi_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *in)
|
||||
{
|
||||
if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the GSSAPI GENSEC mechanism
|
||||
@ -294,8 +336,18 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
|
||||
switch (gensec_security->gensec_role) {
|
||||
case GENSEC_CLIENT:
|
||||
{
|
||||
maj_stat = gss_krb5_ccache_name(&min_stat,
|
||||
gensec_gssapi_state->ccache_name,
|
||||
NULL);
|
||||
if (maj_stat) {
|
||||
DEBUG(1, ("GSS krb5 ccache set %s failed: %s\n",
|
||||
gensec_gssapi_state->ccache_name,
|
||||
gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
maj_stat = gss_init_sec_context(&min_stat,
|
||||
GSS_C_NO_CREDENTIAL,
|
||||
gensec_gssapi_state->cred,
|
||||
&gensec_gssapi_state->gssapi_context,
|
||||
gensec_gssapi_state->server_name,
|
||||
discard_const_p(gss_OID_desc, gensec_gssapi_state->gss_oid),
|
||||
@ -756,6 +808,7 @@ static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
|
||||
.oid = gensec_krb5_oids,
|
||||
.client_start = gensec_gssapi_client_start,
|
||||
.server_start = gensec_gssapi_server_start,
|
||||
.magic = gensec_gssapi_magic,
|
||||
.update = gensec_gssapi_update,
|
||||
.session_key = gensec_gssapi_session_key,
|
||||
.session_info = gensec_gssapi_session_info,
|
||||
|
@ -399,6 +399,26 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the packet is one for this mechansim
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @return Error, INVALID_PARAMETER if it's not a packet for us
|
||||
* or NT_STATUS_OK if the packet is ok.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_krb5_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *in)
|
||||
{
|
||||
if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Krb5 GENSEC mechanism
|
||||
*
|
||||
@ -494,7 +514,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
|
||||
/* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
|
||||
if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
|
||||
nt_status = ads_verify_ticket(out_mem_ctx,
|
||||
gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->smb_krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
lp_realm(),
|
||||
gensec_get_target_service(gensec_security), &in,
|
||||
@ -503,7 +523,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
|
||||
} else {
|
||||
/* TODO: check the tok_id */
|
||||
nt_status = ads_verify_ticket(out_mem_ctx,
|
||||
gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->smb_krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
lp_realm(),
|
||||
gensec_get_target_service(gensec_security),
|
||||
@ -669,6 +689,7 @@ static const struct gensec_security_ops gensec_krb5_security_ops = {
|
||||
.oid = gensec_krb5_oids,
|
||||
.client_start = gensec_krb5_client_start,
|
||||
.server_start = gensec_krb5_server_start,
|
||||
.magic = gensec_krb5_magic,
|
||||
.update = gensec_krb5_update,
|
||||
.session_key = gensec_krb5_session_key,
|
||||
.session_info = gensec_krb5_session_info,
|
||||
|
@ -269,9 +269,21 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_ops[i]->magic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nt_status = all_ops[i]->magic(gensec_security, &in);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spnego_state->state_position = SPNEGO_FALLBACK;
|
||||
|
||||
nt_status = gensec_subcontext_start(spnego_state,
|
||||
gensec_security,
|
||||
&spnego_state->sub_sec_security);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
@ -279,18 +291,11 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
|
||||
nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
|
||||
all_ops[i]);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(spnego_state->sub_sec_security);
|
||||
spnego_state->sub_sec_security = NULL;
|
||||
continue;
|
||||
return nt_status;
|
||||
}
|
||||
nt_status = gensec_update(spnego_state->sub_sec_security,
|
||||
out_mem_ctx, in, out);
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
spnego_state->state_position = SPNEGO_FALLBACK;
|
||||
return nt_status;
|
||||
}
|
||||
talloc_free(spnego_state->sub_sec_security);
|
||||
spnego_state->sub_sec_security = NULL;
|
||||
return nt_status;
|
||||
}
|
||||
DEBUG(1, ("Failed to parse SPNEGO request\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
@ -440,17 +440,6 @@ cleanup_princ:
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
|
||||
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
|
||||
{
|
||||
static krb5_data kdata;
|
||||
|
||||
kdata.data = discard_const(krb5_principal_get_comp_string(context, principal, i));
|
||||
kdata.length = strlen(kdata.data);
|
||||
return &kdata;
|
||||
}
|
||||
#endif
|
||||
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KT_FREE_ENTRY)
|
||||
|
@ -93,3 +93,24 @@ BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, D
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check a GSS-API wrapper packet givin an expected OID
|
||||
*/
|
||||
BOOL gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid)
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
int data_remaining;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx,
|
||||
krb5_ticket *tkt);
|
||||
|
||||
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
krb5_context context,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_auth_context auth_context,
|
||||
const char *realm, const char *service,
|
||||
const DATA_BLOB *ticket,
|
||||
@ -116,5 +116,13 @@ NTSTATUS kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
const char **ccache_name);
|
||||
krb5_error_code smb_krb5_init_context(TALLOC_CTX *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context);
|
||||
krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *salt_princ);
|
||||
NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab *keytab);
|
||||
#endif /* HAVE_KRB5 */
|
||||
|
||||
|
@ -28,10 +28,77 @@
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
struct principal_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_principal principal;
|
||||
};
|
||||
|
||||
struct ccache_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_ccache ccache;
|
||||
} ccache_container;
|
||||
};
|
||||
|
||||
struct keytab_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_keytab keytab;
|
||||
};
|
||||
|
||||
static int free_principal(void *ptr) {
|
||||
struct principal_container *pc = ptr;
|
||||
/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
|
||||
krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *salt_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *machine_username;
|
||||
char *salt_body;
|
||||
char *lower_realm;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
|
||||
|
||||
if (!machine_username) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (machine_username[strlen(machine_username)-1] == '$') {
|
||||
machine_username[strlen(machine_username)-1] = '\0';
|
||||
}
|
||||
lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
|
||||
if (!lower_realm) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
|
||||
lower_realm);
|
||||
if (!salt_body) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
|
||||
cli_credentials_get_realm(machine_account),
|
||||
"host", salt_body, NULL);
|
||||
|
||||
if (ret != 0) {
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *salt_princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int free_ccache(void *ptr) {
|
||||
struct ccache_container *ccc = ptr;
|
||||
@ -71,7 +138,7 @@ static int free_ccache(void *ptr) {
|
||||
|
||||
ret = krb5_cc_resolve(smb_krb5_context->krb5_context, ccache_string, ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to generate a new krb5 keytab (%s): %s\n",
|
||||
DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
|
||||
ccache_string,
|
||||
error_message(ret)));
|
||||
talloc_free(mem_ctx);
|
||||
@ -114,3 +181,95 @@ static int free_ccache(void *ptr) {
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static int free_keytab(void *ptr) {
|
||||
struct keytab_container *ktc = ptr;
|
||||
krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab *keytab)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password_s;
|
||||
krb5_data password;
|
||||
int i;
|
||||
struct keytab_container *mem_ctx = talloc(parent_ctx, struct keytab_container);
|
||||
krb5_enctype *enctypes;
|
||||
krb5_principal salt_princ;
|
||||
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
password_s = talloc_strdup(mem_ctx, cli_credentials_get_password(machine_account));
|
||||
if (!password_s) {
|
||||
DEBUG(1, ("create_memory_keytab: Could not obtain password for our local machine account!\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
password.data = password_s;
|
||||
password.length = strlen(password_s);
|
||||
|
||||
/* this string should be unique */
|
||||
|
||||
ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY_WILDCARD:", keytab);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to generate a new krb5 keytab: %s\n",
|
||||
error_message(ret)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->keytab = *keytab;
|
||||
|
||||
talloc_set_destructor(mem_ctx, free_keytab);
|
||||
|
||||
ret = salt_principal_from_credentials(mem_ctx, machine_account,
|
||||
smb_krb5_context,
|
||||
&salt_princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_memory_keytab: maksing salt principal failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
|
||||
&enctypes);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_memory_keytab: getting encrption types failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
for (i=0; enctypes[i]; i++) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
|
||||
salt_princ, &password, &entry.keyblock, enctypes[i]);
|
||||
if (ret) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
entry.principal = salt_princ;
|
||||
entry.vno = 0 /* replace with real kvno */;
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to add entry for %s to keytab: %s",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
}
|
||||
|
||||
free_kerberos_etypes(smb_krb5_context->krb5_context, enctypes);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
@ -34,9 +34,6 @@
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
#if !defined(HAVE_KRB5_PRINC_COMPONENT)
|
||||
const krb5_data *krb5_princ_component(krb5_context, krb5_principal, int );
|
||||
#endif
|
||||
static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
|
||||
{
|
||||
DATA_BLOB out;
|
||||
@ -308,7 +305,7 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
***********************************************************************************/
|
||||
|
||||
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
krb5_context context,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_auth_context auth_context,
|
||||
const char *realm, const char *service,
|
||||
const DATA_BLOB *ticket,
|
||||
@ -319,15 +316,10 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
|
||||
krb5_data packet;
|
||||
krb5_ticket *tkt = NULL;
|
||||
krb5_rcache rcache = NULL;
|
||||
krb5_principal salt_princ;
|
||||
int ret;
|
||||
|
||||
BOOL got_replay_mutex = False;
|
||||
|
||||
char *malloc_principal;
|
||||
char *machine_username;
|
||||
krb5_principal salt_princ = NULL;
|
||||
char *salt_princ_string;
|
||||
|
||||
NTSTATUS creds_nt_status;
|
||||
struct cli_credentials *machine_account;
|
||||
@ -342,100 +334,45 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
|
||||
if (!NT_STATUS_IS_OK(creds_nt_status)) {
|
||||
DEBUG(3, ("Could not obtain machine account credentials from the local database\n"));
|
||||
|
||||
/* This just becomes a locking key, if we don't have creds, we must be using the keytab */
|
||||
salt_princ_string = talloc_asprintf(mem_ctx, "host/%s@%s", lp_netbios_name(), lp_realm());
|
||||
if (!salt_princ_string) {
|
||||
ret = ENOMEM;
|
||||
} else {
|
||||
ret = krb5_parse_name(context, salt_princ_string, &salt_princ);
|
||||
}
|
||||
talloc_free(machine_account);
|
||||
machine_account = NULL;
|
||||
} else {
|
||||
|
||||
machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
|
||||
|
||||
if (!machine_username) {
|
||||
ret = ENOMEM;
|
||||
} else {
|
||||
char *salt_body;
|
||||
char *lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));;
|
||||
if (machine_username[strlen(machine_username)-1] == '$') {
|
||||
machine_username[strlen(machine_username)-1] = '\0';
|
||||
}
|
||||
if (!lower_realm) {
|
||||
ret = ENOMEM;
|
||||
} else {
|
||||
salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
|
||||
lower_realm);
|
||||
if (!salt_body) {
|
||||
ret = ENOMEM;
|
||||
} else {
|
||||
salt_princ_string = talloc_asprintf(mem_ctx, "host/%s@%s", salt_body, cli_credentials_get_realm(machine_account));
|
||||
if (!salt_princ_string) {
|
||||
ret = ENOMEM;
|
||||
} else {
|
||||
ret = krb5_parse_name(context, salt_princ_string, &salt_princ);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = salt_principal_from_credentials(mem_ctx, machine_account,
|
||||
smb_krb5_context,
|
||||
&salt_princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("ads_verify_ticket: maksing salt principal failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
DEBUG(1,("ads_verify_ticket: maksing salt principal failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* This whole process is far more complex than I would
|
||||
like. We have to go through all this to allow us to store
|
||||
the secret internally, instead of using /etc/krb5.keytab */
|
||||
|
||||
/* Lock a mutex surrounding the replay as there is no locking in the MIT krb5
|
||||
* code surrounding the replay cache... */
|
||||
|
||||
if (!grab_server_mutex("replay cache mutex")) {
|
||||
DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
got_replay_mutex = True;
|
||||
|
||||
/*
|
||||
* JRA. We must set the rcache here. This will prevent replay attacks.
|
||||
* TODO: Actually hook in the replay cache in Heimdal, then
|
||||
* re-add calls to setup a replay cache here, in our private
|
||||
* directory. This will eventually prevent replay attacks
|
||||
*/
|
||||
|
||||
ret = krb5_get_server_rcache(context, krb5_princ_component(context, salt_princ, 0), &rcache);
|
||||
if (ret) {
|
||||
DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_setrcache(context, auth_context, rcache);
|
||||
if (ret) {
|
||||
DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context,
|
||||
ret = ads_keytab_verify_ticket(mem_ctx, smb_krb5_context->krb5_context, auth_context,
|
||||
service, ticket, &packet, &tkt, keyblock);
|
||||
if (ret) {
|
||||
ret = ads_secrets_verify_ticket(mem_ctx, machine_account, context, auth_context,
|
||||
if (ret && machine_account) {
|
||||
ret = ads_secrets_verify_ticket(mem_ctx, machine_account, smb_krb5_context->krb5_context, auth_context,
|
||||
salt_princ, ticket,
|
||||
&packet, &tkt, keyblock);
|
||||
}
|
||||
|
||||
release_server_mutex();
|
||||
got_replay_mutex = False;
|
||||
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_mk_rep(context, auth_context, &packet);
|
||||
ret = krb5_mk_rep(smb_krb5_context->krb5_context, auth_context, &packet);
|
||||
if (ret) {
|
||||
DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -459,10 +396,10 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
|
||||
if ((ret = krb5_unparse_name(smb_krb5_context->krb5_context, get_principal_from_tkt(tkt),
|
||||
&malloc_principal))) {
|
||||
DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
|
||||
sret = NT_STATUS_LOGON_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
@ -479,10 +416,6 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
|
||||
out:
|
||||
|
||||
if (got_replay_mutex) {
|
||||
release_server_mutex();
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(sret)) {
|
||||
data_blob_free(auth_data);
|
||||
}
|
||||
@ -492,11 +425,7 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
}
|
||||
|
||||
if (tkt != NULL) {
|
||||
krb5_free_ticket(context, tkt);
|
||||
}
|
||||
|
||||
if (salt_princ != NULL) {
|
||||
krb5_free_principal(context, salt_princ);
|
||||
krb5_free_ticket(smb_krb5_context->krb5_context, tkt);
|
||||
}
|
||||
|
||||
return sret;
|
||||
|
@ -89,6 +89,16 @@ void debug_ntlmssp_flags(uint32_t neg_flags)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *first_packet)
|
||||
{
|
||||
if (first_packet->length > 8 && memcmp("NTLMSSP\0", first_packet->data, 8) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the wrapped NTLMSSP state machine
|
||||
*
|
||||
@ -337,6 +347,7 @@ static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
|
||||
.oid = gensec_ntlmssp_oids,
|
||||
.client_start = gensec_ntlmssp_client_start,
|
||||
.server_start = gensec_ntlmssp_server_start,
|
||||
.magic = gensec_ntlmssp_magic,
|
||||
.update = gensec_ntlmssp_update,
|
||||
.sig_size = gensec_ntlmssp_sig_size,
|
||||
.sign_packet = gensec_ntlmssp_sign_packet,
|
||||
|
@ -73,7 +73,6 @@ ADD_OBJ_FILES = \
|
||||
lib/ms_fnmatch.o \
|
||||
lib/select.o \
|
||||
lib/mutex.o \
|
||||
lib/server_mutex.o \
|
||||
lib/idtree.o \
|
||||
lib/unix_privs.o \
|
||||
lib/db_wrap.o \
|
||||
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Authenticate against a remote domain
|
||||
Copyright (C) Andrew Tridgell 1992-2002
|
||||
Copyright (C) Andrew Bartlett 2002
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/* For reasons known only to MS, many of their NT/Win2k versions
|
||||
need serialised access only. Two connections at the same time
|
||||
may (in certain situations) cause connections to be reset,
|
||||
or access to be denied.
|
||||
|
||||
This locking allows smbd's mutlithread architecture to look
|
||||
like the single-connection that NT makes. */
|
||||
|
||||
static char *mutex_server_name;
|
||||
/* FIXME. ref_count should be allocated per name... JRA. */
|
||||
size_t ref_count;
|
||||
|
||||
BOOL grab_server_mutex(const char *name)
|
||||
{
|
||||
mutex_server_name = strdup(name);
|
||||
if (!mutex_server_name) {
|
||||
DEBUG(0,("grab_server_mutex: malloc failed for %s\n", name));
|
||||
return False;
|
||||
}
|
||||
if (!secrets_named_mutex(mutex_server_name, 10, &ref_count)) {
|
||||
DEBUG(10,("grab_server_mutex: failed for %s\n", name));
|
||||
SAFE_FREE(mutex_server_name);
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
void release_server_mutex(void)
|
||||
{
|
||||
if (mutex_server_name) {
|
||||
secrets_named_mutex_release(mutex_server_name, &ref_count);
|
||||
SAFE_FREE(mutex_server_name);
|
||||
}
|
||||
}
|
@ -123,57 +123,6 @@ char *secrets_fetch_machine_password(const char *domain)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
Lock the secrets tdb based on a string - this is used as a primitive form of mutex
|
||||
between smbd instances.
|
||||
*******************************************************************************/
|
||||
|
||||
BOOL secrets_named_mutex(const char *name, uint_t timeout, size_t *p_ref_count)
|
||||
{
|
||||
size_t ref_count = *p_ref_count;
|
||||
int ret = 0;
|
||||
|
||||
secrets_init();
|
||||
if (!tdb)
|
||||
return False;
|
||||
|
||||
if (ref_count == 0) {
|
||||
ret = tdb_lock_bystring(tdb->tdb, name);
|
||||
if (ret == 0)
|
||||
DEBUG(10,("secrets_named_mutex: got mutex for %s\n", name ));
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
*p_ref_count = ++ref_count;
|
||||
DEBUG(10,("secrets_named_mutex: ref_count for mutex %s = %u\n", name, (uint_t)ref_count ));
|
||||
}
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Unlock a named mutex.
|
||||
*******************************************************************************/
|
||||
|
||||
void secrets_named_mutex_release(const char *name, size_t *p_ref_count)
|
||||
{
|
||||
size_t ref_count = *p_ref_count;
|
||||
|
||||
SMB_ASSERT(ref_count != 0);
|
||||
|
||||
secrets_init();
|
||||
if (!tdb)
|
||||
return;
|
||||
|
||||
if (ref_count == 1) {
|
||||
tdb_unlock_bystring(tdb->tdb, name);
|
||||
DEBUG(10,("secrets_named_mutex: released mutex for %s\n", name ));
|
||||
}
|
||||
|
||||
*p_ref_count = --ref_count;
|
||||
DEBUG(10,("secrets_named_mutex_release: ref_count for mutex %s = %u\n", name, (uint_t)ref_count ));
|
||||
}
|
||||
|
||||
/*
|
||||
connect to the schannel ldb
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user