mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
* the RPC-ECHO pipe now works in smbd, as long as the data sizes
don't cause fragmented pdus (I'll add fragments shortly) * change data_blob_talloc() to not zero memory when the 2nd argument is NULL. The zeroing just masks bugs, and can't even allow a DOS attack * modified pidl to ensure that [ref] arguments to the out side of functions are allocated when parsing the in side. This allows rpc backends to assume that [ref] variables are all setup. Doesn't work correctly for [ref] arrays yet * changed DLIST_ADD_END() to take the type instead of a tmp variable. This means you don't need to declare a silly tmp variable in the caller
This commit is contained in:
parent
4929c53bc8
commit
46e0a35819
@ -324,7 +324,6 @@ static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context,
|
|||||||
{
|
{
|
||||||
auth_methods *list = NULL;
|
auth_methods *list = NULL;
|
||||||
auth_methods *t = NULL;
|
auth_methods *t = NULL;
|
||||||
auth_methods *tmp;
|
|
||||||
int i;
|
int i;
|
||||||
NTSTATUS nt_status;
|
NTSTATUS nt_status;
|
||||||
|
|
||||||
@ -358,7 +357,7 @@ static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context,
|
|||||||
if (NT_STATUS_IS_OK(builtin_auth_init_functions[i].init(*auth_context, module_params, &t))) {
|
if (NT_STATUS_IS_OK(builtin_auth_init_functions[i].init(*auth_context, module_params, &t))) {
|
||||||
DEBUG(5,("make_auth_context_text_list: auth method %s has a valid init\n",
|
DEBUG(5,("make_auth_context_text_list: auth method %s has a valid init\n",
|
||||||
*text_list));
|
*text_list));
|
||||||
DLIST_ADD_END(list, t, tmp);
|
DLIST_ADD_END(list, t, auth_methods *);
|
||||||
} else {
|
} else {
|
||||||
DEBUG(0,("make_auth_context_text_list: auth method %s did not correctly init\n",
|
DEBUG(0,("make_auth_context_text_list: auth method %s did not correctly init\n",
|
||||||
*text_list));
|
*text_list));
|
||||||
|
@ -208,7 +208,6 @@ static struct chat_struct *make_pw_chat(char *p)
|
|||||||
fstring reply;
|
fstring reply;
|
||||||
struct chat_struct *list = NULL;
|
struct chat_struct *list = NULL;
|
||||||
struct chat_struct *t;
|
struct chat_struct *t;
|
||||||
struct chat_struct *tmp;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
t = (struct chat_struct *)malloc(sizeof(*t));
|
t = (struct chat_struct *)malloc(sizeof(*t));
|
||||||
@ -219,7 +218,7 @@ static struct chat_struct *make_pw_chat(char *p)
|
|||||||
|
|
||||||
ZERO_STRUCTP(t);
|
ZERO_STRUCTP(t);
|
||||||
|
|
||||||
DLIST_ADD_END(list, t, tmp);
|
DLIST_ADD_END(list, t, struct chat_struct *);
|
||||||
|
|
||||||
if (!next_token(&p, prompt, NULL, sizeof(fstring)))
|
if (!next_token(&p, prompt, NULL, sizeof(fstring)))
|
||||||
break;
|
break;
|
||||||
|
@ -1256,6 +1256,11 @@ sub ParseFunctionPull($)
|
|||||||
if (util::has_property($e, "in")) {
|
if (util::has_property($e, "in")) {
|
||||||
ParseFunctionElementPull($e, "in");
|
ParseFunctionElementPull($e, "in");
|
||||||
}
|
}
|
||||||
|
# we need to allocate any reference output variables, so that
|
||||||
|
# a dcerpc backend can be sure they are non-null
|
||||||
|
if (util::has_property($e, "out") && util::has_property($e, "ref")) {
|
||||||
|
pidl "\tNDR_ALLOC(ndr, r->out.$e->{NAME});\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pidl "\nndr_out:\n";
|
pidl "\nndr_out:\n";
|
||||||
|
@ -114,5 +114,4 @@ struct cli_client
|
|||||||
#define CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK 0x0004
|
#define CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK 0x0004
|
||||||
#define CLI_FULL_CONNECTION_USE_DFS 0x0008
|
#define CLI_FULL_CONNECTION_USE_DFS 0x0008
|
||||||
|
|
||||||
#include "cli_context.h"
|
|
||||||
#endif /* _CLIENT_H */
|
#endif /* _CLIENT_H */
|
||||||
|
@ -42,6 +42,44 @@ struct user_context {
|
|||||||
struct user_struct *vuser;
|
struct user_struct *vuser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* each backend has to be one one of the following 3 basic types. In
|
||||||
|
* earlier versions of Samba backends needed to handle all types, now
|
||||||
|
* we implement them separately. */
|
||||||
|
enum ntvfs_type {NTVFS_DISK, NTVFS_PRINT, NTVFS_IPC};
|
||||||
|
|
||||||
|
/* we need a forward declaration of the ntvfs_ops strucutre to prevent
|
||||||
|
include recursion */
|
||||||
|
struct ntvfs_ops;
|
||||||
|
|
||||||
|
struct tcon_context {
|
||||||
|
struct tcon_context *next, *prev;
|
||||||
|
|
||||||
|
/* the server context that this was created on */
|
||||||
|
struct server_context *smb;
|
||||||
|
|
||||||
|
/* a talloc context for all data in this structure */
|
||||||
|
TALLOC_CTX *mem_ctx;
|
||||||
|
|
||||||
|
/* a private structure used by the active NTVFS backend */
|
||||||
|
void *ntvfs_private;
|
||||||
|
|
||||||
|
uint16 cnum; /* an index passed over the wire (the TID) */
|
||||||
|
int service;
|
||||||
|
enum ntvfs_type type;
|
||||||
|
BOOL read_only;
|
||||||
|
BOOL admin_user;
|
||||||
|
|
||||||
|
/* the NTVFS operations - see source/ntvfs/ and include/ntvfs.h for details */
|
||||||
|
struct ntvfs_ops *ntvfs_ops;
|
||||||
|
|
||||||
|
/* the reported filesystem type */
|
||||||
|
char *fs_type;
|
||||||
|
|
||||||
|
/* the reported device type */
|
||||||
|
char *dev_type;
|
||||||
|
};
|
||||||
|
|
||||||
/* the context for a single SMB request. This is passed to any request-context
|
/* the context for a single SMB request. This is passed to any request-context
|
||||||
functions */
|
functions */
|
||||||
struct request_context {
|
struct request_context {
|
||||||
@ -343,3 +381,4 @@ struct server_context {
|
|||||||
struct model_ops *model_ops;
|
struct model_ops *model_ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,16 +57,17 @@ do { \
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* hook into the end of the list - needs a tmp pointer */
|
/* hook into the end of the list - needs a tmp pointer */
|
||||||
#define DLIST_ADD_END(list, p, tmp) \
|
#define DLIST_ADD_END(list, p, type) \
|
||||||
do { \
|
do { \
|
||||||
if (!(list)) { \
|
if (!(list)) { \
|
||||||
(list) = (p); \
|
(list) = (p); \
|
||||||
(p)->next = (p)->prev = NULL; \
|
(p)->next = (p)->prev = NULL; \
|
||||||
} else { \
|
} else { \
|
||||||
for ((tmp) = (list); (tmp)->next; (tmp) = (tmp)->next) ; \
|
type tmp; \
|
||||||
(tmp)->next = (p); \
|
for (tmp = (list); tmp->next; tmp = tmp->next) ; \
|
||||||
|
tmp->next = (p); \
|
||||||
(p)->next = NULL; \
|
(p)->next = NULL; \
|
||||||
(p)->prev = (tmp); \
|
(p)->prev = tmp; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -773,6 +773,11 @@ extern int errno;
|
|||||||
#include "librpc/ndr/libndr.h"
|
#include "librpc/ndr/libndr.h"
|
||||||
#include "librpc/rpc/dcerpc.h"
|
#include "librpc/rpc/dcerpc.h"
|
||||||
|
|
||||||
|
#include "rpc_server/dcerpc_server.h"
|
||||||
|
#include "context.h"
|
||||||
|
#include "ntvfs.h"
|
||||||
|
#include "cli_context.h"
|
||||||
|
|
||||||
|
|
||||||
/* used in net.c */
|
/* used in net.c */
|
||||||
struct functable {
|
struct functable {
|
||||||
|
@ -23,12 +23,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* each backend has to be one one of the following 3 basic types. In
|
|
||||||
* earlier versions of Samba backends needed to handle all types, now
|
|
||||||
* we implement them separately. */
|
|
||||||
enum ntvfs_type {NTVFS_DISK, NTVFS_PRINT, NTVFS_IPC};
|
|
||||||
|
|
||||||
|
|
||||||
/* the ntvfs operations structure - contains function pointers to
|
/* the ntvfs operations structure - contains function pointers to
|
||||||
the backend implementations of each operation */
|
the backend implementations of each operation */
|
||||||
struct ntvfs_ops {
|
struct ntvfs_ops {
|
||||||
|
@ -421,10 +421,7 @@ struct vuid_cache {
|
|||||||
#include "smb_acls.h"
|
#include "smb_acls.h"
|
||||||
#include "enums.h"
|
#include "enums.h"
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
#include "rpc_server/dcerpc_server.h"
|
|
||||||
#include "context.h"
|
|
||||||
#include "smb_interfaces.h"
|
#include "smb_interfaces.h"
|
||||||
#include "ntvfs.h"
|
|
||||||
|
|
||||||
typedef struct smb_vfs_handle_struct
|
typedef struct smb_vfs_handle_struct
|
||||||
{
|
{
|
||||||
@ -435,34 +432,6 @@ typedef struct smb_vfs_handle_struct
|
|||||||
|
|
||||||
} smb_vfs_handle_struct;
|
} smb_vfs_handle_struct;
|
||||||
|
|
||||||
struct tcon_context {
|
|
||||||
struct tcon_context *next, *prev;
|
|
||||||
|
|
||||||
/* the server context that this was created on */
|
|
||||||
struct server_context *smb;
|
|
||||||
|
|
||||||
/* a talloc context for all data in this structure */
|
|
||||||
TALLOC_CTX *mem_ctx;
|
|
||||||
|
|
||||||
/* a private structure used by the active NTVFS backend */
|
|
||||||
void *ntvfs_private;
|
|
||||||
|
|
||||||
uint16 cnum; /* an index passed over the wire (the TID) */
|
|
||||||
int service;
|
|
||||||
enum ntvfs_type type;
|
|
||||||
BOOL read_only;
|
|
||||||
BOOL admin_user;
|
|
||||||
|
|
||||||
/* the NTVFS operations - see source/ntvfs/ and include/ntvfs.h for details */
|
|
||||||
struct ntvfs_ops *ntvfs_ops;
|
|
||||||
|
|
||||||
/* the reported filesystem type */
|
|
||||||
char *fs_type;
|
|
||||||
|
|
||||||
/* the reported device type */
|
|
||||||
char *dev_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct current_user
|
struct current_user
|
||||||
{
|
{
|
||||||
struct tcon_context *conn;
|
struct tcon_context *conn;
|
||||||
|
@ -67,12 +67,12 @@ DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
|
/* note that we do NOT zero memory in this case */
|
||||||
ret.data = talloc(mem_ctx, length);
|
ret.data = talloc(mem_ctx, length);
|
||||||
if (ret.data == NULL) {
|
if (ret.data == NULL) {
|
||||||
smb_panic("data_blob_talloc: talloc_memdup failed.\n");
|
smb_panic("data_blob_talloc: talloc_memdup failed.\n");
|
||||||
}
|
}
|
||||||
ret.length = length;
|
ret.length = length;
|
||||||
memset(ret.data, 0, ret.length);
|
|
||||||
ret.free = NULL;
|
ret.free = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -710,7 +710,6 @@ TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
|
|||||||
TDB_DATA key, next;
|
TDB_DATA key, next;
|
||||||
TDB_LIST_NODE *list = NULL;
|
TDB_LIST_NODE *list = NULL;
|
||||||
TDB_LIST_NODE *rec = NULL;
|
TDB_LIST_NODE *rec = NULL;
|
||||||
TDB_LIST_NODE *tmp = NULL;
|
|
||||||
|
|
||||||
for (key = tdb_firstkey(tdb); key.dptr; key = next) {
|
for (key = tdb_firstkey(tdb); key.dptr; key = next) {
|
||||||
/* duplicate key string to ensure null-termination */
|
/* duplicate key string to ensure null-termination */
|
||||||
@ -731,7 +730,7 @@ TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
|
|||||||
|
|
||||||
rec->node_key = key;
|
rec->node_key = key;
|
||||||
|
|
||||||
DLIST_ADD_END(list, rec, tmp);
|
DLIST_ADD_END(list, rec, TDB_LIST_NODE *);
|
||||||
|
|
||||||
DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
|
DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,6 +81,10 @@ interface dcerpc
|
|||||||
|
|
||||||
const int DCERPC_FAULT_OP_RNG_ERROR = 0x1c010002;
|
const int DCERPC_FAULT_OP_RNG_ERROR = 0x1c010002;
|
||||||
|
|
||||||
|
/* we return this fault when we haven't yet run the test
|
||||||
|
to see what fault w2k3 returns in this case */
|
||||||
|
const int DCERPC_FAULT_TODO = 0x00000042;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32 alloc_hint;
|
uint32 alloc_hint;
|
||||||
uint16 context_id;
|
uint16 context_id;
|
||||||
|
@ -43,7 +43,7 @@ size_t ndr_align_size(uint32 offset, size_t n)
|
|||||||
/*
|
/*
|
||||||
initialise a ndr parse structure from a data blob
|
initialise a ndr parse structure from a data blob
|
||||||
*/
|
*/
|
||||||
struct ndr_pull *ndr_pull_init_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
|
struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
|
||||||
{
|
{
|
||||||
struct ndr_pull *ndr;
|
struct ndr_pull *ndr;
|
||||||
|
|
||||||
|
@ -526,6 +526,11 @@ static NTSTATUS ipc_trans(struct request_context *req, struct smb_trans2 *trans)
|
|||||||
return NT_STATUS_INVALID_HANDLE;
|
return NT_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trans->out.data = data_blob_talloc(req->mem_ctx, NULL, trans->in.max_data);
|
||||||
|
if (!trans->out.data.data) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
/* pass the data to the dcerpc server. Note that we don't
|
/* pass the data to the dcerpc server. Note that we don't
|
||||||
expect this to fail, and things like NDR faults are not
|
expect this to fail, and things like NDR faults are not
|
||||||
reported at this stage. Those sorts of errors happen in the
|
reported at this stage. Those sorts of errors happen in the
|
||||||
|
@ -385,7 +385,7 @@ static NTSTATUS make_pdb_context(struct pdb_context **context)
|
|||||||
NTSTATUS make_pdb_context_list(struct pdb_context **context, const char **selected)
|
NTSTATUS make_pdb_context_list(struct pdb_context **context, const char **selected)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
struct pdb_methods *curmethods, *tmpmethods;
|
struct pdb_methods *curmethods;
|
||||||
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
|
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
|
||||||
|
|
||||||
if (!NT_STATUS_IS_OK(nt_status = make_pdb_context(context))) {
|
if (!NT_STATUS_IS_OK(nt_status = make_pdb_context(context))) {
|
||||||
@ -401,7 +401,7 @@ NTSTATUS make_pdb_context_list(struct pdb_context **context, const char **select
|
|||||||
return nt_status;
|
return nt_status;
|
||||||
}
|
}
|
||||||
curmethods->parent = *context;
|
curmethods->parent = *context;
|
||||||
DLIST_ADD_END((*context)->pdb_methods, curmethods, tmpmethods);
|
DLIST_ADD_END((*context)->pdb_methods, curmethods, struct pdb_methods *);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ static BOOL copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from )
|
|||||||
|
|
||||||
static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg)
|
static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg)
|
||||||
{
|
{
|
||||||
struct notify_queue *pnqueue, *tmp_ptr;
|
struct notify_queue *pnqueue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure we only have one message unique to each name/type/field/id/flags
|
* Ensure we only have one message unique to each name/type/field/id/flags
|
||||||
@ -262,7 +262,7 @@ to notify_queue_head\n", msg->type, msg->field, msg->printer));
|
|||||||
* the messages are sent in the order they were received. JRA.
|
* the messages are sent in the order they were received. JRA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr);
|
DLIST_ADD_END(notify_queue_head, pnqueue, struct notify_queue *);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_notify_field_values(const char *printer_name, uint32 type,
|
static void send_notify_field_values(const char *printer_name, uint32 type,
|
||||||
|
@ -30,7 +30,7 @@ static const struct dcesrv_endpoint_ops *find_endpoint(struct server_context *sm
|
|||||||
{
|
{
|
||||||
struct dce_endpoint *ep;
|
struct dce_endpoint *ep;
|
||||||
for (ep=smb->dcesrv.endpoint_list; ep; ep=ep->next) {
|
for (ep=smb->dcesrv.endpoint_list; ep; ep=ep->next) {
|
||||||
if (ep->endpoint_ops->query(endpoint)) {
|
if (ep->endpoint_ops->query_endpoint(endpoint)) {
|
||||||
return ep->endpoint_ops;
|
return ep->endpoint_ops;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +86,7 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
|
|||||||
(*p)->endpoint = *endpoint;
|
(*p)->endpoint = *endpoint;
|
||||||
(*p)->ops = ops;
|
(*p)->ops = ops;
|
||||||
(*p)->private = NULL;
|
(*p)->private = NULL;
|
||||||
|
(*p)->call_list = NULL;
|
||||||
|
|
||||||
/* make sure the endpoint server likes the connection */
|
/* make sure the endpoint server likes the connection */
|
||||||
status = ops->connect(*p);
|
status = ops->connect(*p);
|
||||||
@ -107,23 +108,318 @@ void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
|
|||||||
talloc_destroy(p->mem_ctx);
|
talloc_destroy(p->mem_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
return a dcerpc fault
|
||||||
|
*/
|
||||||
|
static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
|
||||||
|
{
|
||||||
|
struct ndr_push *push;
|
||||||
|
struct dcerpc_packet pkt;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
/* setup a bind_ack */
|
||||||
|
pkt.rpc_vers = 5;
|
||||||
|
pkt.rpc_vers_minor = 0;
|
||||||
|
pkt.drep[0] = 0x10; /* Little endian */
|
||||||
|
pkt.drep[1] = 0;
|
||||||
|
pkt.drep[2] = 0;
|
||||||
|
pkt.drep[3] = 0;
|
||||||
|
pkt.auth_length = 0;
|
||||||
|
pkt.call_id = call->pkt.call_id;
|
||||||
|
pkt.ptype = DCERPC_PKT_FAULT;
|
||||||
|
pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
|
||||||
|
pkt.u.fault.alloc_hint = 0;
|
||||||
|
pkt.u.fault.context_id = 0;
|
||||||
|
pkt.u.fault.cancel_count = 0;
|
||||||
|
pkt.u.fault.status = fault_code;
|
||||||
|
|
||||||
|
/* now form the NDR for the bind_ack */
|
||||||
|
push = ndr_push_init_ctx(call->mem_ctx);
|
||||||
|
if (!push) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
call->data = ndr_push_blob(push);
|
||||||
|
SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length);
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
handle a bind request
|
||||||
|
*/
|
||||||
|
static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
|
||||||
|
{
|
||||||
|
const char *uuid, *transfer_syntax;
|
||||||
|
uint32 if_version, transfer_syntax_version;
|
||||||
|
struct dcerpc_packet pkt;
|
||||||
|
struct ndr_push *push;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
if (call->pkt.u.bind.num_contexts != 1 ||
|
||||||
|
call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.major_version;
|
||||||
|
uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
|
||||||
|
if (!uuid) {
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].major_version;
|
||||||
|
transfer_syntax = GUID_string(call->mem_ctx,
|
||||||
|
&call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
|
||||||
|
if (!transfer_syntax ||
|
||||||
|
strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
|
||||||
|
NDR_GUID_VERSION != transfer_syntax_version) {
|
||||||
|
/* we only do NDR encoded dcerpc */
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
|
||||||
|
DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
|
||||||
|
/* we don't know about that interface */
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup a bind_ack */
|
||||||
|
pkt.rpc_vers = 5;
|
||||||
|
pkt.rpc_vers_minor = 0;
|
||||||
|
pkt.drep[0] = 0x10; /* Little endian */
|
||||||
|
pkt.drep[1] = 0;
|
||||||
|
pkt.drep[2] = 0;
|
||||||
|
pkt.drep[3] = 0;
|
||||||
|
pkt.auth_length = 0;
|
||||||
|
pkt.call_id = call->pkt.call_id;
|
||||||
|
pkt.ptype = DCERPC_PKT_BIND_ACK;
|
||||||
|
pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
|
||||||
|
pkt.u.bind_ack.max_xmit_frag = 0x2000;
|
||||||
|
pkt.u.bind_ack.max_recv_frag = 0x2000;
|
||||||
|
pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
|
||||||
|
pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s",
|
||||||
|
call->dce->ndr->name);
|
||||||
|
pkt.u.bind_ack.num_results = 1;
|
||||||
|
pkt.u.bind_ack.ctx_list = talloc(call->mem_ctx, sizeof(struct dcerpc_ack_ctx));
|
||||||
|
if (!pkt.u.bind_ack.ctx_list) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
pkt.u.bind_ack.ctx_list[0].result = 0;
|
||||||
|
pkt.u.bind_ack.ctx_list[0].reason = 0;
|
||||||
|
GUID_from_string(uuid, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
|
||||||
|
pkt.u.bind_ack.ctx_list[0].syntax.major_version = if_version;
|
||||||
|
pkt.u.bind_ack.ctx_list[0].syntax.minor_version = 0;
|
||||||
|
pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
|
||||||
|
|
||||||
|
/* now form the NDR for the bind_ack */
|
||||||
|
push = ndr_push_init_ctx(call->mem_ctx);
|
||||||
|
if (!push) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
call->data = ndr_push_blob(push);
|
||||||
|
SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length);
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
handle a dcerpc request packet
|
||||||
|
*/
|
||||||
|
static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
|
||||||
|
{
|
||||||
|
struct ndr_pull *pull;
|
||||||
|
struct ndr_push *push;
|
||||||
|
uint16 opnum;
|
||||||
|
void *r;
|
||||||
|
NTSTATUS status;
|
||||||
|
DATA_BLOB stub;
|
||||||
|
struct dcerpc_packet pkt;
|
||||||
|
|
||||||
|
if (call->pkt.pfc_flags != (DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST)) {
|
||||||
|
/* we don't do fragments in the server yet */
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
opnum = call->pkt.u.request.opnum;
|
||||||
|
|
||||||
|
if (opnum >= call->dce->ndr->num_calls) {
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
|
||||||
|
if (!pull) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
|
||||||
|
if (!r) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unravel the NDR for the packet */
|
||||||
|
status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call the dispatch function */
|
||||||
|
status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* form the reply NDR */
|
||||||
|
push = ndr_push_init_ctx(call->mem_ctx);
|
||||||
|
if (!push) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return dcesrv_fault(call, DCERPC_FAULT_TODO);
|
||||||
|
}
|
||||||
|
|
||||||
|
stub = ndr_push_blob(push);
|
||||||
|
|
||||||
|
/* form the dcerpc response packet */
|
||||||
|
pkt.rpc_vers = 5;
|
||||||
|
pkt.rpc_vers_minor = 0;
|
||||||
|
pkt.drep[0] = 0x10; /* Little endian */
|
||||||
|
pkt.drep[1] = 0;
|
||||||
|
pkt.drep[2] = 0;
|
||||||
|
pkt.drep[3] = 0;
|
||||||
|
pkt.auth_length = 0;
|
||||||
|
pkt.call_id = call->pkt.call_id;
|
||||||
|
pkt.ptype = DCERPC_PKT_RESPONSE;
|
||||||
|
pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
|
||||||
|
pkt.u.response.alloc_hint = stub.length;
|
||||||
|
pkt.u.response.context_id = call->pkt.u.request.context_id;
|
||||||
|
pkt.u.response.cancel_count = 0;
|
||||||
|
pkt.u.response.stub_and_verifier = stub;
|
||||||
|
|
||||||
|
push = ndr_push_init_ctx(call->mem_ctx);
|
||||||
|
if (!push) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
call->data = ndr_push_blob(push);
|
||||||
|
SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length);
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
provide some input to a dcerpc endpoint server. This passes data
|
provide some input to a dcerpc endpoint server. This passes data
|
||||||
from a dcerpc client into the server
|
from a dcerpc client into the server
|
||||||
*/
|
*/
|
||||||
NTSTATUS dcesrv_input(struct dcesrv_state *p, const DATA_BLOB *data)
|
NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
|
||||||
{
|
{
|
||||||
return NT_STATUS_NOT_IMPLEMENTED;
|
struct ndr_pull *ndr;
|
||||||
|
TALLOC_CTX *mem_ctx;
|
||||||
|
NTSTATUS status;
|
||||||
|
struct dcesrv_call_state *call;
|
||||||
|
|
||||||
|
mem_ctx = talloc_init("dcesrv_input");
|
||||||
|
if (!mem_ctx) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
call = talloc(mem_ctx, sizeof(*call));
|
||||||
|
if (!call) {
|
||||||
|
talloc_destroy(mem_ctx);
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
call->mem_ctx = mem_ctx;
|
||||||
|
call->dce = dce;
|
||||||
|
|
||||||
|
ndr = ndr_pull_init_blob(data, mem_ctx);
|
||||||
|
if (!ndr) {
|
||||||
|
talloc_destroy(mem_ctx);
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
talloc_destroy(mem_ctx);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: at this point we should see if the packet is a
|
||||||
|
continuation of an existing call, but I'm too lazy for that
|
||||||
|
right now ... maybe tomorrow */
|
||||||
|
|
||||||
|
|
||||||
|
switch (call->pkt.ptype) {
|
||||||
|
case DCERPC_PKT_BIND:
|
||||||
|
status = dcesrv_bind(call);
|
||||||
|
break;
|
||||||
|
case DCERPC_PKT_REQUEST:
|
||||||
|
status = dcesrv_request(call);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
status = NT_STATUS_INVALID_PARAMETER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we are going to be sending a reply then add
|
||||||
|
it to the list of pending calls. We add it to the end to keep the call
|
||||||
|
list in the order we will answer */
|
||||||
|
if (NT_STATUS_IS_OK(status)) {
|
||||||
|
DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
|
||||||
|
} else {
|
||||||
|
talloc_destroy(mem_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
retrieve some output from a dcerpc server. The amount of data that
|
retrieve some output from a dcerpc server. The amount of data that
|
||||||
is wanted is in data->length
|
is wanted is in data->length and data->data is already allocated
|
||||||
|
to hold that much data.
|
||||||
*/
|
*/
|
||||||
NTSTATUS dcesrv_output(struct dcesrv_state *p, DATA_BLOB *data)
|
NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
|
||||||
{
|
{
|
||||||
return NT_STATUS_NOT_IMPLEMENTED;
|
struct dcesrv_call_state *call;
|
||||||
|
|
||||||
|
call = dce->call_list;
|
||||||
|
if (!call) {
|
||||||
|
return NT_STATUS_FOOBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->length >= call->data.length) {
|
||||||
|
data->length = call->data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data->data, call->data.data, data->length);
|
||||||
|
call->data.length -= data->length;
|
||||||
|
call->data.data += data->length;
|
||||||
|
|
||||||
|
if (call->data.length == 0) {
|
||||||
|
/* we're done with this call */
|
||||||
|
DLIST_REMOVE(dce->call_list, call);
|
||||||
|
talloc_destroy(call->mem_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +32,19 @@ struct dcesrv_endpoint {
|
|||||||
} info;
|
} info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dcesrv_state;
|
||||||
|
|
||||||
|
/* the dispatch functions for an interface take this form */
|
||||||
|
typedef NTSTATUS (*dcesrv_dispatch_fn_t)(struct dcesrv_state *, TALLOC_CTX *, void *);
|
||||||
|
|
||||||
|
/* the state of an ongoing dcerpc call */
|
||||||
|
struct dcesrv_call_state {
|
||||||
|
struct dcesrv_call_state *next, *prev;
|
||||||
|
struct dcesrv_state *dce;
|
||||||
|
TALLOC_CTX *mem_ctx;
|
||||||
|
struct dcerpc_packet pkt;
|
||||||
|
DATA_BLOB data;
|
||||||
|
};
|
||||||
|
|
||||||
/* the state associated with a dcerpc server connection */
|
/* the state associated with a dcerpc server connection */
|
||||||
struct dcesrv_state {
|
struct dcesrv_state {
|
||||||
@ -43,15 +56,29 @@ struct dcesrv_state {
|
|||||||
/* endpoint operations provided by the endpoint server */
|
/* endpoint operations provided by the endpoint server */
|
||||||
const struct dcesrv_endpoint_ops *ops;
|
const struct dcesrv_endpoint_ops *ops;
|
||||||
|
|
||||||
|
/* the ndr function table for the chosen interface */
|
||||||
|
const struct dcerpc_interface_table *ndr;
|
||||||
|
|
||||||
|
/* the dispatch table for the chosen interface. Must contain
|
||||||
|
enough entries for all entries in the ndr table */
|
||||||
|
const dcesrv_dispatch_fn_t *dispatch;
|
||||||
|
|
||||||
|
/* the state of the current calls */
|
||||||
|
struct dcesrv_call_state *call_list;
|
||||||
|
|
||||||
/* private data for the endpoint server */
|
/* private data for the endpoint server */
|
||||||
void *private;
|
void *private;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct dcesrv_endpoint_ops {
|
struct dcesrv_endpoint_ops {
|
||||||
/* the query function is used to ask an endpoint server if it
|
/* this function is used to ask an endpoint server if it
|
||||||
handles a particular endpoint */
|
handles a particular endpoint */
|
||||||
BOOL (*query)(const struct dcesrv_endpoint *);
|
BOOL (*query_endpoint)(const struct dcesrv_endpoint *);
|
||||||
|
|
||||||
|
/* this function sets up the dispatch table for this
|
||||||
|
connection */
|
||||||
|
BOOL (*set_interface)(struct dcesrv_state *, const char *, uint32);
|
||||||
|
|
||||||
/* connect() is called when a connection is made to an endpoint */
|
/* connect() is called when a connection is made to an endpoint */
|
||||||
NTSTATUS (*connect)(struct dcesrv_state *);
|
NTSTATUS (*connect)(struct dcesrv_state *);
|
||||||
|
@ -23,20 +23,94 @@
|
|||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
|
|
||||||
|
static NTSTATUS echo_AddOne(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_AddOne *r)
|
||||||
|
{
|
||||||
|
*r->out.v = *r->in.v + 1;
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS echo_EchoData(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_EchoData *r)
|
||||||
|
{
|
||||||
|
r->out.out_data = talloc(mem_ctx, r->in.len);
|
||||||
|
if (!r->out.out_data) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
memcpy(r->out.out_data, r->in.in_data, r->in.len);
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS echo_SinkData(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_SinkData *r)
|
||||||
|
{
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS echo_SourceData(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct echo_SourceData *r)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
r->out.data = talloc(mem_ctx, r->in.len);
|
||||||
|
if (!r->out.data) {
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
for (i=0;i<r->in.len;i++) {
|
||||||
|
r->out.data[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS echo_TestCall(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct TestCall *r)
|
||||||
|
{
|
||||||
|
return NT_STATUS_BAD_NETWORK_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS echo_TestCall2(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx, struct TestCall2 *r)
|
||||||
|
{
|
||||||
|
return NT_STATUS_BAD_NETWORK_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
all the code below this point is boilerplate that will be auto-generated
|
all the code below this point is boilerplate that will be auto-generated
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
static const dcesrv_dispatch_fn_t dispatch_table[] = {
|
||||||
|
(dcesrv_dispatch_fn_t)echo_AddOne,
|
||||||
|
(dcesrv_dispatch_fn_t)echo_EchoData,
|
||||||
|
(dcesrv_dispatch_fn_t)echo_SinkData,
|
||||||
|
(dcesrv_dispatch_fn_t)echo_SourceData,
|
||||||
|
(dcesrv_dispatch_fn_t)echo_TestCall,
|
||||||
|
(dcesrv_dispatch_fn_t)echo_TestCall2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
return True if we want to handle the given endpoint
|
return True if we want to handle the given endpoint
|
||||||
*/
|
*/
|
||||||
static BOOL op_query(const struct dcesrv_endpoint *ep)
|
static BOOL op_query_endpoint(const struct dcesrv_endpoint *ep)
|
||||||
{
|
{
|
||||||
return dcesrv_table_query(&dcerpc_table_rpcecho, ep);
|
return dcesrv_table_query(&dcerpc_table_rpcecho, ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
setup for a particular rpc interface
|
||||||
|
*/
|
||||||
|
static BOOL op_set_interface(struct dcesrv_state *dce, const char *uuid, uint32 if_version)
|
||||||
|
{
|
||||||
|
if (strcasecmp(uuid, dcerpc_table_rpcecho.uuid) != 0 ||
|
||||||
|
if_version != dcerpc_table_rpcecho.if_version) {
|
||||||
|
DEBUG(2,("Attempt to use unknown interface %s/%d\n", uuid, if_version));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
dce->ndr = &dcerpc_table_rpcecho;
|
||||||
|
dce->dispatch = dispatch_table;
|
||||||
|
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* op_connect is called when a connection is made to an endpoint */
|
/* op_connect is called when a connection is made to an endpoint */
|
||||||
static NTSTATUS op_connect(struct dcesrv_state *dce)
|
static NTSTATUS op_connect(struct dcesrv_state *dce)
|
||||||
@ -51,7 +125,8 @@ static void op_disconnect(struct dcesrv_state *dce)
|
|||||||
|
|
||||||
|
|
||||||
static const struct dcesrv_endpoint_ops rpc_echo_ops = {
|
static const struct dcesrv_endpoint_ops rpc_echo_ops = {
|
||||||
op_query,
|
op_query_endpoint,
|
||||||
|
op_set_interface,
|
||||||
op_connect,
|
op_connect,
|
||||||
op_disconnect
|
op_disconnect
|
||||||
};
|
};
|
||||||
|
@ -1219,8 +1219,10 @@ static NTSTATUS trans2_backend(struct request_context *req, struct smb_trans2 *t
|
|||||||
*/
|
*/
|
||||||
static NTSTATUS trans_backend(struct request_context *req, struct smb_trans2 *trans)
|
static NTSTATUS trans_backend(struct request_context *req, struct smb_trans2 *trans)
|
||||||
{
|
{
|
||||||
|
if (!req->conn->ntvfs_ops->trans) {
|
||||||
return NT_STATUS_NOT_IMPLEMENTED;
|
return NT_STATUS_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
return req->conn->ntvfs_ops->trans(req, trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -710,7 +710,6 @@ TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
|
|||||||
TDB_DATA key, next;
|
TDB_DATA key, next;
|
||||||
TDB_LIST_NODE *list = NULL;
|
TDB_LIST_NODE *list = NULL;
|
||||||
TDB_LIST_NODE *rec = NULL;
|
TDB_LIST_NODE *rec = NULL;
|
||||||
TDB_LIST_NODE *tmp = NULL;
|
|
||||||
|
|
||||||
for (key = tdb_firstkey(tdb); key.dptr; key = next) {
|
for (key = tdb_firstkey(tdb); key.dptr; key = next) {
|
||||||
/* duplicate key string to ensure null-termination */
|
/* duplicate key string to ensure null-termination */
|
||||||
@ -731,7 +730,7 @@ TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
|
|||||||
|
|
||||||
rec->node_key = key;
|
rec->node_key = key;
|
||||||
|
|
||||||
DLIST_ADD_END(list, rec, tmp);
|
DLIST_ADD_END(list, rec, TDB_LIST_NODE *);
|
||||||
|
|
||||||
DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
|
DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user