mirror of
https://github.com/samba-team/samba.git
synced 2025-11-07 12:23:51 +03:00
r1030: added server side schannel support
This commit is contained in:
committed by
Gerald (Jerry) Carter
parent
ee6c17f373
commit
2ac79dfba0
@@ -23,6 +23,17 @@ interface dcerpc
|
||||
dcerpc_syntax_id transfer_syntaxes[num_transfer_syntaxes];
|
||||
} dcerpc_ctx_list;
|
||||
|
||||
/*
|
||||
a schannel bind blob - used in auth_info
|
||||
on a schannel bind
|
||||
*/
|
||||
typedef [public] struct {
|
||||
uint32 unknown1;
|
||||
uint32 unknown2;
|
||||
astring domain;
|
||||
astring hostname;
|
||||
} dcerpc_bind_schannel;
|
||||
|
||||
typedef struct {
|
||||
uint16 max_xmit_frag;
|
||||
uint16 max_recv_frag;
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
*/
|
||||
#define ascstr_noterm [flag(STR_NOTERM|STR_ASCII|STR_SIZE4|STR_LEN4)] string
|
||||
|
||||
/*
|
||||
a null terminated ascii string
|
||||
*/
|
||||
#define astring [flag(STR_ASCII|STR_NULLTERM)] string
|
||||
|
||||
|
||||
#define NDR_NOALIGN LIBNDR_FLAG_NOALIGN
|
||||
#define NDR_REMAINING LIBNDR_FLAG_REMAINING
|
||||
|
||||
@@ -497,6 +497,17 @@ NTSTATUS ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
|
||||
(*s) = as;
|
||||
break;
|
||||
|
||||
case LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM:
|
||||
len1 = strnlen(ndr->data+ndr->offset, (ndr->data_size - ndr->offset));
|
||||
if (len1+1 <= ndr->data_size - ndr->offset) {
|
||||
len1++;
|
||||
}
|
||||
NDR_ALLOC_N(ndr, as, (len1+1));
|
||||
NDR_CHECK(ndr_pull_bytes(ndr, as, len1));
|
||||
as[len1] = 0;
|
||||
(*s) = as;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ndr_pull_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
|
||||
ndr->flags & LIBNDR_STRING_FLAGS);
|
||||
@@ -639,6 +650,18 @@ NTSTATUS ndr_push_string(struct ndr_push *ndr, int ndr_flags, const char *s)
|
||||
ndr->offset += c_len + 1;
|
||||
break;
|
||||
|
||||
case LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM:
|
||||
NDR_PUSH_NEED_BYTES(ndr, c_len + 1);
|
||||
ret = convert_string(CH_UNIX, CH_DOS,
|
||||
s, s_len+1,
|
||||
ndr->data+ndr->offset, c_len + 1);
|
||||
if (ret == -1) {
|
||||
return ndr_push_error(ndr, NDR_ERR_CHARCNV,
|
||||
"Bad character conversion");
|
||||
}
|
||||
ndr->offset += c_len + 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ndr_push_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
|
||||
ndr->flags & LIBNDR_STRING_FLAGS);
|
||||
|
||||
@@ -173,6 +173,7 @@ NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
|
||||
uint8_t full_session_key[16];
|
||||
struct schannel_state *schannel_state;
|
||||
const char *workgroup, *workstation;
|
||||
struct dcerpc_bind_schannel bind_schannel;
|
||||
|
||||
memcpy(full_session_key, session_key, 8);
|
||||
memset(full_session_key+8, 0, 8);
|
||||
@@ -203,21 +204,17 @@ NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
|
||||
p->auth_info->auth_context_id = random();
|
||||
p->security_state = NULL;
|
||||
|
||||
p->auth_info->credentials = data_blob_talloc(p->mem_ctx,
|
||||
NULL,
|
||||
8 +
|
||||
strlen(workgroup)+1 +
|
||||
strlen(workstation)+1);
|
||||
if (!p->auth_info->credentials.data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
/* TODO: what are these?? */
|
||||
bind_schannel.unknown1 = 0;
|
||||
bind_schannel.unknown2 = 3;
|
||||
bind_schannel.domain = workgroup;
|
||||
bind_schannel.hostname = workstation;
|
||||
|
||||
/* oh, this is ugly! */
|
||||
SIVAL(p->auth_info->credentials.data, 0, 0);
|
||||
SIVAL(p->auth_info->credentials.data, 4, 3);
|
||||
memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
|
||||
memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1,
|
||||
workstation, strlen(workstation)+1);
|
||||
status = ndr_push_struct_blob(&p->auth_info->credentials, p->mem_ctx, &bind_schannel,
|
||||
(ndr_push_flags_fn_t)ndr_push_dcerpc_bind_schannel);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* send the authenticated bind request */
|
||||
status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
|
||||
|
||||
@@ -483,7 +483,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
|
||||
(*p)->flags = binding->flags;
|
||||
|
||||
if (binding->flags & DCERPC_SCHANNEL) {
|
||||
const char *trust_password = secrets_fetch_machine_password();
|
||||
const char *trust_password = NULL; // samdb_fetch_member_password();
|
||||
if (!trust_password) {
|
||||
DEBUG(0,("Unable to fetch machine password\n"));
|
||||
goto done;
|
||||
@@ -635,9 +635,9 @@ NTSTATUS dcerpc_pipe_connect(struct dcerpc_pipe **p,
|
||||
|
||||
|
||||
/*
|
||||
create a secondary dcerpc connection on SMB
|
||||
the secondary connection will be on the same SMB connection, but
|
||||
use a new fnum
|
||||
create a secondary dcerpc connection from a primary SMB connection
|
||||
|
||||
the secondary connection will be on the same SMB connection, but use a new fnum
|
||||
*/
|
||||
NTSTATUS dcerpc_secondary_smb(struct dcerpc_pipe *p, struct dcerpc_pipe **p2,
|
||||
const char *pipe_name,
|
||||
|
||||
@@ -126,6 +126,7 @@ ADD_OBJ_FILES = \
|
||||
rpc_server/dcesrv_auth.o \
|
||||
rpc_server/dcesrv_crypto.o \
|
||||
rpc_server/dcesrv_crypto_ntlmssp.o \
|
||||
rpc_server/dcesrv_crypto_schannel.o \
|
||||
rpc_server/handles.o
|
||||
#
|
||||
# End SUBSYSTEM DCERPC
|
||||
|
||||
@@ -97,7 +97,7 @@ struct dcesrv_handle {
|
||||
struct dcesrv_crypto_ops {
|
||||
const char *name;
|
||||
uint8 auth_type;
|
||||
NTSTATUS (*start)(struct dcesrv_auth *auth);
|
||||
NTSTATUS (*start)(struct dcesrv_auth *auth, DATA_BLOB *auth_blob);
|
||||
NTSTATUS (*update)(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out);
|
||||
NTSTATUS (*session_info)(struct dcesrv_auth *auth, struct auth_session_info **session_info);
|
||||
|
||||
@@ -56,7 +56,7 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
|
||||
return False;
|
||||
}
|
||||
|
||||
status = dcesrv_crypto_start(&dce_conn->auth_state);
|
||||
status = dcesrv_crypto_start(&dce_conn->auth_state, &dce_conn->auth_state.auth_info->credentials);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
@@ -68,9 +68,9 @@ NTSTATUS dcesrv_crypto_select_type(struct dcesrv_connection *dce_conn,
|
||||
/*
|
||||
start crypto state
|
||||
*/
|
||||
NTSTATUS dcesrv_crypto_start(struct dcesrv_auth *auth)
|
||||
NTSTATUS dcesrv_crypto_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob)
|
||||
{
|
||||
return auth->crypto_ctx.ops->start(auth);
|
||||
return auth->crypto_ctx.ops->start(auth, auth_blob);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -138,10 +138,8 @@ void dcesrv_crypto_end(struct dcesrv_auth *auth)
|
||||
const struct dcesrv_crypto_ops *dcesrv_crypto_backend_bytype(uint8_t auth_type)
|
||||
{
|
||||
switch (auth_type) {
|
||||
#if 0
|
||||
case DCERPC_AUTH_TYPE_SCHANNEL:
|
||||
return dcesrv_crypto_schannel_get_ops();
|
||||
#endif
|
||||
case DCERPC_AUTH_TYPE_NTLMSSP:
|
||||
return dcesrv_crypto_ntlmssp_get_ops();
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
this provides a crypto interface to the various backends (such as
|
||||
NTLMSSP and SCHANNEL) for the rpc server code
|
||||
this provides the NTLMSSP backend for server side rpc
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
@@ -32,11 +31,15 @@
|
||||
/*
|
||||
start crypto state
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_ntlmssp_start(struct dcesrv_auth *auth)
|
||||
static NTSTATUS dcesrv_crypto_ntlmssp_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob)
|
||||
{
|
||||
struct auth_ntlmssp_state *ntlmssp = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
/* TODO: we should parse the auth_blob and remember the client
|
||||
hostname and target domain, then check against the auth3
|
||||
bind packet */
|
||||
|
||||
status = auth_ntlmssp_start(&ntlmssp);
|
||||
|
||||
auth->crypto_ctx.private_data = ntlmssp;
|
||||
|
||||
170
source/rpc_server/dcesrv_crypto_schannel.c
Normal file
170
source/rpc_server/dcesrv_crypto_schannel.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
server side dcerpc authentication code - schannel auth/crypto code
|
||||
|
||||
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"
|
||||
|
||||
struct srv_schannel_state {
|
||||
TALLOC_CTX *mem_ctx;
|
||||
struct dcerpc_bind_schannel bind_info;
|
||||
struct schannel_state *state;
|
||||
};
|
||||
|
||||
/*
|
||||
start crypto state
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_schannel_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob)
|
||||
{
|
||||
struct srv_schannel_state *schannel = NULL;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
uint8_t session_key[16];
|
||||
|
||||
mem_ctx = talloc_init("schannel_start");
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
schannel = talloc_p(mem_ctx, struct srv_schannel_state);
|
||||
if (!schannel) {
|
||||
talloc_destroy(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
schannel->mem_ctx = mem_ctx;
|
||||
|
||||
/* parse the schannel startup blob */
|
||||
status = ndr_pull_struct_blob(auth_blob, mem_ctx, &schannel->bind_info,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_dcerpc_bind_schannel);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_destroy(mem_ctx);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* pull the session key for this client */
|
||||
status = schannel_fetch_session_key(mem_ctx, schannel->bind_info.hostname, session_key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_destroy(mem_ctx);
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* start up the schannel server code */
|
||||
status = schannel_start(&schannel->state, session_key, False);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_destroy(mem_ctx);
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
auth->crypto_ctx.private_data = schannel;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
update crypto state
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_schannel_update(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
seal a packet
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_schannel_seal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length, DATA_BLOB *sig)
|
||||
{
|
||||
struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
|
||||
|
||||
return schannel_seal_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
sign a packet
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_schannel_sign(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length, DATA_BLOB *sig)
|
||||
{
|
||||
struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
|
||||
|
||||
return schannel_sign_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
check a packet signature
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_schannel_check_sig(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length, const DATA_BLOB *sig)
|
||||
{
|
||||
struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
|
||||
|
||||
return schannel_check_packet(srv_schannel_state->state, data, length, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
unseal a packet
|
||||
*/
|
||||
static NTSTATUS dcesrv_crypto_schannel_unseal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length, DATA_BLOB *sig)
|
||||
{
|
||||
struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
|
||||
|
||||
return schannel_unseal_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
end crypto state
|
||||
*/
|
||||
static void dcesrv_crypto_schannel_end(struct dcesrv_auth *auth)
|
||||
{
|
||||
struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data;
|
||||
|
||||
if (srv_schannel_state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
schannel_end(&srv_schannel_state->state);
|
||||
|
||||
talloc_destroy(srv_schannel_state->mem_ctx);
|
||||
|
||||
auth->crypto_ctx.private_data = NULL;
|
||||
}
|
||||
|
||||
static const struct dcesrv_crypto_ops dcesrv_crypto_schannel_ops = {
|
||||
.name = "schannel",
|
||||
.auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
|
||||
.start = dcesrv_crypto_schannel_start,
|
||||
.update = dcesrv_crypto_schannel_update,
|
||||
.seal = dcesrv_crypto_schannel_seal,
|
||||
.sign = dcesrv_crypto_schannel_sign,
|
||||
.check_sig = dcesrv_crypto_schannel_check_sig,
|
||||
.unseal = dcesrv_crypto_schannel_unseal,
|
||||
.end = dcesrv_crypto_schannel_end
|
||||
};
|
||||
|
||||
/*
|
||||
startup the cryptographic side of an authenticated dcerpc server
|
||||
*/
|
||||
const struct dcesrv_crypto_ops *dcesrv_crypto_schannel_get_ops(void)
|
||||
{
|
||||
return &dcesrv_crypto_schannel_ops;
|
||||
}
|
||||
@@ -88,13 +88,17 @@ NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
|
||||
ldb_msg_add_value(ldb, &msg, "sessionKey", &val);
|
||||
ldb_msg_add_string(ldb, &msg, "expiry", s);
|
||||
|
||||
ldb_delete(ldb, msg.dn);
|
||||
|
||||
ret = ldb_add(ldb, &msg);
|
||||
ldb_close(ldb);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG(1,("Unable to add %s to session key db - %s\n", msg.dn, ldb_errstring(ldb)));
|
||||
ldb_close(ldb);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
ldb_close(ldb);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -110,13 +114,20 @@ NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_message **res;
|
||||
int ret;
|
||||
const struct ldb_val *val;
|
||||
char *expr=NULL;
|
||||
|
||||
ldb = schannel_db_connect(mem_ctx);
|
||||
if (ldb == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, "(dn=%s)", NULL, &res);
|
||||
expr = talloc_asprintf(mem_ctx, "(dn=%s)", computer_name);
|
||||
if (expr == NULL) {
|
||||
ldb_close(ldb);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, expr, NULL, &res);
|
||||
if (ret != 1) {
|
||||
ldb_close(ldb);
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
|
||||
Reference in New Issue
Block a user