wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
SMB_ENABLE(gensec_krb5, $HAVE_KRB5)
SMB_ENABLE(gensec_gssapi, $HAVE_KRB5)
+91
View File
@@ -0,0 +1,91 @@
#################################
# Start SUBSYSTEM gensec
[LIBRARY::gensec]
VERSION = 0.0.1
SO_VERSION = 0
DESCRIPTION = Generic Security Library
PUBLIC_HEADERS = gensec.h spnego.h
PUBLIC_PROTO_HEADER = gensec_proto.h
OBJ_FILES = gensec.o
PUBLIC_DEPENDENCIES = \
CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL
# End SUBSYSTEM gensec
#################################
################################################
# Start MODULE gensec_krb5
[MODULE::gensec_krb5]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_krb5_init
OBJ_FILES = gensec_krb5.o
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth auth_sam
# End MODULE gensec_krb5
################################################
################################################
# Start MODULE gensec_gssapi
[MODULE::gensec_gssapi]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_gssapi_init
OBJ_FILES = gensec_gssapi.o
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth HEIMDAL_GSSAPI
# End MODULE gensec_gssapi
################################################
################################################
# Start MODULE cyrus_sasl
[MODULE::cyrus_sasl]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_sasl_init
OBJ_FILES = cyrus_sasl.o
PUBLIC_DEPENDENCIES = CREDENTIALS SASL auth
# End MODULE cyrus_sasl
################################################
################################################
# Start MODULE gensec_spnego
[MODULE::gensec_spnego]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_spnego_init
PRIVATE_PROTO_HEADER = spnego_proto.h
PRIVATE_DEPENDENCIES = ASN1_UTIL GENSEC_SOCKET
PUBLIC_DEPENDENCIES = CREDENTIALS
OBJ_FILES = spnego.o \
spnego_parse.o
# End MODULE gensec_spnego
################################################
################################################
# Start MODULE gensec_schannel
[MODULE::gensec_schannel]
SUBSYSTEM = gensec
PRIVATE_PROTO_HEADER = schannel_proto.h
INIT_FUNCTION = gensec_schannel_init
OBJ_FILES = schannel.o \
schannel_sign.o
PUBLIC_DEPENDENCIES = auth SCHANNELDB NDR_SCHANNEL CREDENTIALS
OUTPUT_TYPE = INTEGRATED
# End MODULE gensec_schannel
################################################
################################################
# Start SUBSYSTEM SCHANNELDB
[SUBSYSTEM::SCHANNELDB]
PRIVATE_PROTO_HEADER = schannel_state.h
OBJ_FILES = \
schannel_state.o
#
# End SUBSYSTEM SCHANNELDB
################################################
################################################
# Start SUBSYSTEM GENSEC_SOCKET
[SUBSYSTEM::GENSEC_SOCKET]
OBJ_FILES = \
socket.o
PUBLIC_DEPENDENCIES = samba-socket LIBPACKET
#PUBLIC_DEPENDENCIES = gensec
#
# End SUBSYSTEM GENSEC_SOCKET
################################################
+424
View File
@@ -0,0 +1,424 @@
/*
Unix SMB/CIFS implementation.
Connect GENSEC to an external SASL lib
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
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"
#include "auth/auth.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "lib/socket/socket.h"
#include <sasl/sasl.h>
struct gensec_sasl_state {
sasl_conn_t *conn;
int step;
};
static NTSTATUS sasl_nt_status(int sasl_ret)
{
switch (sasl_ret) {
case SASL_CONTINUE:
return NT_STATUS_MORE_PROCESSING_REQUIRED;
case SASL_NOMEM:
return NT_STATUS_NO_MEMORY;
case SASL_BADPARAM:
case SASL_NOMECH:
return NT_STATUS_INVALID_PARAMETER;
case SASL_BADMAC:
return NT_STATUS_ACCESS_DENIED;
case SASL_OK:
return NT_STATUS_OK;
default:
return NT_STATUS_UNSUCCESSFUL;
}
}
static int gensec_sasl_get_user(void *context, int id,
const char **result, unsigned *len)
{
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
return SASL_FAIL;
}
*result = username;
return SASL_OK;
}
static int gensec_sasl_get_realm(void *context, int id,
const char **availrealms,
const char **result)
{
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
int i;
if (id != SASL_CB_GETREALM) {
return SASL_FAIL;
}
for (i=0; availrealms && availrealms[i]; i++) {
if (strcasecmp_m(realm, availrealms[i]) == 0) {
result[i] = availrealms[i];
return SASL_OK;
}
}
/* None of the realms match, so lets not specify one */
*result = "";
return SASL_OK;
}
static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
sasl_secret_t **psecret)
{
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
sasl_secret_t *secret;
if (!password) {
*psecret = NULL;
return SASL_OK;
}
secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
if (!secret) {
return SASL_NOMEM;
}
secret->len = strlen(password);
safe_strcpy(secret->data, password, secret->len+1);
*psecret = secret;
return SASL_OK;
}
static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
{
sasl_dispose(&gensec_sasl_state->conn);
return 0;
}
static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
{
struct gensec_sasl_state *gensec_sasl_state;
const char *service = gensec_get_target_service(gensec_security);
const char *target_name = gensec_get_target_hostname(gensec_security);
struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
char *local_addr = NULL;
char *remote_addr = NULL;
int sasl_ret;
sasl_callback_t *callbacks;
gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
if (!gensec_sasl_state) {
return NT_STATUS_NO_MEMORY;
}
callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
callbacks[0].id = SASL_CB_USER;
callbacks[0].proc = gensec_sasl_get_user;
callbacks[0].context = gensec_security;
callbacks[1].id = SASL_CB_AUTHNAME;
callbacks[1].proc = gensec_sasl_get_user;
callbacks[1].context = gensec_security;
callbacks[2].id = SASL_CB_GETREALM;
callbacks[2].proc = gensec_sasl_get_realm;
callbacks[2].context = gensec_security;
callbacks[3].id = SASL_CB_PASS;
callbacks[3].proc = gensec_sasl_get_password;
callbacks[3].context = gensec_security;
callbacks[4].id = SASL_CB_LIST_END;
callbacks[4].proc = NULL;
callbacks[4].context = NULL;
gensec_security->private_data = gensec_sasl_state;
if (local_socket_addr) {
local_addr = talloc_asprintf(gensec_sasl_state,
"%s;%d",
local_socket_addr->addr,
local_socket_addr->port);
}
if (remote_socket_addr) {
remote_addr = talloc_asprintf(gensec_sasl_state,
"%s;%d",
remote_socket_addr->addr,
remote_socket_addr->port);
}
gensec_sasl_state->step = 0;
sasl_ret = sasl_client_new(service,
target_name,
local_addr, remote_addr, callbacks, 0,
&gensec_sasl_state->conn);
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
sasl_security_properties_t props;
talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
ZERO_STRUCT(props);
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
props.min_ssf = 1;
}
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
props.min_ssf = 40;
}
props.max_ssf = UINT_MAX;
props.maxbufsize = 65536;
sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
if (sasl_ret != SASL_OK) {
return sasl_nt_status(sasl_ret);
}
} else {
DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
}
return sasl_nt_status(sasl_ret);
}
static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
int sasl_ret;
const char *out_data;
unsigned int out_len;
if (gensec_sasl_state->step == 0) {
const char *mech;
sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name,
NULL, &out_data, &out_len, &mech);
} else {
sasl_ret = sasl_client_step(gensec_sasl_state->conn,
in.data, in.length, NULL, &out_data, &out_len);
}
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
} else {
DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step,
sasl_errdetail(gensec_sasl_state->conn)));
}
gensec_sasl_state->step++;
return sasl_nt_status(sasl_ret);
}
static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
const char *out_data;
unsigned int out_len;
int sasl_ret = sasl_decode(gensec_sasl_state->conn,
in->data, in->length, &out_data, &out_len);
if (sasl_ret == SASL_OK) {
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
*len_processed = in->length;
} else {
DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
}
return sasl_nt_status(sasl_ret);
}
static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
const char *out_data;
unsigned int out_len;
int sasl_ret = sasl_encode(gensec_sasl_state->conn,
in->data, in->length, &out_data, &out_len);
if (sasl_ret == SASL_OK) {
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
*len_processed = in->length;
} else {
DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
}
return sasl_nt_status(sasl_ret);
}
/* Try to figure out what features we actually got on the connection */
static BOOL gensec_sasl_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
sasl_ssf_t ssf;
int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF, &ssf);
if (sasl_ret != SASL_OK) {
return False;
}
if (feature & GENSEC_FEATURE_SIGN) {
if (ssf == 0) {
return False;
}
if (ssf >= 1) {
return True;
}
}
if (feature & GENSEC_FEATURE_SEAL) {
if (ssf <= 1) {
return False;
}
if (ssf > 1) {
return True;
}
}
return False;
}
/* This could in theory work with any SASL mech */
static const struct gensec_security_ops gensec_sasl_security_ops = {
.name = "sasl-DIGEST-MD5",
.sasl_name = "DIGEST-MD5",
.client_start = gensec_sasl_client_start,
.update = gensec_sasl_update,
.wrap_packets = gensec_sasl_wrap_packets,
.unwrap_packets = gensec_sasl_unwrap_packets,
.have_feature = gensec_sasl_have_feature,
.enabled = True,
.priority = GENSEC_SASL
};
int gensec_sasl_log(void *context,
int sasl_log_level,
const char *message)
{
int debug_level;
switch (sasl_log_level) {
case SASL_LOG_NONE:
debug_level = 0;
break;
case SASL_LOG_ERR:
debug_level = 1;
break;
case SASL_LOG_FAIL:
debug_level = 2;
break;
case SASL_LOG_WARN:
debug_level = 3;
break;
case SASL_LOG_NOTE:
debug_level = 5;
break;
case SASL_LOG_DEBUG:
debug_level = 10;
break;
case SASL_LOG_TRACE:
debug_level = 11;
break;
#if DEBUG_PASSWORD
case SASL_LOG_PASS:
debug_level = 100;
break;
#endif
default:
debug_level = 0;
break;
}
DEBUG(debug_level, ("gensec_sasl: %s\n", message));
return SASL_OK;
}
NTSTATUS gensec_sasl_init(void)
{
NTSTATUS ret;
int sasl_ret, i;
const char **sasl_mechs;
static const sasl_callback_t callbacks[] = {
{
.id = SASL_CB_LOG,
.proc = gensec_sasl_log,
.context = NULL,
},
{
.id = SASL_CB_LIST_END,
.proc = gensec_sasl_log,
.context = NULL,
}
};
sasl_ret = sasl_client_init(callbacks);
if (sasl_ret == SASL_NOMECH) {
/* Nothing to do here */
return NT_STATUS_OK;
}
if (sasl_ret != SASL_OK) {
return sasl_nt_status(sasl_ret);
}
/* For now, we just register DIGEST-MD5 */
#if 1
ret = gensec_register(&gensec_sasl_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_sasl_security_ops.name));
return ret;
}
#else
sasl_mechs = sasl_global_listmech();
for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
const struct gensec_security_ops *oldmech;
struct gensec_security_ops *newmech;
oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
if (oldmech) {
continue;
}
newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
if (!newmech) {
return NT_STATUS_NO_MEMORY;
}
*newmech = gensec_sasl_security_ops;
newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
if (!newmech->sasl_name || !newmech->name) {
return NT_STATUS_NO_MEMORY;
}
ret = gensec_register(newmech);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_sasl_security_ops.name));
return ret;
}
}
#endif
return NT_STATUS_OK;
}
File diff suppressed because it is too large Load Diff
+175
View File
@@ -0,0 +1,175 @@
/*
Unix SMB/CIFS implementation.
Generic Authentication Interface
Copyright (C) Andrew Tridgell 2003
Copyright (C) Andrew Bartlett <abartlet@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 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.
*/
#ifndef __GENSEC_H__
#define __GENSEC_H__
#include "core.h"
#include "auth/credentials/credentials.h"
#define GENSEC_OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
#define GENSEC_OID_SPNEGO "1 3 6 1 5 5 2"
#define GENSEC_OID_KERBEROS5 "1 2 840 113554 1 2 2"
#define GENSEC_OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
#define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3"
enum gensec_priority {
GENSEC_SPNEGO = 90,
GENSEC_GSSAPI = 80,
GENSEC_KRB5 = 70,
GENSEC_SCHANNEL = 60,
GENSEC_NTLMSSP = 50,
GENSEC_SASL = 20,
GENSEC_OTHER = 0
};
enum credentials_use_kerberos;
struct gensec_security;
struct gensec_target {
const char *principal;
const char *hostname;
const char *service;
};
#define GENSEC_FEATURE_SESSION_KEY 0x00000001
#define GENSEC_FEATURE_SIGN 0x00000002
#define GENSEC_FEATURE_SEAL 0x00000004
#define GENSEC_FEATURE_DCE_STYLE 0x00000008
#define GENSEC_FEATURE_ASYNC_REPLIES 0x00000010
#define GENSEC_FEATURE_DATAGRAM_MODE 0x00000020
/* GENSEC mode */
enum gensec_role
{
GENSEC_SERVER,
GENSEC_CLIENT
};
struct auth_session_info;
struct gensec_update_request {
struct gensec_security *gensec_security;
void *private_data;
DATA_BLOB in;
DATA_BLOB out;
NTSTATUS status;
struct {
void (*fn)(struct gensec_update_request *req, void *private_data);
void *private_data;
} callback;
};
struct gensec_security_ops {
const char *name;
const char *sasl_name;
uint8_t auth_type; /* 0 if not offered on DCE-RPC */
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,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig);
NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig);
size_t (*sig_size)(struct gensec_security *gensec_security, size_t data_size);
size_t (*max_input_size)(struct gensec_security *gensec_security);
size_t (*max_wrapped_size)(struct gensec_security *gensec_security);
NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig);
NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig);
NTSTATUS (*wrap)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out);
NTSTATUS (*unwrap)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out);
NTSTATUS (*wrap_packets)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
NTSTATUS (*unwrap_packets)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
NTSTATUS (*packet_full_request)(struct gensec_security *gensec_security,
DATA_BLOB blob, size_t *size);
NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
NTSTATUS (*session_info)(struct gensec_security *gensec_security,
struct auth_session_info **session_info);
BOOL (*have_feature)(struct gensec_security *gensec_security,
uint32_t feature);
BOOL enabled;
BOOL kerberos;
enum gensec_priority priority;
};
struct gensec_security_ops_wrapper {
const struct gensec_security_ops *op;
const char *oid;
};
#define GENSEC_INTERFACE_VERSION 0
struct gensec_security {
const struct gensec_security_ops *ops;
void *private_data;
struct cli_credentials *credentials;
struct gensec_target target;
enum gensec_role gensec_role;
BOOL subcontext;
uint32_t want_features;
struct event_context *event_ctx;
struct messaging_context *msg_ctx; /* only valid as server */
struct socket_address *my_addr, *peer_addr;
};
/* this structure is used by backends to determine the size of some critical types */
struct gensec_critical_sizes {
int interface_version;
int sizeof_gensec_security_ops;
int sizeof_gensec_security;
};
#include "auth/gensec/gensec_proto.h"
#endif /* __GENSEC_H__ */
File diff suppressed because it is too large Load Diff
+792
View File
@@ -0,0 +1,792 @@
/*
Unix SMB/CIFS implementation.
Kerberos backend for GENSEC
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-2003
Copyright (C) Stefan Metzmacher 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 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"
#include "system/kerberos.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 "system/network.h"
#include "lib/socket/socket.h"
#include "librpc/rpc/dcerpc.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
#include "auth/gensec/gensec.h"
enum GENSEC_KRB5_STATE {
GENSEC_KRB5_SERVER_START,
GENSEC_KRB5_CLIENT_START,
GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
GENSEC_KRB5_DONE
};
struct gensec_krb5_state {
DATA_BLOB session_key;
DATA_BLOB pac;
enum GENSEC_KRB5_STATE state_position;
struct smb_krb5_context *smb_krb5_context;
krb5_auth_context auth_context;
krb5_data enc_ticket;
krb5_keyblock *keyblock;
krb5_ticket *ticket;
BOOL gssapi;
};
static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
{
if (!gensec_krb5_state->smb_krb5_context) {
/* We can't clean anything else up unless we started up this far */
return 0;
}
if (gensec_krb5_state->enc_ticket.length) {
kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->enc_ticket);
}
if (gensec_krb5_state->ticket) {
krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->ticket);
}
/* ccache freed in a child destructor */
krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->keyblock);
if (gensec_krb5_state->auth_context) {
krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context);
}
return 0;
}
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
{
krb5_error_code ret;
struct gensec_krb5_state *gensec_krb5_state;
struct cli_credentials *creds;
const struct socket_address *my_addr, *peer_addr;
krb5_address my_krb5_addr, peer_krb5_addr;
creds = gensec_get_credentials(gensec_security);
if (!creds) {
return NT_STATUS_INVALID_PARAMETER;
}
gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
if (!gensec_krb5_state) {
return NT_STATUS_NO_MEMORY;
}
gensec_security->private_data = gensec_krb5_state;
gensec_krb5_state->smb_krb5_context = NULL;
gensec_krb5_state->auth_context = NULL;
gensec_krb5_state->ticket = NULL;
ZERO_STRUCT(gensec_krb5_state->enc_ticket);
gensec_krb5_state->keyblock = NULL;
gensec_krb5_state->session_key = data_blob(NULL, 0);
gensec_krb5_state->pac = data_blob(NULL, 0);
gensec_krb5_state->gssapi = False;
talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
if (cli_credentials_get_krb5_context(creds, &gensec_krb5_state->smb_krb5_context)) {
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context,
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
my_addr = gensec_get_my_addr(gensec_security);
if (my_addr && my_addr->sockaddr) {
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
my_addr->sockaddr, &my_krb5_addr);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
}
peer_addr = gensec_get_peer_addr(gensec_security);
if (peer_addr && peer_addr->sockaddr) {
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
peer_addr->sockaddr, &peer_krb5_addr);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
}
ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context,
my_addr ? &my_krb5_addr : NULL,
peer_addr ? &peer_krb5_addr : NULL);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
return NT_STATUS_OK;
}
static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status;
struct gensec_krb5_state *gensec_krb5_state;
nt_status = gensec_krb5_start(gensec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
return NT_STATUS_OK;
}
static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status = gensec_krb5_server_start(gensec_security);
if (NT_STATUS_IS_OK(nt_status)) {
struct gensec_krb5_state *gensec_krb5_state;
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->gssapi = True;
}
return nt_status;
}
static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
{
struct gensec_krb5_state *gensec_krb5_state;
krb5_error_code ret;
NTSTATUS nt_status;
struct ccache_container *ccache_container;
const char *hostname;
krb5_flags ap_req_options = AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
const char *principal;
krb5_data in_data;
hostname = gensec_get_target_hostname(gensec_security);
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 krb5 to an IP address"));
return NT_STATUS_INVALID_PARAMETER;
}
if (strcmp(hostname, "localhost") == 0) {
DEBUG(2, ("krb5 to 'localhost' does not make sense"));
return NT_STATUS_INVALID_PARAMETER;
}
nt_status = gensec_krb5_start(gensec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), &ccache_container);
if (ret) {
DEBUG(1,("gensec_krb5_start: cli_credentials_get_ccache failed: %s\n",
error_message(ret)));
return NT_STATUS_UNSUCCESSFUL;
}
in_data.length = 0;
principal = gensec_get_target_principal(gensec_security);
if (principal && lp_client_use_spnego_principal()) {
krb5_principal target_principal;
ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
&target_principal);
if (ret == 0) {
ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->auth_context,
ap_req_options,
target_principal,
&in_data, ccache_container->ccache,
&gensec_krb5_state->enc_ticket);
krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
target_principal);
}
} else {
ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->auth_context,
ap_req_options,
gensec_get_target_service(gensec_security),
hostname,
&in_data, ccache_container->ccache,
&gensec_krb5_state->enc_ticket);
}
switch (ret) {
case 0:
return NT_STATUS_OK;
case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
case KRB5_KDC_UNREACH:
DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
case KRB5KDC_ERR_PREAUTH_FAILED:
case KRB5KRB_AP_ERR_TKT_EXPIRED:
case KRB5_CC_END:
/* Too much clock skew - we will need to kinit to re-skew the clock */
case KRB5KRB_AP_ERR_SKEW:
case KRB5_KDCREP_SKEW:
{
DEBUG(3, ("kerberos (mk_req) failed: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
/*fall through*/
}
/* just don't print a message for these really ordinary messages */
case KRB5_FCC_NOFILE:
case KRB5_CC_NOTFOUND:
case ENOENT:
return NT_STATUS_UNSUCCESSFUL;
break;
default:
DEBUG(0, ("kerberos: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
return NT_STATUS_UNSUCCESSFUL;
}
}
static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status = gensec_krb5_client_start(gensec_security);
if (NT_STATUS_IS_OK(nt_status)) {
struct gensec_krb5_state *gensec_krb5_state;
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->gssapi = True;
}
return nt_status;
}
/**
* 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_fake_gssapi_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
*
* @param gensec_krb5_state KRB5 State
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
* @param in The request, as a DATA_BLOB
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
* or NT_STATUS_OK if the user is authenticated.
*/
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_error_code ret = 0;
NTSTATUS nt_status;
switch (gensec_krb5_state->state_position) {
case GENSEC_KRB5_CLIENT_START:
{
DATA_BLOB unwrapped_out;
if (gensec_krb5_state->gssapi) {
unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
/* wrap that up in a nice GSS-API wrapping */
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
} else {
*out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
}
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
return nt_status;
}
case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
{
DATA_BLOB unwrapped_in;
krb5_data inbuf;
krb5_ap_rep_enc_part *repl = NULL;
uint8_t tok_id[2];
if (gensec_krb5_state->gssapi) {
if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
dump_data_pw("Mutual authentication message:\n", in.data, in.length);
return NT_STATUS_INVALID_PARAMETER;
}
} else {
unwrapped_in = in;
}
/* TODO: check the tok_id */
inbuf.data = unwrapped_in.data;
inbuf.length = unwrapped_in.length;
ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context,
&inbuf, &repl);
if (ret) {
DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
nt_status = NT_STATUS_ACCESS_DENIED;
} else {
*out = data_blob(NULL, 0);
nt_status = NT_STATUS_OK;
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
}
if (repl) {
krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
}
return nt_status;
}
case GENSEC_KRB5_SERVER_START:
{
DATA_BLOB unwrapped_in;
DATA_BLOB unwrapped_out = data_blob(NULL, 0);
krb5_data inbuf, outbuf;
uint8_t tok_id[2];
struct keytab_container *keytab;
krb5_principal server_in_keytab;
if (!in.data) {
return NT_STATUS_INVALID_PARAMETER;
}
/* Grab the keytab, however generated */
ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), &keytab);
if (ret) {
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
/* This ensures we lookup the correct entry in that keytab */
ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
gensec_krb5_state->smb_krb5_context,
&server_in_keytab);
if (ret) {
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
/* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
if (gensec_krb5_state->gssapi
&& gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
inbuf.data = unwrapped_in.data;
inbuf.length = unwrapped_in.length;
} else {
inbuf.data = in.data;
inbuf.length = in.length;
}
ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->auth_context,
&inbuf, keytab->keytab, server_in_keytab,
&outbuf,
&gensec_krb5_state->ticket,
&gensec_krb5_state->keyblock);
if (ret) {
return NT_STATUS_LOGON_FAILURE;
}
unwrapped_out.data = outbuf.data;
unwrapped_out.length = outbuf.length;
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
/* wrap that up in a nice GSS-API wrapping */
if (gensec_krb5_state->gssapi) {
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
} else {
*out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
}
krb5_data_free(&outbuf);
return NT_STATUS_OK;
}
case GENSEC_KRB5_DONE:
default:
/* Asking too many times... */
return NT_STATUS_INVALID_PARAMETER;
}
}
static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
DATA_BLOB *session_key)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
krb5_keyblock *skey;
krb5_error_code err = -1;
if (gensec_krb5_state->session_key.data) {
*session_key = gensec_krb5_state->session_key;
return NT_STATUS_OK;
}
switch (gensec_security->gensec_role) {
case GENSEC_CLIENT:
err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
break;
case GENSEC_SERVER:
err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
break;
}
if (err == 0 && skey != NULL) {
DEBUG(10, ("Got KRB5 session key of length %d\n",
(int)KRB5_KEY_LENGTH(skey)));
gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state,
KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
*session_key = gensec_krb5_state->session_key;
dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
krb5_free_keyblock(context, skey);
return NT_STATUS_OK;
} else {
DEBUG(10, ("KRB5 error getting session key %d\n", err));
return NT_STATUS_NO_USER_SESSION_KEY;
}
}
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
struct auth_session_info **_session_info)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info = NULL;
struct PAC_LOGON_INFO *logon_info;
krb5_principal client_principal;
char *principal_string;
DATA_BLOB pac;
krb5_data pac_data;
krb5_error_code ret;
TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
if (ret) {
DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n",
smb_get_krb5_error_message(context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
client_principal, &principal_string);
if (ret) {
DEBUG(1, ("Unable to parse client principal: %s\n",
smb_get_krb5_error_message(context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
KRB5_AUTHDATA_WIN2K_PAC,
&pac_data);
if (ret && lp_parm_bool(-1, "gensec", "require_pac", False)) {
DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
principal_string,
smb_get_krb5_error_message(context,
ret, mem_ctx)));
krb5_free_principal(context, client_principal);
free(principal_string);
return NT_STATUS_ACCESS_DENIED;
} else if (ret) {
/* NO pac */
DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
smb_get_krb5_error_message(context,
ret, mem_ctx)));
nt_status = sam_get_server_info_principal(mem_ctx, principal_string,
&server_info);
krb5_free_principal(context, client_principal);
free(principal_string);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
} else {
/* Found pac */
union netr_Validation validation;
free(principal_string);
pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
if (!pac.data) {
krb5_free_principal(context, client_principal);
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
/* decode and verify the pac */
nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, pac,
gensec_krb5_state->smb_krb5_context->krb5_context,
NULL, gensec_krb5_state->keyblock,
client_principal,
gensec_krb5_state->ticket->ticket.authtime, NULL);
krb5_free_principal(context, client_principal);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
validation.sam3 = &logon_info->info3;
nt_status = make_server_info_netlogon_validation(mem_ctx,
NULL,
3, &validation,
&server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
}
/* references the server_info into the session_info */
nt_status = auth_generate_session_info(mem_ctx, server_info, &session_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
*_session_info = session_info;
talloc_steal(gensec_krb5_state, session_info);
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
krb5_error_code ret;
krb5_data input, output;
input.length = in->length;
input.data = in->data;
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
if (ret) {
DEBUG(1, ("krb5_mk_priv failed: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, mem_ctx)));
return NT_STATUS_ACCESS_DENIED;
}
*out = data_blob_talloc(mem_ctx, output.data, output.length);
krb5_data_free(&output);
} else {
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
krb5_error_code ret;
krb5_data input, output;
krb5_replay_data replay;
input.length = in->length;
input.data = in->data;
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
if (ret) {
DEBUG(1, ("krb5_rd_priv failed: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, mem_ctx)));
return NT_STATUS_ACCESS_DENIED;
}
*out = data_blob_talloc(mem_ctx, output.data, output.length);
krb5_data_free(&output);
} else {
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
if (feature & GENSEC_FEATURE_SESSION_KEY) {
return True;
}
if (!gensec_krb5_state->gssapi &&
(feature & GENSEC_FEATURE_SEAL)) {
return True;
}
return False;
}
static const char *gensec_krb5_oids[] = {
GENSEC_OID_KERBEROS5,
GENSEC_OID_KERBEROS5_OLD,
NULL
};
static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
.name = "fake_gssapi_krb5",
.auth_type = DCERPC_AUTH_TYPE_KRB5,
.oid = gensec_krb5_oids,
.client_start = gensec_fake_gssapi_krb5_client_start,
.server_start = gensec_fake_gssapi_krb5_server_start,
.update = gensec_krb5_update,
.magic = gensec_fake_gssapi_krb5_magic,
.session_key = gensec_krb5_session_key,
.session_info = gensec_krb5_session_info,
.have_feature = gensec_krb5_have_feature,
.enabled = False,
.kerberos = True,
.priority = GENSEC_KRB5
};
static const struct gensec_security_ops gensec_krb5_security_ops = {
.name = "krb5",
.client_start = gensec_krb5_client_start,
.server_start = gensec_krb5_server_start,
.update = gensec_krb5_update,
.session_key = gensec_krb5_session_key,
.session_info = gensec_krb5_session_info,
.have_feature = gensec_krb5_have_feature,
.wrap = gensec_krb5_wrap,
.unwrap = gensec_krb5_unwrap,
.enabled = True,
.kerberos = True,
.priority = GENSEC_KRB5
};
NTSTATUS gensec_krb5_init(void)
{
NTSTATUS ret;
auth_init();
ret = gensec_register(&gensec_krb5_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_krb5_security_ops.name));
return ret;
}
ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_krb5_security_ops.name));
return ret;
}
return ret;
}
+275
View File
@@ -0,0 +1,275 @@
/*
Unix SMB/CIFS implementation.
dcerpc schannel operations
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@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 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"
#include "librpc/gen_ndr/ndr_schannel.h"
#include "auth/auth.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "auth/gensec/schannel.h"
#include "auth/gensec/schannel_state.h"
#include "auth/gensec/schannel_proto.h"
#include "librpc/rpc/dcerpc.h"
static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
{
return 32;
}
static NTSTATUS schannel_session_key(struct gensec_security *gensec_security,
DATA_BLOB *session_key)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct schannel_state *state = gensec_security->private_data;
NTSTATUS status;
struct schannel_bind bind_schannel;
struct schannel_bind_ack bind_schannel_ack;
struct creds_CredentialState *creds;
const char *workstation;
const char *domain;
*out = data_blob(NULL, 0);
switch (gensec_security->gensec_role) {
case GENSEC_CLIENT:
if (state->state != SCHANNEL_STATE_START) {
/* we could parse the bind ack, but we don't know what it is yet */
return NT_STATUS_OK;
}
state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials));
bind_schannel.unknown1 = 0;
#if 0
/* to support this we'd need to have access to the full domain name */
bind_schannel.bind_type = 23;
bind_schannel.u.info23.domain = cli_credentials_get_domain(gensec_security->credentials);
bind_schannel.u.info23.workstation = cli_credentials_get_workstation(gensec_security->credentials);
bind_schannel.u.info23.dnsdomain = cli_credentials_get_realm(gensec_security->credentials);
/* w2k3 refuses us if we use the full DNS workstation?
why? perhaps because we don't fill in the dNSHostName
attribute in the machine account? */
bind_schannel.u.info23.dnsworkstation = cli_credentials_get_workstation(gensec_security->credentials);
#else
bind_schannel.bind_type = 3;
bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
#endif
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
(ndr_push_flags_fn_t)ndr_push_schannel_bind);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Could not create schannel bind: %s\n",
nt_errstr(status)));
return status;
}
state->state = SCHANNEL_STATE_UPDATE_1;
return NT_STATUS_MORE_PROCESSING_REQUIRED;
case GENSEC_SERVER:
if (state->state != SCHANNEL_STATE_START) {
/* no third leg on this protocol */
return NT_STATUS_INVALID_PARAMETER;
}
/* parse the schannel startup blob */
status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
(ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (bind_schannel.bind_type == 23) {
workstation = bind_schannel.u.info23.workstation;
domain = bind_schannel.u.info23.domain;
} else {
workstation = bind_schannel.u.info3.workstation;
domain = bind_schannel.u.info3.domain;
}
/* pull the session key for this client */
status = schannel_fetch_session_key(out_mem_ctx, workstation,
domain, &creds);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
workstation, nt_errstr(status)));
return status;
}
state->creds = talloc_reference(state, creds);
bind_schannel_ack.unknown1 = 1;
bind_schannel_ack.unknown2 = 0;
bind_schannel_ack.unknown3 = 0x6c0000;
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
(ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
workstation, nt_errstr(status)));
return status;
}
state->state = SCHANNEL_STATE_UPDATE_1;
return NT_STATUS_OK;
}
return NT_STATUS_INVALID_PARAMETER;
}
/**
* Return the struct creds_CredentialState.
*
* Make sure not to call this unless gensec is using schannel...
*/
/* TODO: make this non-public */
_PUBLIC_ NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
struct creds_CredentialState **creds)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
*creds = talloc_reference(mem_ctx, state->creds);
if (!*creds) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/**
* Returns anonymous credentials for schannel, matching Win2k3.
*
*/
static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
struct auth_session_info **_session_info)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
return auth_anonymous_session_info(state, _session_info);
}
static NTSTATUS schannel_start(struct gensec_security *gensec_security)
{
struct schannel_state *state;
state = talloc(gensec_security, struct schannel_state);
if (!state) {
return NT_STATUS_NO_MEMORY;
}
state->state = SCHANNEL_STATE_START;
state->seq_num = 0;
gensec_security->private_data = state;
return NT_STATUS_OK;
}
static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
{
NTSTATUS status;
struct schannel_state *state;
status = schannel_start(gensec_security);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
state = gensec_security->private_data;
state->initiator = False;
return NT_STATUS_OK;
}
static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
{
NTSTATUS status;
struct schannel_state *state;
status = schannel_start(gensec_security);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
state = gensec_security->private_data;
state->initiator = True;
return NT_STATUS_OK;
}
static BOOL schannel_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
if (feature & (GENSEC_FEATURE_SIGN |
GENSEC_FEATURE_SEAL)) {
return True;
}
if (feature & GENSEC_FEATURE_DCE_STYLE) {
return True;
}
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
return True;
}
return False;
}
static const struct gensec_security_ops gensec_schannel_security_ops = {
.name = "schannel",
.auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
.client_start = schannel_client_start,
.server_start = schannel_server_start,
.update = schannel_update,
.seal_packet = schannel_seal_packet,
.sign_packet = schannel_sign_packet,
.check_packet = schannel_check_packet,
.unseal_packet = schannel_unseal_packet,
.session_key = schannel_session_key,
.session_info = schannel_session_info,
.sig_size = schannel_sig_size,
.have_feature = schannel_have_feature,
.enabled = True,
.priority = GENSEC_SCHANNEL
};
NTSTATUS gensec_schannel_init(void)
{
NTSTATUS ret;
ret = gensec_register(&gensec_schannel_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_schannel_security_ops.name));
return ret;
}
return ret;
}
+37
View File
@@ -0,0 +1,37 @@
/*
Unix SMB/CIFS implementation.
dcerpc schannel operations
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@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 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 "libcli/auth/credentials.h"
enum schannel_position {
SCHANNEL_STATE_START = 0,
SCHANNEL_STATE_UPDATE_1
};
struct schannel_state {
enum schannel_position state;
uint32_t seq_num;
BOOL initiator;
struct creds_CredentialState *creds;
};
+285
View File
@@ -0,0 +1,285 @@
/*
Unix SMB/CIFS implementation.
schannel library code
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 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"
#include "lib/crypto/crypto.h"
#include "auth/auth.h"
#include "auth/gensec/schannel.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
/*******************************************************************
Encode or Decode the sequence number (which is symmetric)
********************************************************************/
static void netsec_deal_with_seq_num(struct schannel_state *state,
const uint8_t packet_digest[8],
uint8_t seq_num[8])
{
static const uint8_t zeros[4];
uint8_t sequence_key[16];
uint8_t digest1[16];
hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
hmac_md5(digest1, packet_digest, 8, sequence_key);
arcfour_crypt(seq_num, sequence_key, 8);
state->seq_num++;
}
/*******************************************************************
Calculate the key with which to encode the data payload
********************************************************************/
static void netsec_get_sealing_key(const uint8_t session_key[16],
const uint8_t seq_num[8],
uint8_t sealing_key[16])
{
static const uint8_t zeros[4];
uint8_t digest2[16];
uint8_t sess_kf0[16];
int i;
for (i = 0; i < 16; i++) {
sess_kf0[i] = session_key[i] ^ 0xf0;
}
hmac_md5(sess_kf0, zeros, 4, digest2);
hmac_md5(digest2, seq_num, 8, sealing_key);
}
/*******************************************************************
Create a digest over the entire packet (including the data), and
MD5 it with the session key.
********************************************************************/
static void schannel_digest(const uint8_t sess_key[16],
const uint8_t netsec_sig[8],
const uint8_t *confounder,
const uint8_t *data, size_t data_len,
uint8_t digest_final[16])
{
uint8_t packet_digest[16];
static const uint8_t zeros[4];
struct MD5Context ctx;
MD5Init(&ctx);
MD5Update(&ctx, zeros, 4);
MD5Update(&ctx, netsec_sig, 8);
if (confounder) {
MD5Update(&ctx, confounder, 8);
}
MD5Update(&ctx, data, data_len);
MD5Final(packet_digest, &ctx);
hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final);
}
/*
unseal a packet
*/
NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t confounder[8];
uint8_t seq_num[8];
uint8_t sealing_key[16];
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
if (sig->length != 32) {
return NT_STATUS_ACCESS_DENIED;
}
memcpy(confounder, sig->data+24, 8);
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0:0x80);
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
arcfour_crypt(confounder, sealing_key, 8);
arcfour_crypt(data, sealing_key, length);
schannel_digest(state->creds->session_key,
netsec_sig, confounder,
data, length, digest_final);
if (memcmp(digest_final, sig->data+16, 8) != 0) {
dump_data_pw("calc digest:", digest_final, 8);
dump_data_pw("wire digest:", sig->data+16, 8);
return NT_STATUS_ACCESS_DENIED;
}
netsec_deal_with_seq_num(state, digest_final, seq_num);
if (memcmp(seq_num, sig->data+8, 8) != 0) {
dump_data_pw("calc seq num:", seq_num, 8);
dump_data_pw("wire seq num:", sig->data+8, 8);
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
/*
check the signature on a packet
*/
NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t seq_num[8];
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
/* w2k sends just 24 bytes and skip the confounder */
if (sig->length != 32 && sig->length != 24) {
return NT_STATUS_ACCESS_DENIED;
}
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0:0x80);
dump_data_pw("seq_num:\n", seq_num, 8);
dump_data_pw("sess_key:\n", state->creds->session_key, 16);
schannel_digest(state->creds->session_key,
netsec_sig, NULL,
data, length, digest_final);
netsec_deal_with_seq_num(state, digest_final, seq_num);
if (memcmp(seq_num, sig->data+8, 8) != 0) {
dump_data_pw("calc seq num:", seq_num, 8);
dump_data_pw("wire seq num:", sig->data+8, 8);
return NT_STATUS_ACCESS_DENIED;
}
if (memcmp(digest_final, sig->data+16, 8) != 0) {
dump_data_pw("calc digest:", digest_final, 8);
dump_data_pw("wire digest:", sig->data+16, 8);
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
/*
seal a packet
*/
NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t confounder[8];
uint8_t seq_num[8];
uint8_t sealing_key[16];
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
generate_random_buffer(confounder, 8);
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0x80:0);
schannel_digest(state->creds->session_key,
netsec_sig, confounder,
data, length, digest_final);
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
arcfour_crypt(confounder, sealing_key, 8);
arcfour_crypt(data, sealing_key, length);
netsec_deal_with_seq_num(state, digest_final, seq_num);
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
memcpy(sig->data, netsec_sig, 8);
memcpy(sig->data+8, seq_num, 8);
memcpy(sig->data+16, digest_final, 8);
memcpy(sig->data+24, confounder, 8);
dump_data_pw("signature:", sig->data+ 0, 8);
dump_data_pw("seq_num :", sig->data+ 8, 8);
dump_data_pw("digest :", sig->data+16, 8);
dump_data_pw("confound :", sig->data+24, 8);
return NT_STATUS_OK;
}
/*
sign a packet
*/
NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t seq_num[8];
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0x80:0);
schannel_digest(state->creds->session_key,
netsec_sig, NULL,
data, length, digest_final);
netsec_deal_with_seq_num(state, digest_final, seq_num);
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
memcpy(sig->data, netsec_sig, 8);
memcpy(sig->data+8, seq_num, 8);
memcpy(sig->data+16, digest_final, 8);
memset(sig->data+24, 0, 8);
dump_data_pw("signature:", sig->data+ 0, 8);
dump_data_pw("seq_num :", sig->data+ 8, 8);
dump_data_pw("digest :", sig->data+16, 8);
dump_data_pw("confound :", sig->data+24, 8);
return NT_STATUS_OK;
}
+285
View File
@@ -0,0 +1,285 @@
/*
Unix SMB/CIFS implementation.
module to store/fetch session keys for the schannel server
Copyright (C) Andrew Tridgell 2004
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"
#include "lib/ldb/include/ldb.h"
#include "lib/ldb/include/ldb_errors.h"
#include "dsdb/samdb/samdb.h"
#include "db_wrap.h"
#include "libcli/auth/libcli_auth.h"
#include "auth/auth.h"
/**
connect to the schannel ldb
*/
struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx)
{
char *path;
struct ldb_context *ldb;
BOOL existed;
const char *init_ldif =
"dn: @ATTRIBUTES\n" \
"computerName: CASE_INSENSITIVE\n" \
"flatname: CASE_INSENSITIVE\n";
path = smbd_tmp_path(mem_ctx, "schannel.ldb");
if (!path) {
return NULL;
}
existed = file_exist(path);
ldb = ldb_wrap_connect(mem_ctx, path, system_session(mem_ctx),
NULL, LDB_FLG_NOSYNC, NULL);
talloc_free(path);
if (!ldb) {
return NULL;
}
if (!existed) {
gendb_add_ldif(ldb, init_ldif);
}
return ldb;
}
/*
remember an established session key for a netr server authentication
use a simple ldb structure
*/
NTSTATUS schannel_store_session_key_ldb(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb,
struct creds_CredentialState *creds)
{
struct ldb_message *msg;
struct ldb_val val, seed, client_state, server_state;
char *f;
char *sct;
int ret;
f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
if (f == NULL) {
return NT_STATUS_NO_MEMORY;
}
sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
if (sct == NULL) {
return NT_STATUS_NO_MEMORY;
}
msg = ldb_msg_new(ldb);
if (msg == NULL) {
return NT_STATUS_NO_MEMORY;
}
msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
if ( ! msg->dn) {
return NT_STATUS_NO_MEMORY;
}
val.data = creds->session_key;
val.length = sizeof(creds->session_key);
seed.data = creds->seed.data;
seed.length = sizeof(creds->seed.data);
client_state.data = creds->client.data;
client_state.length = sizeof(creds->client.data);
server_state.data = creds->server.data;
server_state.length = sizeof(creds->server.data);
ldb_msg_add_string(msg, "objectClass", "schannelState");
ldb_msg_add_value(msg, "sessionKey", &val, NULL);
ldb_msg_add_value(msg, "seed", &seed, NULL);
ldb_msg_add_value(msg, "clientState", &client_state, NULL);
ldb_msg_add_value(msg, "serverState", &server_state, NULL);
ldb_msg_add_string(msg, "negotiateFlags", f);
ldb_msg_add_string(msg, "secureChannelType", sct);
ldb_msg_add_string(msg, "accountName", creds->account_name);
ldb_msg_add_string(msg, "computerName", creds->computer_name);
ldb_msg_add_string(msg, "flatname", creds->domain);
samdb_msg_add_dom_sid(ldb, mem_ctx, msg, "objectSid", creds->sid);
ldb_delete(ldb, msg->dn);
ret = ldb_add(ldb, msg);
if (ret != 0) {
DEBUG(0,("Unable to add %s to session key db - %s\n",
ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb)));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
struct creds_CredentialState *creds)
{
struct ldb_context *ldb;
NTSTATUS nt_status;
int ret;
ldb = schannel_db_connect(mem_ctx);
if (!ldb) {
return NT_STATUS_ACCESS_DENIED;
}
ret = ldb_transaction_start(ldb);
if (ret != 0) {
talloc_free(ldb);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
nt_status = schannel_store_session_key_ldb(mem_ctx, ldb, creds);
if (NT_STATUS_IS_OK(nt_status)) {
ret = ldb_transaction_commit(ldb);
} else {
ret = ldb_transaction_cancel(ldb);
}
if (ret != 0) {
DEBUG(0,("Unable to commit adding credentials for %s to schannel key db - %s\n",
creds->computer_name, ldb_errstring(ldb)));
talloc_free(ldb);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
talloc_free(ldb);
return nt_status;
}
/*
read back a credentials back for a computer
*/
NTSTATUS schannel_fetch_session_key_ldb(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb,
const char *computer_name,
const char *domain,
struct creds_CredentialState **creds)
{
struct ldb_result *res;
int ret;
const struct ldb_val *val;
*creds = talloc_zero(mem_ctx, struct creds_CredentialState);
if (!*creds) {
return NT_STATUS_NO_MEMORY;
}
ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
NULL, LDB_SCOPE_SUBTREE, NULL,
"(&(computerName=%s)(flatname=%s))", computer_name, domain);
if (ret != LDB_SUCCESS) {
DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name, ldb_errstring(ldb)));
return NT_STATUS_INVALID_HANDLE;
}
if (res->count != 1) {
DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name, res->count));
talloc_free(res);
return NT_STATUS_INVALID_HANDLE;
}
val = ldb_msg_find_ldb_val(res->msgs[0], "sessionKey");
if (val == NULL || val->length != 16) {
DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->session_key, val->data, 16);
val = ldb_msg_find_ldb_val(res->msgs[0], "seed");
if (val == NULL || val->length != 8) {
DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->seed.data, val->data, 8);
val = ldb_msg_find_ldb_val(res->msgs[0], "clientState");
if (val == NULL || val->length != 8) {
DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->client.data, val->data, 8);
val = ldb_msg_find_ldb_val(res->msgs[0], "serverState");
if (val == NULL || val->length != 8) {
DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->server.data, val->data, 8);
(*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
(*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
(*creds)->account_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "accountName", NULL));
if ((*creds)->account_name == NULL) {
talloc_free(res);
return NT_STATUS_NO_MEMORY;
}
(*creds)->computer_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "computerName", NULL));
if ((*creds)->computer_name == NULL) {
talloc_free(res);
return NT_STATUS_NO_MEMORY;
}
(*creds)->domain = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "flatname", NULL));
if ((*creds)->domain == NULL) {
talloc_free(res);
return NT_STATUS_NO_MEMORY;
}
(*creds)->sid = samdb_result_dom_sid(*creds, res->msgs[0], "objectSid");
talloc_free(res);
return NT_STATUS_OK;
}
NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
const char *computer_name,
const char *domain,
struct creds_CredentialState **creds)
{
NTSTATUS nt_status;
struct ldb_context *ldb;
ldb = schannel_db_connect(mem_ctx);
if (!ldb) {
return NT_STATUS_ACCESS_DENIED;
}
nt_status = schannel_fetch_session_key_ldb(mem_ctx, ldb,
computer_name, domain,
creds);
talloc_free(ldb);
return nt_status;
}
+530
View File
@@ -0,0 +1,530 @@
/*
Unix SMB/CIFS implementation.
GENSEC socket interface
Copyright (C) Andrew Bartlett 2006
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"
#include "lib/events/events.h"
#include "lib/socket/socket.h"
#include "lib/stream/packet.h"
#include "auth/gensec/gensec.h"
static const struct socket_ops gensec_socket_ops;
struct gensec_socket {
struct gensec_security *gensec_security;
struct socket_context *socket;
struct event_context *ev;
struct packet_context *packet;
DATA_BLOB read_buffer; /* SASL packets are turned into liniarlised data here, for reading */
size_t orig_send_len;
BOOL eof;
NTSTATUS error;
BOOL interrupted;
void (*recv_handler)(void *, uint16_t);
void *recv_private;
int in_extra_read;
BOOL wrap; /* Should we be wrapping on this socket at all? */
};
static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
{
switch (sock->type) {
case SOCKET_TYPE_STREAM:
break;
default:
return NT_STATUS_INVALID_PARAMETER;
}
sock->backend_name = "gensec";
return NT_STATUS_OK;
}
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
_PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
if (!gensec_security->ops->wrap_packets) {
NTSTATUS nt_status;
size_t max_input_size;
DATA_BLOB unwrapped, wrapped;
max_input_size = gensec_max_input_size(gensec_security);
unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));
nt_status = gensec_wrap(gensec_security,
mem_ctx,
&unwrapped, &wrapped);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
*out = data_blob_talloc(mem_ctx, NULL, 4);
if (!out->data) {
return NT_STATUS_NO_MEMORY;
}
RSIVAL(out->data, 0, wrapped.length);
nt_status = data_blob_append(mem_ctx, out, wrapped.data, wrapped.length);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
*len_processed = unwrapped.length;
return nt_status;
}
return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
len_processed);
}
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
if (!gensec_security->ops->unwrap_packets) {
DATA_BLOB wrapped;
NTSTATUS nt_status;
size_t packet_size;
if (in->length < 4) {
/* Missing the header we already had! */
DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n"));
return NT_STATUS_INVALID_PARAMETER;
}
packet_size = RIVAL(in->data, 0);
wrapped = data_blob_const(in->data + 4, packet_size);
if (wrapped.length > (in->length - 4)) {
DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n",
(int)wrapped.length, (int)(in->length - 4)));
return NT_STATUS_INTERNAL_ERROR;
}
nt_status = gensec_unwrap(gensec_security,
mem_ctx,
&wrapped, out);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
*len_processed = packet_size + 4;
return nt_status;
}
return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
len_processed);
}
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
DATA_BLOB blob, size_t *size)
{
if (gensec_security->ops->packet_full_request) {
return gensec_security->ops->packet_full_request(gensec_security,
blob, size);
}
if (gensec_security->ops->unwrap_packets) {
if (blob.length) {
*size = blob.length;
return NT_STATUS_OK;
}
return STATUS_MORE_ENTRIES;
}
return packet_full_request_u32(NULL, blob, size);
}
static NTSTATUS gensec_socket_full_request(void *private, DATA_BLOB blob, size_t *size)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
struct gensec_security *gensec_security = gensec_socket->gensec_security;
return gensec_packet_full_request(gensec_security, blob, size);
}
/* Try to figure out how much data is waiting to be read */
static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending)
{
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
if (!gensec_socket->wrap) {
return socket_pending(gensec_socket->socket, npending);
}
if (gensec_socket->read_buffer.length > 0) {
*npending = gensec_socket->read_buffer.length;
return NT_STATUS_OK;
}
/* This is a lie. We hope the decrypted data will always be
* less than this value, so the application just gets a short
* read. Without reading and decrypting it, we can't tell.
* If the SASL mech does compression, then we just need to
* manually trigger read events */
return socket_pending(gensec_socket->socket, npending);
}
/* Note if an error occours, so we can return it up the stack */
static void gensec_socket_error_handler(void *private, NTSTATUS status)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
gensec_socket->eof = True;
} else {
gensec_socket->error = status;
}
}
static void gensec_socket_trigger_read(struct event_context *ev,
struct timed_event *te,
struct timeval t, void *private)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
gensec_socket->in_extra_read++;
gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
gensec_socket->in_extra_read--;
/* It may well be that, having run the recv handler, we still
* have even more data waiting for us!
*/
if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
/* Schedule this funcion to run again */
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
gensec_socket_trigger_read, gensec_socket);
}
}
/* These two routines could be changed to use a circular buffer of
* some kind, or linked lists, or ... */
static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
size_t wantlen, size_t *nread)
{
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
if (!gensec_socket->wrap) {
return socket_recv(gensec_socket->socket, buf, wantlen, nread);
}
gensec_socket->error = NT_STATUS_OK;
if (gensec_socket->read_buffer.length == 0) {
/* Process any data on the socket, into the read buffer. At
* this point, the socket is not available for read any
* longer */
packet_recv(gensec_socket->packet);
if (gensec_socket->eof) {
*nread = 0;
return NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
return gensec_socket->error;
}
if (gensec_socket->read_buffer.length == 0) {
/* Clearly we don't have the entire SASL packet yet,
* so it has not been written into the buffer */
*nread = 0;
return STATUS_MORE_ENTRIES;
}
}
*nread = MIN(wantlen, gensec_socket->read_buffer.length);
memcpy(buf, gensec_socket->read_buffer.data, *nread);
if (gensec_socket->read_buffer.length > *nread) {
memmove(gensec_socket->read_buffer.data,
gensec_socket->read_buffer.data + *nread,
gensec_socket->read_buffer.length - *nread);
}
gensec_socket->read_buffer.length -= *nread;
gensec_socket->read_buffer.data = talloc_realloc(gensec_socket,
gensec_socket->read_buffer.data,
uint8_t,
gensec_socket->read_buffer.length);
if (gensec_socket->read_buffer.length &&
gensec_socket->in_extra_read == 0 &&
gensec_socket->recv_handler) {
/* Manually call a read event, to get this moving
* again (as the socket should be dry, so the normal
* event handler won't trigger) */
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
gensec_socket_trigger_read, gensec_socket);
}
return NT_STATUS_OK;
}
/* Completed SASL packet callback. When we have a 'whole' SASL
* packet, decrypt it, and add it to the read buffer
*
* This function (and anything under it) MUST NOT call the event system
*/
static NTSTATUS gensec_socket_unwrap(void *private, DATA_BLOB blob)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
DATA_BLOB unwrapped;
NTSTATUS nt_status;
TALLOC_CTX *mem_ctx;
size_t packet_size;
mem_ctx = talloc_new(gensec_socket);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
nt_status = gensec_unwrap_packets(gensec_socket->gensec_security,
mem_ctx,
&blob, &unwrapped,
&packet_size);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
if (packet_size != blob.length) {
DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n"));
return NT_STATUS_INTERNAL_ERROR;
}
/* We could change this into a linked list, and have
* gensec_socket_recv() and gensec_socket_pending() walk the
* linked list */
nt_status = data_blob_append(gensec_socket, &gensec_socket->read_buffer,
unwrapped.data, unwrapped.length);
talloc_free(mem_ctx);
return nt_status;
}
/* when the data is sent, we know we have not been interrupted */
static void send_callback(void *private)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
gensec_socket->interrupted = False;
}
/*
send data, but only as much as we allow in one packet.
If this returns STATUS_MORE_ENTRIES, the caller must retry with
exactly the same data, or a NULL blob.
*/
static NTSTATUS gensec_socket_send(struct socket_context *sock,
const DATA_BLOB *blob, size_t *sendlen)
{
NTSTATUS nt_status;
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
DATA_BLOB wrapped;
TALLOC_CTX *mem_ctx;
if (!gensec_socket->wrap) {
return socket_send(gensec_socket->socket, blob, sendlen);
}
*sendlen = 0;
/* We have have been interupted, so the caller should be
* giving us the same data again. */
if (gensec_socket->interrupted) {
packet_queue_run(gensec_socket->packet);
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
return gensec_socket->error;
} else if (gensec_socket->interrupted) {
return STATUS_MORE_ENTRIES;
} else {
*sendlen = gensec_socket->orig_send_len;
gensec_socket->orig_send_len = 0;
return NT_STATUS_OK;
}
}
mem_ctx = talloc_new(gensec_socket);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
nt_status = gensec_wrap_packets(gensec_socket->gensec_security,
mem_ctx,
blob, &wrapped,
&gensec_socket->orig_send_len);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
gensec_socket->interrupted = True;
gensec_socket->error = NT_STATUS_OK;
nt_status = packet_send_callback(gensec_socket->packet,
wrapped,
send_callback, gensec_socket);
talloc_free(mem_ctx);
packet_queue_run(gensec_socket->packet);
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
return gensec_socket->error;
} else if (gensec_socket->interrupted) {
return STATUS_MORE_ENTRIES;
} else {
*sendlen = gensec_socket->orig_send_len;
gensec_socket->orig_send_len = 0;
return NT_STATUS_OK;
}
}
/* Turn a normal socket into a potentially GENSEC wrapped socket */
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
struct socket_context *current_socket,
struct event_context *ev,
void (*recv_handler)(void *, uint16_t),
void *recv_private,
struct socket_context **new_socket)
{
struct gensec_socket *gensec_socket;
struct socket_context *new_sock;
NTSTATUS nt_status;
nt_status = socket_create_with_ops(current_socket, &gensec_socket_ops, &new_sock,
SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT);
if (!NT_STATUS_IS_OK(nt_status)) {
*new_socket = NULL;
return nt_status;
}
new_sock->state = current_socket->state;
gensec_socket = talloc(new_sock, struct gensec_socket);
if (gensec_socket == NULL) {
*new_socket = NULL;
return NT_STATUS_NO_MEMORY;
}
new_sock->private_data = gensec_socket;
gensec_socket->socket = current_socket;
if (talloc_reference(gensec_socket, current_socket) == NULL) {
*new_socket = NULL;
return NT_STATUS_NO_MEMORY;
}
/* Nothing to do here, if we are not actually wrapping on this socket */
if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
gensec_socket->wrap = False;
*new_socket = new_sock;
return NT_STATUS_OK;
}
gensec_socket->gensec_security = gensec_security;
gensec_socket->wrap = True;
gensec_socket->eof = False;
gensec_socket->error = NT_STATUS_OK;
gensec_socket->interrupted = False;
gensec_socket->in_extra_read = 0;
gensec_socket->read_buffer = data_blob(NULL, 0);
gensec_socket->recv_handler = recv_handler;
gensec_socket->recv_private = recv_private;
gensec_socket->ev = ev;
gensec_socket->packet = packet_init(gensec_socket);
if (gensec_socket->packet == NULL) {
*new_socket = NULL;
return NT_STATUS_NO_MEMORY;
}
packet_set_private(gensec_socket->packet, gensec_socket);
packet_set_socket(gensec_socket->packet, gensec_socket->socket);
packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
packet_set_full_request(gensec_socket->packet, gensec_socket_full_request);
packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
packet_set_serialise(gensec_socket->packet);
/* TODO: full-request that knows about maximum packet size */
*new_socket = new_sock;
return NT_STATUS_OK;
}
static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
{
set_socket_options(socket_get_fd(sock), option);
return NT_STATUS_OK;
}
static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_peer_name(gensec->socket, mem_ctx);
}
static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_peer_addr(gensec->socket, mem_ctx);
}
static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_my_addr(gensec->socket, mem_ctx);
}
static int gensec_socket_get_fd(struct socket_context *sock)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_fd(gensec->socket);
}
static const struct socket_ops gensec_socket_ops = {
.name = "gensec",
.fn_init = gensec_socket_init_fn,
.fn_recv = gensec_socket_recv,
.fn_send = gensec_socket_send,
.fn_pending = gensec_socket_pending,
.fn_set_option = gensec_socket_set_option,
.fn_get_peer_name = gensec_socket_get_peer_name,
.fn_get_peer_addr = gensec_socket_get_peer_addr,
.fn_get_my_addr = gensec_socket_get_my_addr,
.fn_get_fd = gensec_socket_get_fd
};
+51
View File
@@ -0,0 +1,51 @@
/*
Unix SMB/CIFS implementation.
Generic Authentication Interface (socket wrapper)
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
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.
*/
struct gensec_security;
struct socket_context;
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
struct socket_context *current_socket,
struct event_context *ev,
void (*recv_handler)(void *, uint16_t),
void *recv_private,
struct socket_context **new_socket);
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
DATA_BLOB blob, size_t *size);
File diff suppressed because it is too large Load Diff
+66
View File
@@ -0,0 +1,66 @@
/*
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
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.
*/
#define SPNEGO_DELEG_FLAG 0x01
#define SPNEGO_MUTUAL_FLAG 0x02
#define SPNEGO_REPLAY_FLAG 0x04
#define SPNEGO_SEQUENCE_FLAG 0x08
#define SPNEGO_ANON_FLAG 0x10
#define SPNEGO_CONF_FLAG 0x20
#define SPNEGO_INTEG_FLAG 0x40
#define SPNEGO_REQ_FLAG 0x80
enum spnego_negResult {
SPNEGO_ACCEPT_COMPLETED = 0,
SPNEGO_ACCEPT_INCOMPLETE = 1,
SPNEGO_REJECT = 2,
SPNEGO_NONE_RESULT = 3
};
struct spnego_negTokenInit {
const char **mechTypes;
int reqFlags;
DATA_BLOB mechToken;
DATA_BLOB mechListMIC;
char *targetPrincipal;
};
struct spnego_negTokenTarg {
uint8_t negResult;
const char *supportedMech;
DATA_BLOB responseToken;
DATA_BLOB mechListMIC;
};
struct spnego_data {
int type;
struct spnego_negTokenInit negTokenInit;
struct spnego_negTokenTarg negTokenTarg;
};
enum spnego_message_type {
SPNEGO_NEG_TOKEN_INIT = 0,
SPNEGO_NEG_TOKEN_TARG = 1,
};
#include "auth/gensec/spnego_proto.h"
+373
View File
@@ -0,0 +1,373 @@
/*
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
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"
#include "auth/gensec/spnego.h"
#include "auth/gensec/gensec.h"
#include "libcli/util/asn_1.h"
static BOOL read_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
{
ZERO_STRUCTP(token);
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
int i;
uint8_t context;
if (!asn1_peek_uint8(asn1, &context)) {
asn1->has_error = True;
break;
}
switch (context) {
/* Read mechTypes */
case ASN1_CONTEXT(0):
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
token->mechTypes = talloc(NULL, const char *);
for (i = 0; !asn1->has_error &&
0 < asn1_tag_remaining(asn1); i++) {
token->mechTypes = talloc_realloc(NULL,
token->mechTypes,
const char *, i+2);
asn1_read_OID(asn1, token->mechTypes + i);
if (token->mechTypes[i]) {
talloc_steal(token->mechTypes,
token->mechTypes[i]);
}
}
token->mechTypes[i] = NULL;
asn1_end_tag(asn1);
asn1_end_tag(asn1);
break;
/* Read reqFlags */
case ASN1_CONTEXT(1):
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_read_Integer(asn1, &token->reqFlags);
token->reqFlags |= SPNEGO_REQ_FLAG;
asn1_end_tag(asn1);
break;
/* Read mechToken */
case ASN1_CONTEXT(2):
asn1_start_tag(asn1, ASN1_CONTEXT(2));
asn1_read_OctetString(asn1, &token->mechToken);
asn1_end_tag(asn1);
break;
/* Read mecListMIC */
case ASN1_CONTEXT(3):
{
uint8_t type_peek;
asn1_start_tag(asn1, ASN1_CONTEXT(3));
if (!asn1_peek_uint8(asn1, &type_peek)) {
asn1->has_error = True;
break;
}
if (type_peek == ASN1_OCTET_STRING) {
asn1_read_OctetString(asn1,
&token->mechListMIC);
} else {
/* RFC 2478 says we have an Octet String here,
but W2k sends something different... */
char *mechListMIC;
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_read_GeneralString(asn1, &mechListMIC);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
token->targetPrincipal = mechListMIC;
}
asn1_end_tag(asn1);
break;
}
default:
asn1->has_error = True;
break;
}
}
asn1_end_tag(asn1);
asn1_end_tag(asn1);
return !asn1->has_error;
}
static BOOL write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
{
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
/* Write mechTypes */
if (token->mechTypes && *token->mechTypes) {
int i;
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
for (i = 0; token->mechTypes[i]; i++) {
asn1_write_OID(asn1, token->mechTypes[i]);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
}
/* write reqFlags */
if (token->reqFlags & SPNEGO_REQ_FLAG) {
int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_write_Integer(asn1, flags);
asn1_pop_tag(asn1);
}
/* write mechToken */
if (token->mechToken.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(2));
asn1_write_OctetString(asn1, token->mechToken.data,
token->mechToken.length);
asn1_pop_tag(asn1);
}
/* write mechListMIC */
if (token->mechListMIC.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(3));
#if 0
/* This is what RFC 2478 says ... */
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
#else
/* ... but unfortunately this is what Windows
sends/expects */
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_GENERAL_STRING);
asn1_write(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
#endif
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
static BOOL read_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
{
ZERO_STRUCTP(token);
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
uint8_t context;
if (!asn1_peek_uint8(asn1, &context)) {
asn1->has_error = True;
break;
}
switch (context) {
case ASN1_CONTEXT(0):
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_ENUMERATED);
asn1_read_uint8(asn1, &token->negResult);
asn1_end_tag(asn1);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(1):
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_read_OID(asn1, &token->supportedMech);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(2):
asn1_start_tag(asn1, ASN1_CONTEXT(2));
asn1_read_OctetString(asn1, &token->responseToken);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(3):
asn1_start_tag(asn1, ASN1_CONTEXT(3));
asn1_read_OctetString(asn1, &token->mechListMIC);
asn1_end_tag(asn1);
break;
default:
asn1->has_error = True;
break;
}
}
asn1_end_tag(asn1);
asn1_end_tag(asn1);
return !asn1->has_error;
}
static BOOL write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
{
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
if (token->negResult != SPNEGO_NONE_RESULT) {
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_write_enumerated(asn1, token->negResult);
asn1_pop_tag(asn1);
}
if (token->supportedMech) {
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_write_OID(asn1, token->supportedMech);
asn1_pop_tag(asn1);
}
if (token->responseToken.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(2));
asn1_write_OctetString(asn1, token->responseToken.data,
token->responseToken.length);
asn1_pop_tag(asn1);
}
if (token->mechListMIC.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(3));
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
{
struct asn1_data asn1;
ssize_t ret = -1;
uint8_t context;
ZERO_STRUCTP(token);
ZERO_STRUCT(asn1);
if (data.length == 0) {
return ret;
}
asn1_load(&asn1, data);
if (!asn1_peek_uint8(&asn1, &context)) {
asn1.has_error = True;
} else {
switch (context) {
case ASN1_APPLICATION(0):
asn1_start_tag(&asn1, ASN1_APPLICATION(0));
asn1_check_OID(&asn1, GENSEC_OID_SPNEGO);
if (read_negTokenInit(&asn1, &token->negTokenInit)) {
token->type = SPNEGO_NEG_TOKEN_INIT;
}
asn1_end_tag(&asn1);
break;
case ASN1_CONTEXT(1):
if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
token->type = SPNEGO_NEG_TOKEN_TARG;
}
break;
default:
asn1.has_error = True;
break;
}
}
if (!asn1.has_error) ret = asn1.ofs;
asn1_free(&asn1);
return ret;
}
ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
{
struct asn1_data asn1;
ssize_t ret = -1;
ZERO_STRUCT(asn1);
switch (spnego->type) {
case SPNEGO_NEG_TOKEN_INIT:
asn1_push_tag(&asn1, ASN1_APPLICATION(0));
asn1_write_OID(&asn1, GENSEC_OID_SPNEGO);
write_negTokenInit(&asn1, &spnego->negTokenInit);
asn1_pop_tag(&asn1);
break;
case SPNEGO_NEG_TOKEN_TARG:
write_negTokenTarg(&asn1, &spnego->negTokenTarg);
break;
default:
asn1.has_error = True;
break;
}
if (!asn1.has_error) {
*blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
ret = asn1.ofs;
}
asn1_free(&asn1);
return ret;
}
BOOL spnego_free_data(struct spnego_data *spnego)
{
BOOL ret = True;
if (!spnego) goto out;
switch(spnego->type) {
case SPNEGO_NEG_TOKEN_INIT:
if (spnego->negTokenInit.mechTypes) {
talloc_free(spnego->negTokenInit.mechTypes);
}
data_blob_free(&spnego->negTokenInit.mechToken);
data_blob_free(&spnego->negTokenInit.mechListMIC);
talloc_free(spnego->negTokenInit.targetPrincipal);
break;
case SPNEGO_NEG_TOKEN_TARG:
if (spnego->negTokenTarg.supportedMech) {
talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
}
data_blob_free(&spnego->negTokenTarg.responseToken);
data_blob_free(&spnego->negTokenTarg.mechListMIC);
break;
default:
ret = False;
break;
}
ZERO_STRUCTP(spnego);
out:
return ret;
}