wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Monitor structure and message types definitions. Composite function monitoring
|
||||
* allows client application to be notified on function progress. This enables
|
||||
* eg. gui client to display progress bars, status messages, etc.
|
||||
*/
|
||||
|
||||
|
||||
#define rpc_create_user (0x00000001) /* userman.h */
|
||||
#define rpc_open_user (0x00000002) /* userinfo.h */
|
||||
#define rpc_query_user (0x00000003) /* userinfo.h */
|
||||
#define rpc_close_user (0x00000004) /* userinfo.h */
|
||||
#define rpc_lookup_name (0x00000005) /* userman.h */
|
||||
#define rpc_delete_user (0x00000006) /* userman.h */
|
||||
#define rpc_set_user (0x00000007) /* userman.h */
|
||||
|
||||
#define net_lookup_dc (0x00000100) /* libnet_rpc.h */
|
||||
#define net_pipe_connected (0x00000200) /* libnet_rpc.h */
|
||||
|
||||
|
||||
struct monitor_msg {
|
||||
uint32_t type;
|
||||
void *data;
|
||||
size_t data_size;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
[LIBRARY::LIBSAMBA-NET]
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = Convenient high level access to Samba management interfaces
|
||||
PRIVATE_PROTO_HEADER = libnet_proto.h
|
||||
PUBLIC_HEADERS = libnet.h libnet_join.h libnet_lookup.h libnet_passwd.h \
|
||||
libnet_rpc.h libnet_share.h libnet_time.h \
|
||||
libnet_user.h libnet_site.h libnet_vampire.h \
|
||||
userinfo.h userman.h
|
||||
OBJ_FILES = \
|
||||
libnet.o \
|
||||
libnet_passwd.o \
|
||||
libnet_time.o \
|
||||
libnet_rpc.o \
|
||||
libnet_join.o \
|
||||
libnet_site.o \
|
||||
libnet_become_dc.o \
|
||||
libnet_unbecome_dc.o \
|
||||
libnet_vampire.o \
|
||||
libnet_samdump.o \
|
||||
libnet_samdump_keytab.o \
|
||||
libnet_samsync_ldb.o \
|
||||
libnet_user.o \
|
||||
libnet_share.o \
|
||||
libnet_lookup.o \
|
||||
libnet_domain.o \
|
||||
userinfo.o \
|
||||
userman.o \
|
||||
prereq_domain.o
|
||||
PUBLIC_DEPENDENCIES = dcerpc RPC_NDR_SAMR RPC_NDR_LSA RPC_NDR_SRVSVC RPC_NDR_DRSUAPI LIBCLI_COMPOSITE LIBCLI_RESOLVE LIBCLI_FINDDCS LIBSAMBA3 LIBCLI_CLDAP LIBCLI_FINDDCS gensec_schannel
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 "libnet/libnet.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
struct libnet_context *libnet_context_init(struct event_context *ev)
|
||||
{
|
||||
struct libnet_context *ctx;
|
||||
|
||||
/* create brand new libnet context */
|
||||
ctx = talloc(ev, struct libnet_context);
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* events */
|
||||
if (ev == NULL) {
|
||||
ev = event_context_find(ctx);
|
||||
if (ev == NULL) {
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
ctx->event_ctx = ev;
|
||||
|
||||
/* name resolution methods */
|
||||
ctx->name_res_methods = str_list_copy(ctx, lp_name_resolve_order());
|
||||
|
||||
/* connected services' params */
|
||||
ZERO_STRUCT(ctx->samr);
|
||||
ZERO_STRUCT(ctx->lsa);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Rafal Szczesniak 2005-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 libnet_context {
|
||||
/* here we need:
|
||||
* a client env context
|
||||
* a user env context
|
||||
*/
|
||||
struct cli_credentials *cred;
|
||||
|
||||
/* samr connection parameters - opened handles and related properties */
|
||||
struct {
|
||||
struct dcerpc_pipe *pipe;
|
||||
const char *name;
|
||||
uint32_t access_mask;
|
||||
struct policy_handle handle;
|
||||
} samr;
|
||||
|
||||
/* lsa connection parameters - opened handles and related properties */
|
||||
struct {
|
||||
struct dcerpc_pipe *pipe;
|
||||
const char *name;
|
||||
uint32_t access_mask;
|
||||
struct policy_handle handle;
|
||||
} lsa;
|
||||
|
||||
/* name resolution methods */
|
||||
const char **name_res_methods;
|
||||
|
||||
struct event_context *event_ctx;
|
||||
};
|
||||
|
||||
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "libnet/composite.h"
|
||||
#include "libnet/userman.h"
|
||||
#include "libnet/userinfo.h"
|
||||
#include "libnet/libnet_passwd.h"
|
||||
#include "libnet/libnet_time.h"
|
||||
#include "libnet/libnet_rpc.h"
|
||||
#include "libnet/libnet_join.h"
|
||||
#include "libnet/libnet_site.h"
|
||||
#include "libnet/libnet_become_dc.h"
|
||||
#include "libnet/libnet_unbecome_dc.h"
|
||||
#include "libnet/libnet_vampire.h"
|
||||
#include "libnet/libnet_user.h"
|
||||
#include "libnet/libnet_share.h"
|
||||
#include "libnet/libnet_lookup.h"
|
||||
#include "libnet/libnet_domain.h"
|
||||
#include "libnet/libnet_proto.h"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 libnet_BecomeDC {
|
||||
struct {
|
||||
const char *domain_dns_name;
|
||||
const char *domain_netbios_name;
|
||||
const struct dom_sid *domain_sid;
|
||||
const char *source_dsa_address;
|
||||
const char *dest_dsa_netbios_name;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
@@ -0,0 +1,885 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
a composite function for domain handling on samr and lsa pipes
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libnet/libnet.h"
|
||||
#include "librpc/gen_ndr/ndr_samr_c.h"
|
||||
#include "librpc/gen_ndr/ndr_lsa_c.h"
|
||||
|
||||
static void domain_open_handler(struct rpc_request*);
|
||||
|
||||
enum domain_open_stage { DOMOPEN_CONNECT, DOMOPEN_LOOKUP, DOMOPEN_OPEN,
|
||||
DOMOPEN_CLOSE_EXISTING, DOMOPEN_RPC_CONNECT };
|
||||
|
||||
struct domain_open_samr_state {
|
||||
enum domain_open_stage stage;
|
||||
struct libnet_context *ctx;
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct rpc_request *req;
|
||||
struct composite_context *rpcconn_req;
|
||||
struct samr_Connect connect;
|
||||
struct samr_LookupDomain lookup;
|
||||
struct samr_OpenDomain open;
|
||||
struct samr_Close close;
|
||||
struct libnet_RpcConnect rpcconn;
|
||||
struct lsa_String domain_name;
|
||||
uint32_t access_mask;
|
||||
struct policy_handle connect_handle;
|
||||
struct policy_handle domain_handle;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg*);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stage 0.5 (optional): Connect to samr rpc pipe
|
||||
*/
|
||||
static void domain_open_rpc_connect(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_open_samr_state *s;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct domain_open_samr_state);
|
||||
|
||||
c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
s->pipe = s->rpcconn.out.dcerpc_pipe;
|
||||
|
||||
/* preparing parameters for samr_Connect rpc call */
|
||||
s->connect.in.system_name = 0;
|
||||
s->connect.in.access_mask = s->access_mask;
|
||||
s->connect.out.connect_handle = &s->connect_handle;
|
||||
|
||||
/* send request */
|
||||
s->req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
|
||||
if (composite_nomem(s->req, c)) return;
|
||||
|
||||
/* callback handler */
|
||||
s->req->async.callback = domain_open_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = DOMOPEN_CONNECT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 0.5 (optional): Close existing (in libnet context) domain
|
||||
* handle
|
||||
*/
|
||||
static NTSTATUS domain_open_close(struct composite_context *c,
|
||||
struct domain_open_samr_state *s)
|
||||
{
|
||||
/* receive samr_Close reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* reset domain handle and associated data in libnet_context */
|
||||
s->ctx->samr.name = NULL;
|
||||
s->ctx->samr.access_mask = 0;
|
||||
ZERO_STRUCT(s->ctx->samr.handle);
|
||||
|
||||
/* preparing parameters for samr_Connect rpc call */
|
||||
s->connect.in.system_name = 0;
|
||||
s->connect.in.access_mask = s->access_mask;
|
||||
s->connect.out.connect_handle = &s->connect_handle;
|
||||
|
||||
/* send request */
|
||||
s->req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
|
||||
if (s->req == NULL) return NT_STATUS_NO_MEMORY;
|
||||
|
||||
/* callback handler */
|
||||
s->req->async.callback = domain_open_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = DOMOPEN_CONNECT;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 1: Connect to SAM server.
|
||||
*/
|
||||
static NTSTATUS domain_open_connect(struct composite_context *c,
|
||||
struct domain_open_samr_state *s)
|
||||
{
|
||||
struct samr_LookupDomain *r = &s->lookup;
|
||||
|
||||
/* receive samr_Connect reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* prepare for samr_LookupDomain call */
|
||||
r->in.connect_handle = &s->connect_handle;
|
||||
r->in.domain_name = &s->domain_name;
|
||||
|
||||
s->req = dcerpc_samr_LookupDomain_send(s->pipe, c, r);
|
||||
if (s->req == NULL) goto failure;
|
||||
|
||||
s->req->async.callback = domain_open_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = DOMOPEN_LOOKUP;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failure:
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 2: Lookup domain by name.
|
||||
*/
|
||||
static NTSTATUS domain_open_lookup(struct composite_context *c,
|
||||
struct domain_open_samr_state *s)
|
||||
{
|
||||
struct samr_OpenDomain *r = &s->open;
|
||||
|
||||
/* receive samr_LookupDomain reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* prepare for samr_OpenDomain call */
|
||||
r->in.connect_handle = &s->connect_handle;
|
||||
r->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
r->in.sid = s->lookup.out.sid;
|
||||
r->out.domain_handle = &s->domain_handle;
|
||||
|
||||
s->req = dcerpc_samr_OpenDomain_send(s->pipe, c, r);
|
||||
if (s->req == NULL) goto failure;
|
||||
|
||||
s->req->async.callback = domain_open_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = DOMOPEN_OPEN;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failure:
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Stage 3: Open domain.
|
||||
*/
|
||||
static NTSTATUS domain_open_open(struct composite_context *c,
|
||||
struct domain_open_samr_state *s)
|
||||
{
|
||||
/* receive samr_OpenDomain reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for asynchronous request. Handles transition through
|
||||
* intermediate stages of the call.
|
||||
*
|
||||
* @param req rpc call context
|
||||
*/
|
||||
static void domain_open_handler(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c = req->async.private;
|
||||
struct domain_open_samr_state *s = talloc_get_type(c->private_data,
|
||||
struct domain_open_samr_state);
|
||||
|
||||
/* Stages of the call */
|
||||
switch (s->stage) {
|
||||
case DOMOPEN_CONNECT:
|
||||
c->status = domain_open_connect(c, s);
|
||||
break;
|
||||
case DOMOPEN_LOOKUP:
|
||||
c->status = domain_open_lookup(c, s);
|
||||
break;
|
||||
case DOMOPEN_OPEN:
|
||||
c->status = domain_open_open(c, s);
|
||||
break;
|
||||
case DOMOPEN_CLOSE_EXISTING:
|
||||
c->status = domain_open_close(c, s);
|
||||
break;
|
||||
case DOMOPEN_RPC_CONNECT:
|
||||
/* this state shouldn't be handled here */
|
||||
c->status = NT_STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
}
|
||||
|
||||
if (c->state == COMPOSITE_STATE_DONE) {
|
||||
composite_done(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous DomainOpenSamr request
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param io arguments and results of the call
|
||||
* @param monitor pointer to monitor function that is passed monitor message
|
||||
*/
|
||||
|
||||
struct composite_context *libnet_DomainOpenSamr_send(struct libnet_context *ctx,
|
||||
struct libnet_DomainOpen *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_open_samr_state *s;
|
||||
|
||||
c = composite_create(ctx, ctx->event_ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct domain_open_samr_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->private_data = s;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
s->ctx = ctx;
|
||||
s->pipe = ctx->samr.pipe;
|
||||
s->access_mask = io->in.access_mask;
|
||||
s->domain_name.string = talloc_strdup(c, io->in.domain_name);
|
||||
|
||||
/* check, if there's samr pipe opened already, before opening a domain */
|
||||
if (ctx->samr.pipe == NULL) {
|
||||
|
||||
/* attempting to connect a domain controller */
|
||||
s->rpcconn.level = LIBNET_RPC_CONNECT_DC;
|
||||
s->rpcconn.in.name = io->in.domain_name;
|
||||
s->rpcconn.in.dcerpc_iface = &dcerpc_table_samr;
|
||||
|
||||
/* send rpc pipe connect request */
|
||||
s->rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn);
|
||||
if (composite_nomem(s->rpcconn_req, c)) return c;
|
||||
|
||||
s->rpcconn_req->async.fn = domain_open_rpc_connect;
|
||||
s->rpcconn_req->async.private_data = c;
|
||||
s->stage = DOMOPEN_RPC_CONNECT;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* libnet context's domain handle is not empty, so check out what
|
||||
was opened first, before doing anything */
|
||||
if (!policy_handle_empty(&ctx->samr.handle)) {
|
||||
if (strequal(ctx->samr.name, io->in.domain_name) &&
|
||||
ctx->samr.access_mask == io->in.access_mask) {
|
||||
|
||||
/* this domain is already opened */
|
||||
composite_done(c);
|
||||
return c;
|
||||
|
||||
} else {
|
||||
/* another domain or access rights have been
|
||||
requested - close the existing handle first */
|
||||
s->close.in.handle = &ctx->samr.handle;
|
||||
|
||||
/* send request to close domain handle */
|
||||
s->req = dcerpc_samr_Close_send(s->pipe, c, &s->close);
|
||||
if (composite_nomem(s->req, c)) return c;
|
||||
|
||||
/* callback handler */
|
||||
s->req->async.callback = domain_open_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = DOMOPEN_CLOSE_EXISTING;
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/* preparing parameters for samr_Connect rpc call */
|
||||
s->connect.in.system_name = 0;
|
||||
s->connect.in.access_mask = s->access_mask;
|
||||
s->connect.out.connect_handle = &s->connect_handle;
|
||||
|
||||
/* send request */
|
||||
s->req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
|
||||
if (composite_nomem(s->req, c)) return c;
|
||||
|
||||
/* callback handler */
|
||||
s->req->async.callback = domain_open_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = DOMOPEN_CONNECT;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for and receives result of asynchronous DomainOpenSamr call
|
||||
*
|
||||
* @param c composite context returned by asynchronous DomainOpen call
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_DomainOpenSamr_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct domain_open_samr_state *s;
|
||||
|
||||
/* wait for results of sending request */
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
s = talloc_get_type(c->private_data, struct domain_open_samr_state);
|
||||
io->out.domain_handle = s->domain_handle;
|
||||
|
||||
/* store the resulting handle and related data for use by other
|
||||
libnet functions */
|
||||
ctx->samr.handle = s->domain_handle;
|
||||
ctx->samr.name = talloc_steal(ctx, s->domain_name.string);
|
||||
ctx->samr.access_mask = s->access_mask;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
struct domain_open_lsa_state {
|
||||
const char *name;
|
||||
uint32_t access_mask;
|
||||
struct libnet_context *ctx;
|
||||
struct libnet_RpcConnect rpcconn;
|
||||
struct lsa_OpenPolicy2 openpol;
|
||||
struct policy_handle handle;
|
||||
struct dcerpc_pipe *pipe;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg*);
|
||||
};
|
||||
|
||||
|
||||
static void continue_rpc_connect_lsa(struct composite_context *ctx);
|
||||
static void continue_lsa_policy_open(struct rpc_request *req);
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous DomainOpenLsa request
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param io arguments and results of the call
|
||||
* @param monitor pointer to monitor function that is passed monitor message
|
||||
*/
|
||||
|
||||
struct composite_context* libnet_DomainOpenLsa_send(struct libnet_context *ctx,
|
||||
struct libnet_DomainOpen *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_open_lsa_state *s;
|
||||
struct composite_context *rpcconn_req;
|
||||
struct rpc_request *openpol_req;
|
||||
struct lsa_QosInfo *qos;
|
||||
|
||||
/* create composite context and state */
|
||||
c = composite_create(ctx, ctx->event_ctx);
|
||||
if (c == NULL) return c;
|
||||
|
||||
s = talloc_zero(c, struct domain_open_lsa_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->private_data = s;
|
||||
|
||||
/* store arguments in the state structure */
|
||||
s->name = talloc_strdup(c, io->in.domain_name);
|
||||
s->access_mask = io->in.access_mask;
|
||||
s->ctx = ctx;
|
||||
|
||||
/* check, if there's lsa pipe opened already, before opening a handle */
|
||||
if (ctx->lsa.pipe == NULL) {
|
||||
|
||||
/* attempting to connect a domain controller */
|
||||
s->rpcconn.level = LIBNET_RPC_CONNECT_DC;
|
||||
s->rpcconn.in.name = talloc_strdup(c, io->in.domain_name);
|
||||
s->rpcconn.in.dcerpc_iface = &dcerpc_table_lsarpc;
|
||||
|
||||
/* send rpc pipe connect request */
|
||||
rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn);
|
||||
if (composite_nomem(rpcconn_req, c)) return c;
|
||||
|
||||
composite_continue(c, rpcconn_req, continue_rpc_connect_lsa, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
s->pipe = ctx->lsa.pipe;
|
||||
|
||||
/* preparing parameters for lsa_OpenPolicy2 rpc call */
|
||||
s->openpol.in.system_name = s->name;
|
||||
s->openpol.in.access_mask = s->access_mask;
|
||||
s->openpol.in.attr = talloc_zero(c, struct lsa_ObjectAttribute);
|
||||
|
||||
qos = talloc_zero(c, struct lsa_QosInfo);
|
||||
qos->len = 0;
|
||||
qos->impersonation_level = 2;
|
||||
qos->context_mode = 1;
|
||||
qos->effective_only = 0;
|
||||
|
||||
s->openpol.in.attr->sec_qos = qos;
|
||||
s->openpol.out.handle = &s->handle;
|
||||
|
||||
/* send rpc request */
|
||||
openpol_req = dcerpc_lsa_OpenPolicy2_send(s->pipe, c, &s->openpol);
|
||||
if (composite_nomem(openpol_req, c)) return c;
|
||||
|
||||
composite_continue_rpc(c, openpol_req, continue_lsa_policy_open, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stage 0.5 (optional): Rpc pipe connected, send lsa open policy request
|
||||
*/
|
||||
static void continue_rpc_connect_lsa(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_open_lsa_state *s;
|
||||
struct lsa_QosInfo *qos;
|
||||
struct rpc_request *openpol_req;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct domain_open_lsa_state);
|
||||
|
||||
/* receive rpc connection */
|
||||
c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
/* RpcConnect function leaves the pipe in libnet context,
|
||||
so get it from there */
|
||||
s->pipe = s->ctx->lsa.pipe;
|
||||
|
||||
/* prepare lsa_OpenPolicy2 call */
|
||||
s->openpol.in.system_name = s->name;
|
||||
s->openpol.in.access_mask = s->access_mask;
|
||||
s->openpol.in.attr = talloc_zero(c, struct lsa_ObjectAttribute);
|
||||
|
||||
qos = talloc_zero(c, struct lsa_QosInfo);
|
||||
qos->len = 0;
|
||||
qos->impersonation_level = 2;
|
||||
qos->context_mode = 1;
|
||||
qos->effective_only = 0;
|
||||
|
||||
s->openpol.in.attr->sec_qos = qos;
|
||||
s->openpol.out.handle = &s->handle;
|
||||
|
||||
/* send rpc request */
|
||||
openpol_req = dcerpc_lsa_OpenPolicy2_send(s->pipe, c, &s->openpol);
|
||||
if (composite_nomem(openpol_req, c)) return;
|
||||
|
||||
composite_continue_rpc(c, openpol_req, continue_lsa_policy_open, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stage 1: Lsa policy opened - we're done, if successfully
|
||||
*/
|
||||
static void continue_lsa_policy_open(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_open_lsa_state *s;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct domain_open_lsa_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives result of asynchronous DomainOpenLsa call
|
||||
*
|
||||
* @param c composite context returned by asynchronous DomainOpenLsa call
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_DomainOpenLsa_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct domain_open_lsa_state *s;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
/* everything went fine - get the results and
|
||||
return the error string */
|
||||
s = talloc_get_type(c->private_data, struct domain_open_lsa_state);
|
||||
io->out.domain_handle = s->handle;
|
||||
|
||||
ctx->lsa.handle = s->handle;
|
||||
ctx->lsa.name = talloc_steal(ctx, s->name);
|
||||
ctx->lsa.access_mask = s->access_mask;
|
||||
|
||||
io->out.error_string = talloc_strdup(mem_ctx, "Success");
|
||||
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
/* there was an error, so provide nt status code description */
|
||||
io->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Failed to open domain: %s",
|
||||
nt_errstr(status));
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a request to open a domain in desired service
|
||||
*
|
||||
* @param ctx initalised libnet context
|
||||
* @param io arguments and results of the call
|
||||
* @param monitor pointer to monitor function that is passed monitor message
|
||||
*/
|
||||
|
||||
struct composite_context* libnet_DomainOpen_send(struct libnet_context *ctx,
|
||||
struct libnet_DomainOpen *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
|
||||
switch (io->in.type) {
|
||||
case DOMAIN_LSA:
|
||||
/* reques to open a policy handle on \pipe\lsarpc */
|
||||
c = libnet_DomainOpenLsa_send(ctx, io, monitor);
|
||||
break;
|
||||
|
||||
case DOMAIN_SAMR:
|
||||
default:
|
||||
/* request to open a domain policy handle on \pipe\samr */
|
||||
c = libnet_DomainOpenSamr_send(ctx, io, monitor);
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receive result of domain open request
|
||||
*
|
||||
* @param c composite context returned by DomainOpen_send function
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io results and arguments of the call
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_DomainOpen_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
switch (io->in.type) {
|
||||
case DOMAIN_LSA:
|
||||
status = libnet_DomainOpenLsa_recv(c, ctx, mem_ctx, io);
|
||||
break;
|
||||
|
||||
case DOMAIN_SAMR:
|
||||
default:
|
||||
status = libnet_DomainOpenSamr_recv(c, ctx, mem_ctx, io);
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of DomainOpen call
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context for the call
|
||||
* @param io arguments and results of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_DomainOpen(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_DomainOpen *io)
|
||||
{
|
||||
struct composite_context *c = libnet_DomainOpen_send(ctx, io, NULL);
|
||||
return libnet_DomainOpen_recv(c, ctx, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
struct domain_close_lsa_state {
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct lsa_Close close;
|
||||
struct policy_handle handle;
|
||||
|
||||
void (*monitor_fn)(struct monitor_msg*);
|
||||
};
|
||||
|
||||
|
||||
static void continue_lsa_close(struct rpc_request *req);
|
||||
|
||||
|
||||
struct composite_context* libnet_DomainCloseLsa_send(struct libnet_context *ctx,
|
||||
struct libnet_DomainClose *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_close_lsa_state *s;
|
||||
struct rpc_request *close_req;
|
||||
|
||||
/* composite context and state structure allocation */
|
||||
c = composite_create(ctx, ctx->event_ctx);
|
||||
if (c == NULL) return c;
|
||||
|
||||
s = talloc_zero(c, struct domain_close_lsa_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->private_data = s;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
/* TODO: check if lsa pipe pointer is non-null */
|
||||
|
||||
if (!strequal(ctx->lsa.name, io->in.domain_name)) {
|
||||
composite_error(c, NT_STATUS_INVALID_PARAMETER);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* get opened lsarpc pipe pointer */
|
||||
s->pipe = ctx->lsa.pipe;
|
||||
|
||||
/* prepare close handle call arguments */
|
||||
s->close.in.handle = &ctx->lsa.handle;
|
||||
s->close.out.handle = &s->handle;
|
||||
|
||||
/* send the request */
|
||||
close_req = dcerpc_lsa_Close_send(s->pipe, c, &s->close);
|
||||
if (composite_nomem(close_req, c)) return c;
|
||||
|
||||
composite_continue_rpc(c, close_req, continue_lsa_close, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stage 1: Receive result of lsa close call
|
||||
*/
|
||||
static void continue_lsa_close(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_close_lsa_state *s;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct domain_close_lsa_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_DomainCloseLsa_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
/* policy handle closed successfully */
|
||||
|
||||
ctx->lsa.name = NULL;
|
||||
ZERO_STRUCT(ctx->lsa.handle);
|
||||
|
||||
io->out.error_string = talloc_asprintf(mem_ctx, "Success");
|
||||
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
/* there was an error, so return description of the status code */
|
||||
io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
struct domain_close_samr_state {
|
||||
struct samr_Close close;
|
||||
struct policy_handle handle;
|
||||
|
||||
void (*monitor_fn)(struct monitor_msg*);
|
||||
};
|
||||
|
||||
|
||||
static void continue_samr_close(struct rpc_request *req);
|
||||
|
||||
|
||||
struct composite_context* libnet_DomainCloseSamr_send(struct libnet_context *ctx,
|
||||
struct libnet_DomainClose *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_close_samr_state *s;
|
||||
struct rpc_request *close_req;
|
||||
|
||||
/* composite context and state structure allocation */
|
||||
c = composite_create(ctx, ctx->event_ctx);
|
||||
if (c == NULL) return c;
|
||||
|
||||
s = talloc_zero(c, struct domain_close_samr_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->private_data = s;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
/* TODO: check if samr pipe pointer is non-null */
|
||||
|
||||
if (!strequal(ctx->samr.name, io->in.domain_name)) {
|
||||
composite_error(c, NT_STATUS_INVALID_PARAMETER);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* prepare close domain handle call arguments */
|
||||
ZERO_STRUCT(s->close);
|
||||
s->close.in.handle = &ctx->samr.handle;
|
||||
s->close.out.handle = &s->handle;
|
||||
|
||||
/* send the request */
|
||||
close_req = dcerpc_samr_Close_send(ctx->samr.pipe, ctx, &s->close);
|
||||
if (composite_nomem(close_req, c)) return c;
|
||||
|
||||
composite_continue_rpc(c, close_req, continue_samr_close, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stage 1: Receive result of samr close call
|
||||
*/
|
||||
static void continue_samr_close(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct domain_close_samr_state *s;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct domain_close_samr_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_DomainCloseSamr_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
/* domain policy handle closed successfully */
|
||||
|
||||
ZERO_STRUCT(ctx->samr.handle);
|
||||
ctx->samr.name = NULL;
|
||||
|
||||
io->out.error_string = talloc_asprintf(mem_ctx, "Success");
|
||||
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
/* there was an error, so return description of the status code */
|
||||
io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
struct composite_context* libnet_DomainClose_send(struct libnet_context *ctx,
|
||||
struct libnet_DomainClose *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
|
||||
switch (io->in.type) {
|
||||
case DOMAIN_LSA:
|
||||
/* request to close policy handle on \pipe\lsarpc */
|
||||
c = libnet_DomainCloseLsa_send(ctx, io, monitor);
|
||||
break;
|
||||
|
||||
case DOMAIN_SAMR:
|
||||
default:
|
||||
/* request to close domain policy handle on \pipe\samr */
|
||||
c = libnet_DomainCloseSamr_send(ctx, io, monitor);
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_DomainClose_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
switch (io->in.type) {
|
||||
case DOMAIN_LSA:
|
||||
/* receive result of closing lsa policy handle */
|
||||
status = libnet_DomainCloseLsa_recv(c, ctx, mem_ctx, io);
|
||||
break;
|
||||
|
||||
case DOMAIN_SAMR:
|
||||
default:
|
||||
/* receive result of closing samr domain policy handle */
|
||||
status = libnet_DomainCloseSamr_recv(c, ctx, mem_ctx, io);
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_DomainClose(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_DomainClose *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
|
||||
c = libnet_DomainClose_send(ctx, io, NULL);
|
||||
return libnet_DomainClose_recv(c, ctx, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
|
||||
enum service_type { DOMAIN_SAMR, DOMAIN_LSA };
|
||||
|
||||
/*
|
||||
* struct definition for opening a domain
|
||||
*/
|
||||
|
||||
struct libnet_DomainOpen {
|
||||
struct {
|
||||
enum service_type type;
|
||||
const char *domain_name;
|
||||
uint32_t access_mask;
|
||||
} in;
|
||||
struct {
|
||||
struct policy_handle domain_handle;
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_DomainClose {
|
||||
struct {
|
||||
enum service_type type;
|
||||
const char *domain_name;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
Copyright (C) Brad Henry 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 __LIBNET_JOIN_H__
|
||||
#define __LIBNET_JOIN_H__
|
||||
|
||||
#include "librpc/gen_ndr/netlogon.h"
|
||||
|
||||
enum libnet_Join_level {
|
||||
LIBNET_JOIN_AUTOMATIC,
|
||||
LIBNET_JOIN_SPECIFIED,
|
||||
};
|
||||
|
||||
enum libnet_JoinDomain_level {
|
||||
LIBNET_JOINDOMAIN_AUTOMATIC,
|
||||
LIBNET_JOINDOMAIN_SPECIFIED,
|
||||
};
|
||||
|
||||
struct libnet_JoinDomain {
|
||||
struct {
|
||||
const char *domain_name;
|
||||
const char *account_name;
|
||||
const char *netbios_name;
|
||||
const char *binding;
|
||||
enum libnet_JoinDomain_level level;
|
||||
uint32_t acct_type;
|
||||
BOOL recreate_account;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
const char *error_string;
|
||||
const char *join_password;
|
||||
struct dom_sid *domain_sid;
|
||||
const char *domain_name;
|
||||
const char *realm;
|
||||
const char *domain_dn_str;
|
||||
const char *account_dn_str;
|
||||
const char *server_dn_str;
|
||||
uint32_t kvno; /* msDS-KeyVersionNumber */
|
||||
struct dcerpc_pipe *samr_pipe;
|
||||
struct dcerpc_binding *samr_binding;
|
||||
struct policy_handle *user_handle;
|
||||
struct dom_sid *account_sid;
|
||||
} out;
|
||||
};
|
||||
|
||||
struct libnet_Join {
|
||||
struct {
|
||||
const char *domain_name;
|
||||
const char *netbios_name;
|
||||
enum netr_SchannelType join_type;
|
||||
enum libnet_Join_level level;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
const char *error_string;
|
||||
const char *join_password;
|
||||
struct dom_sid *domain_sid;
|
||||
const char *domain_name;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
#endif /* __LIBNET_JOIN_H__ */
|
||||
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
a composite function for name resolving
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "libnet/libnet.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "lib/messaging/messaging.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
#include "libcli/finddcs.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "librpc/gen_ndr/lsa.h"
|
||||
#include "librpc/gen_ndr/ndr_lsa_c.h"
|
||||
|
||||
struct lookup_state {
|
||||
struct nbt_name hostname;
|
||||
const char *address;
|
||||
};
|
||||
|
||||
|
||||
static void continue_name_resolved(struct composite_context *ctx);
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous Lookup request
|
||||
*
|
||||
* @param io arguments and result of the call
|
||||
*/
|
||||
|
||||
struct composite_context *libnet_Lookup_send(struct libnet_context *ctx,
|
||||
struct libnet_Lookup *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct lookup_state *s;
|
||||
struct composite_context *cresolve_req;
|
||||
const char** methods;
|
||||
|
||||
/* allocate context and state structures */
|
||||
c = talloc_zero(NULL, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct lookup_state);
|
||||
if (s == NULL) {
|
||||
composite_error(c, NT_STATUS_NO_MEMORY);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* prepare event context */
|
||||
c->event_ctx = event_context_find(c);
|
||||
if (c->event_ctx == NULL) {
|
||||
composite_error(c, NT_STATUS_NO_MEMORY);
|
||||
return c;
|
||||
}
|
||||
|
||||
if (io == NULL || io->in.hostname == NULL) {
|
||||
composite_error(c, NT_STATUS_INVALID_PARAMETER);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* parameters */
|
||||
s->hostname.name = talloc_strdup(s, io->in.hostname);
|
||||
s->hostname.type = io->in.type;
|
||||
s->hostname.scope = NULL;
|
||||
|
||||
/* name resolution methods */
|
||||
if (io->in.methods) {
|
||||
methods = io->in.methods;
|
||||
} else {
|
||||
methods = ctx->name_res_methods;
|
||||
}
|
||||
|
||||
c->private_data = s;
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
|
||||
/* send resolve request */
|
||||
cresolve_req = resolve_name_send(&s->hostname, c->event_ctx, methods);
|
||||
|
||||
composite_continue(c, cresolve_req, continue_name_resolved, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
static void continue_name_resolved(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct lookup_state *s;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct lookup_state);
|
||||
|
||||
c->status = resolve_name_recv(ctx, s, &s->address);
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for and receives results of asynchronous Lookup call
|
||||
*
|
||||
* @param c composite context returned by asynchronous Lookup call
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_Lookup_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_Lookup *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct lookup_state *s;
|
||||
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
s = talloc_get_type(c->private_data, struct lookup_state);
|
||||
|
||||
io->out.address = str_list_make(mem_ctx, s->address, NULL);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io->out.address);
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of Lookup call
|
||||
*
|
||||
* @param mem_ctx memory context for the call
|
||||
* @param io arguments and results of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_Lookup(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_Lookup *io)
|
||||
{
|
||||
struct composite_context *c = libnet_Lookup_send(ctx, io);
|
||||
return libnet_Lookup_recv(c, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Shortcut functions to find common types of name
|
||||
* (and skip nbt name type argument)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous LookupHost request
|
||||
*/
|
||||
struct composite_context* libnet_LookupHost_send(struct libnet_context *ctx,
|
||||
struct libnet_Lookup *io)
|
||||
{
|
||||
io->in.type = NBT_NAME_SERVER;
|
||||
return libnet_Lookup_send(ctx, io);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of LookupHost call
|
||||
*/
|
||||
NTSTATUS libnet_LookupHost(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_Lookup *io)
|
||||
{
|
||||
struct composite_context *c = libnet_LookupHost_send(ctx, io);
|
||||
return libnet_Lookup_recv(c, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous LookupDCs request
|
||||
*/
|
||||
struct composite_context* libnet_LookupDCs_send(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_LookupDCs *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct messaging_context *msg_ctx = messaging_client_init(mem_ctx, ctx->event_ctx);
|
||||
|
||||
c = finddcs_send(mem_ctx, io->in.domain_name, io->in.name_type,
|
||||
NULL, ctx->name_res_methods, ctx->event_ctx, msg_ctx);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for and receives results of asynchronous Lookup call
|
||||
*
|
||||
* @param c composite context returned by asynchronous Lookup call
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_LookupDCs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_LookupDCs *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = finddcs_recv(c, mem_ctx, &io->out.num_dcs, &io->out.dcs);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static struct composite_context* lsa_policy_opened(struct libnet_context *ctx,
|
||||
const char *domain_name,
|
||||
struct composite_context *parent_ctx,
|
||||
struct libnet_DomainOpen *domain_open,
|
||||
void (*continue_fn)(struct composite_context*),
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *domopen_req;
|
||||
|
||||
if (domain_name == NULL) {
|
||||
if (policy_handle_empty(&ctx->lsa.handle)) {
|
||||
domain_open->in.type = DOMAIN_LSA;
|
||||
domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
|
||||
domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
|
||||
} else {
|
||||
composite_error(parent_ctx, NT_STATUS_INVALID_PARAMETER);
|
||||
return parent_ctx;
|
||||
}
|
||||
} else {
|
||||
if (policy_handle_empty(&ctx->lsa.handle) ||
|
||||
!strequal(domain_name, ctx->lsa.name)) {
|
||||
domain_open->in.type = DOMAIN_LSA;
|
||||
domain_open->in.domain_name = domain_name;
|
||||
domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
domopen_req = libnet_DomainOpen_send(ctx, domain_open, monitor);
|
||||
if (composite_nomem(domopen_req, parent_ctx)) return parent_ctx;
|
||||
|
||||
composite_continue(parent_ctx, domopen_req, continue_fn, parent_ctx);
|
||||
return parent_ctx;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of LookupDCs
|
||||
*/
|
||||
NTSTATUS libnet_LookupDCs(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_LookupDCs *io)
|
||||
{
|
||||
struct composite_context *c = libnet_LookupDCs_send(ctx, mem_ctx, io);
|
||||
return libnet_LookupDCs_recv(c, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
struct lookup_name_state {
|
||||
struct libnet_context *ctx;
|
||||
const char *name;
|
||||
uint32_t count;
|
||||
struct libnet_DomainOpen domopen;
|
||||
struct lsa_LookupNames lookup;
|
||||
struct lsa_TransSidArray sids;
|
||||
struct lsa_String *names;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg *);
|
||||
};
|
||||
|
||||
|
||||
static bool prepare_lookup_params(struct libnet_context *ctx,
|
||||
struct composite_context *c,
|
||||
struct lookup_name_state *s);
|
||||
static void continue_lookup_name(struct composite_context *ctx);
|
||||
static void continue_name_found(struct rpc_request *req);
|
||||
|
||||
|
||||
struct composite_context* libnet_LookupName_send(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_LookupName *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct lookup_name_state *s;
|
||||
struct composite_context *prereq_ctx;
|
||||
struct rpc_request *lookup_req;
|
||||
|
||||
c = composite_create(mem_ctx, ctx->event_ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct lookup_name_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->private_data = s;
|
||||
|
||||
s->name = talloc_strdup(c, io->in.name);
|
||||
s->monitor_fn = monitor;
|
||||
s->ctx = ctx;
|
||||
|
||||
prereq_ctx = lsa_policy_opened(ctx, io->in.domain_name, c, &s->domopen,
|
||||
continue_lookup_name, monitor);
|
||||
if (prereq_ctx) return prereq_ctx;
|
||||
|
||||
if (!prepare_lookup_params(ctx, c, s)) return c;
|
||||
|
||||
lookup_req = dcerpc_lsa_LookupNames_send(ctx->lsa.pipe, c, &s->lookup);
|
||||
if (composite_nomem(lookup_req, c)) return c;
|
||||
|
||||
composite_continue_rpc(c, lookup_req, continue_name_found, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
static bool prepare_lookup_params(struct libnet_context *ctx,
|
||||
struct composite_context *c,
|
||||
struct lookup_name_state *s)
|
||||
{
|
||||
const int single_name = 1;
|
||||
|
||||
s->sids.count = 0;
|
||||
s->sids.sids = NULL;
|
||||
|
||||
s->names = talloc_array(ctx, struct lsa_String, single_name);
|
||||
if (composite_nomem(s->names, c)) return false;
|
||||
s->names[0].string = s->name;
|
||||
|
||||
s->lookup.in.handle = &ctx->lsa.handle;
|
||||
s->lookup.in.num_names = single_name;
|
||||
s->lookup.in.names = s->names;
|
||||
s->lookup.in.sids = &s->sids;
|
||||
s->lookup.in.level = 1;
|
||||
s->lookup.in.count = &s->count;
|
||||
s->lookup.out.count = &s->count;
|
||||
s->lookup.out.sids = &s->sids;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void continue_lookup_name(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct lookup_name_state *s;
|
||||
struct rpc_request *lookup_req;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct lookup_name_state);
|
||||
|
||||
c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
if (!prepare_lookup_params(s->ctx, c, s)) return;
|
||||
|
||||
lookup_req = dcerpc_lsa_LookupNames_send(s->ctx->lsa.pipe, c, &s->lookup);
|
||||
if (composite_nomem(lookup_req, c)) return;
|
||||
|
||||
composite_continue_rpc(c, lookup_req, continue_name_found, c);
|
||||
}
|
||||
|
||||
|
||||
static void continue_name_found(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct lookup_name_state *s;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct lookup_name_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_LookupName_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_LookupName *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct lookup_name_state *s;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
s = talloc_get_type(c->private_data, struct lookup_name_state);
|
||||
|
||||
ZERO_STRUCT(io->out.domain_sid);
|
||||
io->out.rid = 0;
|
||||
io->out.sidstr = NULL;
|
||||
|
||||
if (*s->lookup.out.count > 0) {
|
||||
int num_auths;
|
||||
struct lsa_RefDomainList *domains = s->lookup.out.domains;
|
||||
struct lsa_TransSidArray *sids = s->lookup.out.sids;
|
||||
|
||||
/* TODO: verify if returned pointers are non-null */
|
||||
|
||||
io->out.domain_sid = *domains->domains[0].sid;
|
||||
io->out.rid = sids->sids[0].rid;
|
||||
io->out.sid_type = sids->sids[0].sid_type;
|
||||
|
||||
num_auths = io->out.domain_sid.num_auths++;
|
||||
io->out.domain_sid.sub_auths[num_auths] = io->out.rid;
|
||||
|
||||
io->out.sidstr = dom_sid_string(mem_ctx, &io->out.domain_sid);
|
||||
}
|
||||
|
||||
io->out.error_string = talloc_strdup(mem_ctx, "Success");
|
||||
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_LookupName(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_LookupName *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
|
||||
c = libnet_LookupName_send(ctx, mem_ctx, io, NULL);
|
||||
return libnet_LookupName_recv(c, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
|
||||
struct libnet_Lookup {
|
||||
struct {
|
||||
const char *hostname;
|
||||
int type;
|
||||
const char **methods;
|
||||
} in;
|
||||
struct {
|
||||
const char **address;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_LookupDCs {
|
||||
struct {
|
||||
const char *domain_name;
|
||||
int name_type;
|
||||
} in;
|
||||
struct {
|
||||
int num_dcs;
|
||||
struct nbt_dc_name *dcs;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_LookupName {
|
||||
struct {
|
||||
const char *name;
|
||||
const char *domain_name;
|
||||
} in;
|
||||
struct {
|
||||
struct dom_sid domain_sid;
|
||||
int rid;
|
||||
enum lsa_SidType sid_type;
|
||||
const char *sidstr;
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
@@ -0,0 +1,703 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 "libnet/libnet.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "librpc/gen_ndr/ndr_samr_c.h"
|
||||
|
||||
/*
|
||||
* do a password change using DCERPC/SAMR calls
|
||||
* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
|
||||
* 2. try samr_ChangePasswordUser3
|
||||
* 3. try samr_ChangePasswordUser2
|
||||
* 4. try samr_OemChangePasswordUser2
|
||||
* (not yet: 5. try samr_ChangePasswordUser)
|
||||
*/
|
||||
static NTSTATUS libnet_ChangePassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct libnet_RpcConnect c;
|
||||
#if 0
|
||||
struct policy_handle user_handle;
|
||||
struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6;
|
||||
struct samr_ChangePasswordUser pw;
|
||||
#endif
|
||||
struct samr_OemChangePasswordUser2 oe2;
|
||||
struct samr_ChangePasswordUser2 pw2;
|
||||
struct samr_ChangePasswordUser3 pw3;
|
||||
struct lsa_String server, account;
|
||||
struct lsa_AsciiString a_server, a_account;
|
||||
struct samr_CryptPassword nt_pass, lm_pass;
|
||||
struct samr_Password nt_verifier, lm_verifier;
|
||||
uint8_t old_nt_hash[16], new_nt_hash[16];
|
||||
uint8_t old_lm_hash[16], new_lm_hash[16];
|
||||
|
||||
/* prepare connect to the SAMR pipe of the users domain PDC */
|
||||
c.level = LIBNET_RPC_CONNECT_PDC;
|
||||
c.in.name = r->samr.in.domain_name;
|
||||
c.in.dcerpc_iface = &dcerpc_table_samr;
|
||||
|
||||
/* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
|
||||
status = libnet_RpcConnect(ctx, mem_ctx, &c);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to SAMR pipe of PDC of domain '%s' failed: %s",
|
||||
r->samr.in.domain_name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* prepare password change for account */
|
||||
server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
|
||||
account.string = r->samr.in.account_name;
|
||||
|
||||
E_md4hash(r->samr.in.oldpassword, old_nt_hash);
|
||||
E_md4hash(r->samr.in.newpassword, new_nt_hash);
|
||||
|
||||
E_deshash(r->samr.in.oldpassword, old_lm_hash);
|
||||
E_deshash(r->samr.in.newpassword, new_lm_hash);
|
||||
|
||||
/* prepare samr_ChangePasswordUser3 */
|
||||
encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_UNICODE);
|
||||
arcfour_crypt(lm_pass.data, old_nt_hash, 516);
|
||||
E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
|
||||
|
||||
encode_pw_buffer(nt_pass.data, r->samr.in.newpassword, STR_UNICODE);
|
||||
arcfour_crypt(nt_pass.data, old_nt_hash, 516);
|
||||
E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
|
||||
|
||||
pw3.in.server = &server;
|
||||
pw3.in.account = &account;
|
||||
pw3.in.nt_password = &nt_pass;
|
||||
pw3.in.nt_verifier = &nt_verifier;
|
||||
pw3.in.lm_change = 1;
|
||||
pw3.in.lm_password = &lm_pass;
|
||||
pw3.in.lm_verifier = &lm_verifier;
|
||||
pw3.in.password3 = NULL;
|
||||
|
||||
/* 2. try samr_ChangePasswordUser3 */
|
||||
status = dcerpc_samr_ChangePasswordUser3(c.out.dcerpc_pipe, mem_ctx, &pw3);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_ChangePasswordUser3 failed: %s",
|
||||
nt_errstr(status));
|
||||
goto ChangePasswordUser2;
|
||||
}
|
||||
|
||||
/* check result of samr_ChangePasswordUser3 */
|
||||
if (!NT_STATUS_IS_OK(pw3.out.result)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_ChangePasswordUser3 for '%s\\%s' failed: %s",
|
||||
r->samr.in.domain_name, r->samr.in.account_name,
|
||||
nt_errstr(pw3.out.result));
|
||||
/* TODO: give the reason of the reject */
|
||||
if (NT_STATUS_EQUAL(pw3.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
|
||||
status = pw3.out.result;
|
||||
goto disconnect;
|
||||
}
|
||||
goto ChangePasswordUser2;
|
||||
}
|
||||
|
||||
goto disconnect;
|
||||
|
||||
ChangePasswordUser2:
|
||||
/* prepare samr_ChangePasswordUser2 */
|
||||
encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII|STR_TERMINATE);
|
||||
arcfour_crypt(lm_pass.data, old_lm_hash, 516);
|
||||
E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
|
||||
|
||||
encode_pw_buffer(nt_pass.data, r->samr.in.newpassword, STR_UNICODE);
|
||||
arcfour_crypt(nt_pass.data, old_nt_hash, 516);
|
||||
E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
|
||||
|
||||
pw2.in.server = &server;
|
||||
pw2.in.account = &account;
|
||||
pw2.in.nt_password = &nt_pass;
|
||||
pw2.in.nt_verifier = &nt_verifier;
|
||||
pw2.in.lm_change = 1;
|
||||
pw2.in.lm_password = &lm_pass;
|
||||
pw2.in.lm_verifier = &lm_verifier;
|
||||
|
||||
/* 3. try samr_ChangePasswordUser2 */
|
||||
status = dcerpc_samr_ChangePasswordUser2(c.out.dcerpc_pipe, mem_ctx, &pw2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_ChangePasswordUser2 failed: %s",
|
||||
nt_errstr(status));
|
||||
goto OemChangePasswordUser2;
|
||||
}
|
||||
|
||||
/* check result of samr_ChangePasswordUser2 */
|
||||
if (!NT_STATUS_IS_OK(pw2.out.result)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_ChangePasswordUser2 for '%s\\%s' failed: %s",
|
||||
r->samr.in.domain_name, r->samr.in.account_name,
|
||||
nt_errstr(pw2.out.result));
|
||||
if (NT_STATUS_EQUAL(pw2.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
|
||||
status = pw2.out.result;
|
||||
goto disconnect;
|
||||
}
|
||||
goto OemChangePasswordUser2;
|
||||
}
|
||||
|
||||
goto disconnect;
|
||||
|
||||
OemChangePasswordUser2:
|
||||
/* prepare samr_OemChangePasswordUser2 */
|
||||
a_server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
|
||||
a_account.string = r->samr.in.account_name;
|
||||
|
||||
encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII);
|
||||
arcfour_crypt(lm_pass.data, old_lm_hash, 516);
|
||||
E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
|
||||
|
||||
oe2.in.server = &a_server;
|
||||
oe2.in.account = &a_account;
|
||||
oe2.in.password = &lm_pass;
|
||||
oe2.in.hash = &lm_verifier;
|
||||
|
||||
/* 4. try samr_OemChangePasswordUser2 */
|
||||
status = dcerpc_samr_OemChangePasswordUser2(c.out.dcerpc_pipe, mem_ctx, &oe2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_OemChangePasswordUser2 failed: %s",
|
||||
nt_errstr(status));
|
||||
goto ChangePasswordUser;
|
||||
}
|
||||
|
||||
/* check result of samr_OemChangePasswordUser2 */
|
||||
if (!NT_STATUS_IS_OK(oe2.out.result)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_OemChangePasswordUser2 for '%s\\%s' failed: %s",
|
||||
r->samr.in.domain_name, r->samr.in.account_name,
|
||||
nt_errstr(oe2.out.result));
|
||||
if (NT_STATUS_EQUAL(oe2.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
|
||||
status = oe2.out.result;
|
||||
goto disconnect;
|
||||
}
|
||||
goto ChangePasswordUser;
|
||||
}
|
||||
|
||||
goto disconnect;
|
||||
|
||||
ChangePasswordUser:
|
||||
#if 0
|
||||
/* prepare samr_ChangePasswordUser */
|
||||
E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
|
||||
E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
|
||||
E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
|
||||
E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
|
||||
E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
|
||||
E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
|
||||
|
||||
/* TODO: ask for a user_handle */
|
||||
pw.in.handle = &user_handle;
|
||||
pw.in.lm_present = 1;
|
||||
pw.in.old_lm_crypted = &hash1;
|
||||
pw.in.new_lm_crypted = &hash2;
|
||||
pw.in.nt_present = 1;
|
||||
pw.in.old_nt_crypted = &hash3;
|
||||
pw.in.new_nt_crypted = &hash4;
|
||||
pw.in.cross1_present = 1;
|
||||
pw.in.nt_cross = &hash5;
|
||||
pw.in.cross2_present = 1;
|
||||
pw.in.lm_cross = &hash6;
|
||||
|
||||
/* 5. try samr_ChangePasswordUser */
|
||||
status = dcerpc_samr_ChangePasswordUser(c.pdc.out.dcerpc_pipe, mem_ctx, &pw);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_ChangePasswordUser failed: %s",
|
||||
nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* check result of samr_ChangePasswordUser */
|
||||
if (!NT_STATUS_IS_OK(pw.out.result)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_ChangePasswordUser for '%s\\%s' failed: %s",
|
||||
r->samr.in.domain_name, r->samr.in.account_name,
|
||||
nt_errstr(pw.out.result));
|
||||
if (NT_STATUS_EQUAL(pw.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
|
||||
status = pw.out.result;
|
||||
goto disconnect;
|
||||
}
|
||||
goto disconnect;
|
||||
}
|
||||
#endif
|
||||
disconnect:
|
||||
/* close connection */
|
||||
talloc_free(c.out.dcerpc_pipe);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_ChangePassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
union libnet_ChangePassword r2;
|
||||
|
||||
r2.samr.level = LIBNET_CHANGE_PASSWORD_SAMR;
|
||||
r2.samr.in.account_name = r->generic.in.account_name;
|
||||
r2.samr.in.domain_name = r->generic.in.domain_name;
|
||||
r2.samr.in.oldpassword = r->generic.in.oldpassword;
|
||||
r2.samr.in.newpassword = r->generic.in.newpassword;
|
||||
|
||||
status = libnet_ChangePassword(ctx, mem_ctx, &r2);
|
||||
|
||||
r->generic.out.error_string = r2.samr.out.error_string;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_ChangePassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
|
||||
{
|
||||
switch (r->generic.level) {
|
||||
case LIBNET_CHANGE_PASSWORD_GENERIC:
|
||||
return libnet_ChangePassword_generic(ctx, mem_ctx, r);
|
||||
case LIBNET_CHANGE_PASSWORD_SAMR:
|
||||
return libnet_ChangePassword_samr(ctx, mem_ctx, r);
|
||||
case LIBNET_CHANGE_PASSWORD_KRB5:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
case LIBNET_CHANGE_PASSWORD_LDAP:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
case LIBNET_CHANGE_PASSWORD_RAP:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_SetPassword_samr_handle_26(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct samr_SetUserInfo2 sui;
|
||||
union samr_UserInfo u_info;
|
||||
DATA_BLOB session_key;
|
||||
DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
uint8_t confounder[16];
|
||||
struct MD5Context md5;
|
||||
|
||||
if (r->samr_handle.in.info21) {
|
||||
return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
}
|
||||
|
||||
/* prepare samr_SetUserInfo2 level 26 */
|
||||
ZERO_STRUCT(u_info);
|
||||
encode_pw_buffer(u_info.info26.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
|
||||
u_info.info26.pw_len = strlen(r->samr_handle.in.newpassword);
|
||||
|
||||
status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"dcerpc_fetch_session_key failed: %s",
|
||||
nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
generate_random_buffer((uint8_t *)confounder, 16);
|
||||
|
||||
MD5Init(&md5);
|
||||
MD5Update(&md5, confounder, 16);
|
||||
MD5Update(&md5, session_key.data, session_key.length);
|
||||
MD5Final(confounded_session_key.data, &md5);
|
||||
|
||||
arcfour_crypt_blob(u_info.info26.password.data, 516, &confounded_session_key);
|
||||
memcpy(&u_info.info26.password.data[516], confounder, 16);
|
||||
|
||||
sui.in.user_handle = r->samr_handle.in.user_handle;
|
||||
sui.in.info = &u_info;
|
||||
sui.in.level = 26;
|
||||
|
||||
/* 7. try samr_SetUserInfo2 level 26 to set the password */
|
||||
status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
|
||||
/* check result of samr_SetUserInfo2 level 26 */
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string
|
||||
= talloc_asprintf(mem_ctx,
|
||||
"SetUserInfo2 level 26 for [%s] failed: %s",
|
||||
r->samr_handle.in.account_name, nt_errstr(status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_SetPassword_samr_handle_25(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct samr_SetUserInfo2 sui;
|
||||
union samr_UserInfo u_info;
|
||||
DATA_BLOB session_key;
|
||||
DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
uint8_t confounder[16];
|
||||
struct MD5Context md5;
|
||||
|
||||
if (!r->samr_handle.in.info21) {
|
||||
return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
}
|
||||
|
||||
/* prepare samr_SetUserInfo2 level 25 */
|
||||
ZERO_STRUCT(u_info);
|
||||
u_info.info25.info = *r->samr_handle.in.info21;
|
||||
u_info.info25.info.fields_present |= SAMR_FIELD_PASSWORD;
|
||||
encode_pw_buffer(u_info.info25.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
|
||||
|
||||
status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"dcerpc_fetch_session_key failed: %s",
|
||||
nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
generate_random_buffer((uint8_t *)confounder, 16);
|
||||
|
||||
MD5Init(&md5);
|
||||
MD5Update(&md5, confounder, 16);
|
||||
MD5Update(&md5, session_key.data, session_key.length);
|
||||
MD5Final(confounded_session_key.data, &md5);
|
||||
|
||||
arcfour_crypt_blob(u_info.info25.password.data, 516, &confounded_session_key);
|
||||
memcpy(&u_info.info25.password.data[516], confounder, 16);
|
||||
|
||||
sui.in.user_handle = r->samr_handle.in.user_handle;
|
||||
sui.in.info = &u_info;
|
||||
sui.in.level = 25;
|
||||
|
||||
/* 8. try samr_SetUserInfo2 level 25 to set the password */
|
||||
status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string
|
||||
= talloc_asprintf(mem_ctx,
|
||||
"SetUserInfo2 level 25 for [%s] failed: %s",
|
||||
r->samr_handle.in.account_name, nt_errstr(status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_SetPassword_samr_handle_24(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct samr_SetUserInfo2 sui;
|
||||
union samr_UserInfo u_info;
|
||||
DATA_BLOB session_key;
|
||||
|
||||
if (r->samr_handle.in.info21) {
|
||||
return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
}
|
||||
|
||||
/* prepare samr_SetUserInfo2 level 24 */
|
||||
ZERO_STRUCT(u_info);
|
||||
encode_pw_buffer(u_info.info24.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
|
||||
/* w2k3 ignores this length */
|
||||
u_info.info24.pw_len = strlen_m(r->samr_handle.in.newpassword)*2;
|
||||
|
||||
status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"dcerpc_fetch_session_key failed: %s",
|
||||
nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
arcfour_crypt_blob(u_info.info24.password.data, 516, &session_key);
|
||||
|
||||
sui.in.user_handle = r->samr_handle.in.user_handle;
|
||||
sui.in.info = &u_info;
|
||||
sui.in.level = 24;
|
||||
|
||||
/* 9. try samr_SetUserInfo2 level 24 to set the password */
|
||||
status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string
|
||||
= talloc_asprintf(mem_ctx,
|
||||
"SetUserInfo2 level 24 for [%s] failed: %s",
|
||||
r->samr_handle.in.account_name, nt_errstr(status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_SetPassword_samr_handle_23(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct samr_SetUserInfo2 sui;
|
||||
union samr_UserInfo u_info;
|
||||
DATA_BLOB session_key;
|
||||
|
||||
if (!r->samr_handle.in.info21) {
|
||||
return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
}
|
||||
|
||||
/* prepare samr_SetUserInfo2 level 23 */
|
||||
ZERO_STRUCT(u_info);
|
||||
u_info.info23.info = *r->samr_handle.in.info21;
|
||||
u_info.info23.info.fields_present |= SAMR_FIELD_PASSWORD;
|
||||
encode_pw_buffer(u_info.info23.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
|
||||
|
||||
status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string
|
||||
= talloc_asprintf(mem_ctx,
|
||||
"dcerpc_fetch_session_key failed: %s",
|
||||
nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
arcfour_crypt_blob(u_info.info23.password.data, 516, &session_key);
|
||||
|
||||
sui.in.user_handle = r->samr_handle.in.user_handle;
|
||||
sui.in.info = &u_info;
|
||||
sui.in.level = 23;
|
||||
|
||||
/* 10. try samr_SetUserInfo2 level 23 to set the password */
|
||||
status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr_handle.out.error_string
|
||||
= talloc_asprintf(mem_ctx,
|
||||
"SetUserInfo2 level 23 for [%s] failed: %s",
|
||||
r->samr_handle.in.account_name, nt_errstr(status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. try samr_SetUserInfo2 level 26 to set the password
|
||||
* 2. try samr_SetUserInfo2 level 25 to set the password
|
||||
* 3. try samr_SetUserInfo2 level 24 to set the password
|
||||
* 4. try samr_SetUserInfo2 level 23 to set the password
|
||||
*/
|
||||
static NTSTATUS libnet_SetPassword_samr_handle(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
|
||||
NTSTATUS status;
|
||||
enum libnet_SetPassword_level levels[] = {
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(levels); i++) {
|
||||
r->generic.level = levels[i];
|
||||
status = libnet_SetPassword(ctx, mem_ctx, r);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)
|
||||
|| NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER_MIX)
|
||||
|| NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
|
||||
/* Try another password set mechanism */
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
/*
|
||||
* set a password with DCERPC/SAMR calls
|
||||
* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
|
||||
* is it correct to contact the the pdc of the domain of the user who's password should be set?
|
||||
* 2. do a samr_Connect to get a policy handle
|
||||
* 3. do a samr_LookupDomain to get the domain sid
|
||||
* 4. do a samr_OpenDomain to get a domain handle
|
||||
* 5. do a samr_LookupNames to get the users rid
|
||||
* 6. do a samr_OpenUser to get a user handle
|
||||
* 7 call libnet_SetPassword_samr_handle to set the password
|
||||
*/
|
||||
static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct libnet_RpcConnect c;
|
||||
struct samr_Connect sc;
|
||||
struct policy_handle p_handle;
|
||||
struct samr_LookupDomain ld;
|
||||
struct lsa_String d_name;
|
||||
struct samr_OpenDomain od;
|
||||
struct policy_handle d_handle;
|
||||
struct samr_LookupNames ln;
|
||||
struct samr_OpenUser ou;
|
||||
struct policy_handle u_handle;
|
||||
union libnet_SetPassword r2;
|
||||
|
||||
/* prepare connect to the SAMR pipe of users domain PDC */
|
||||
c.level = LIBNET_RPC_CONNECT_PDC;
|
||||
c.in.name = r->samr.in.domain_name;
|
||||
c.in.dcerpc_iface = &dcerpc_table_samr;
|
||||
|
||||
/* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
|
||||
status = libnet_RpcConnect(ctx, mem_ctx, &c);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to SAMR pipe of PDC of domain '%s' failed: %s",
|
||||
r->samr.in.domain_name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* prepare samr_Connect */
|
||||
ZERO_STRUCT(p_handle);
|
||||
sc.in.system_name = NULL;
|
||||
sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
sc.out.connect_handle = &p_handle;
|
||||
|
||||
/* 2. do a samr_Connect to get a policy handle */
|
||||
status = dcerpc_samr_Connect(c.out.dcerpc_pipe, mem_ctx, &sc);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_Connect failed: %s",
|
||||
nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* prepare samr_LookupDomain */
|
||||
d_name.string = r->samr.in.domain_name;
|
||||
ld.in.connect_handle = &p_handle;
|
||||
ld.in.domain_name = &d_name;
|
||||
|
||||
/* 3. do a samr_LookupDomain to get the domain sid */
|
||||
status = dcerpc_samr_LookupDomain(c.out.dcerpc_pipe, mem_ctx, &ld);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_LookupDomain for [%s] failed: %s",
|
||||
r->samr.in.domain_name, nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* prepare samr_OpenDomain */
|
||||
ZERO_STRUCT(d_handle);
|
||||
od.in.connect_handle = &p_handle;
|
||||
od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
od.in.sid = ld.out.sid;
|
||||
od.out.domain_handle = &d_handle;
|
||||
|
||||
/* 4. do a samr_OpenDomain to get a domain handle */
|
||||
status = dcerpc_samr_OpenDomain(c.out.dcerpc_pipe, mem_ctx, &od);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_OpenDomain for [%s] failed: %s",
|
||||
r->samr.in.domain_name, nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* prepare samr_LookupNames */
|
||||
ln.in.domain_handle = &d_handle;
|
||||
ln.in.num_names = 1;
|
||||
ln.in.names = talloc_array(mem_ctx, struct lsa_String, 1);
|
||||
if (!ln.in.names) {
|
||||
r->samr.out.error_string = "Out of Memory";
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
ln.in.names[0].string = r->samr.in.account_name;
|
||||
|
||||
/* 5. do a samr_LookupNames to get the users rid */
|
||||
status = dcerpc_samr_LookupNames(c.out.dcerpc_pipe, mem_ctx, &ln);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_LookupNames for [%s] failed: %s",
|
||||
r->samr.in.account_name, nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* check if we got one RID for the user */
|
||||
if (ln.out.rids.count != 1) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_LookupNames for [%s] returns %d RIDs",
|
||||
r->samr.in.account_name, ln.out.rids.count);
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* prepare samr_OpenUser */
|
||||
ZERO_STRUCT(u_handle);
|
||||
ou.in.domain_handle = &d_handle;
|
||||
ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
ou.in.rid = ln.out.rids.ids[0];
|
||||
ou.out.user_handle = &u_handle;
|
||||
|
||||
/* 6. do a samr_OpenUser to get a user handle */
|
||||
status = dcerpc_samr_OpenUser(c.out.dcerpc_pipe, mem_ctx, &ou);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->samr.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"samr_OpenUser for [%s] failed: %s",
|
||||
r->samr.in.account_name, nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
|
||||
r2.samr_handle.in.account_name = r->samr.in.account_name;
|
||||
r2.samr_handle.in.newpassword = r->samr.in.newpassword;
|
||||
r2.samr_handle.in.user_handle = &u_handle;
|
||||
r2.samr_handle.in.dcerpc_pipe = c.out.dcerpc_pipe;
|
||||
|
||||
status = libnet_SetPassword(ctx, mem_ctx, &r2);
|
||||
|
||||
r->generic.out.error_string = r2.samr_handle.out.error_string;
|
||||
|
||||
disconnect:
|
||||
/* close connection */
|
||||
talloc_free(c.out.dcerpc_pipe);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_SetPassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
union libnet_SetPassword r2;
|
||||
|
||||
r2.samr.level = LIBNET_SET_PASSWORD_SAMR;
|
||||
r2.samr.in.account_name = r->generic.in.account_name;
|
||||
r2.samr.in.domain_name = r->generic.in.domain_name;
|
||||
r2.samr.in.newpassword = r->generic.in.newpassword;
|
||||
|
||||
r->generic.out.error_string = "Unknown Error";
|
||||
status = libnet_SetPassword(ctx, mem_ctx, &r2);
|
||||
|
||||
r->generic.out.error_string = r2.samr.out.error_string;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_SetPassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
|
||||
{
|
||||
switch (r->generic.level) {
|
||||
case LIBNET_SET_PASSWORD_GENERIC:
|
||||
return libnet_SetPassword_generic(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_SAMR:
|
||||
return libnet_SetPassword_samr(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_SAMR_HANDLE:
|
||||
return libnet_SetPassword_samr_handle(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_SAMR_HANDLE_26:
|
||||
return libnet_SetPassword_samr_handle_26(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_SAMR_HANDLE_25:
|
||||
return libnet_SetPassword_samr_handle_25(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_SAMR_HANDLE_24:
|
||||
return libnet_SetPassword_samr_handle_24(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_SAMR_HANDLE_23:
|
||||
return libnet_SetPassword_samr_handle_23(ctx, mem_ctx, r);
|
||||
case LIBNET_SET_PASSWORD_KRB5:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
case LIBNET_SET_PASSWORD_LDAP:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
case LIBNET_SET_PASSWORD_RAP:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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.
|
||||
*/
|
||||
|
||||
/* struct and enum for doing a remote password change */
|
||||
enum libnet_ChangePassword_level {
|
||||
LIBNET_CHANGE_PASSWORD_GENERIC,
|
||||
LIBNET_CHANGE_PASSWORD_SAMR,
|
||||
LIBNET_CHANGE_PASSWORD_KRB5,
|
||||
LIBNET_CHANGE_PASSWORD_LDAP,
|
||||
LIBNET_CHANGE_PASSWORD_RAP
|
||||
};
|
||||
|
||||
union libnet_ChangePassword {
|
||||
struct {
|
||||
enum libnet_ChangePassword_level level;
|
||||
|
||||
struct _libnet_ChangePassword_in {
|
||||
const char *account_name;
|
||||
const char *domain_name;
|
||||
const char *oldpassword;
|
||||
const char *newpassword;
|
||||
} in;
|
||||
|
||||
struct _libnet_ChangePassword_out {
|
||||
const char *error_string;
|
||||
} out;
|
||||
} generic;
|
||||
|
||||
struct {
|
||||
enum libnet_ChangePassword_level level;
|
||||
struct _libnet_ChangePassword_in in;
|
||||
struct _libnet_ChangePassword_out out;
|
||||
} samr;
|
||||
|
||||
struct {
|
||||
enum libnet_ChangePassword_level level;
|
||||
struct _libnet_ChangePassword_in in;
|
||||
struct _libnet_ChangePassword_out out;
|
||||
} krb5;
|
||||
|
||||
struct {
|
||||
enum libnet_ChangePassword_level level;
|
||||
struct _libnet_ChangePassword_in in;
|
||||
struct _libnet_ChangePassword_out out;
|
||||
} ldap;
|
||||
|
||||
struct {
|
||||
enum libnet_ChangePassword_level level;
|
||||
struct _libnet_ChangePassword_in in;
|
||||
struct _libnet_ChangePassword_out out;
|
||||
} rap;
|
||||
};
|
||||
|
||||
/* struct and enum for doing a remote password set */
|
||||
enum libnet_SetPassword_level {
|
||||
LIBNET_SET_PASSWORD_GENERIC,
|
||||
LIBNET_SET_PASSWORD_SAMR,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
|
||||
LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
|
||||
LIBNET_SET_PASSWORD_KRB5,
|
||||
LIBNET_SET_PASSWORD_LDAP,
|
||||
LIBNET_SET_PASSWORD_RAP
|
||||
};
|
||||
|
||||
union libnet_SetPassword {
|
||||
struct {
|
||||
enum libnet_SetPassword_level level;
|
||||
|
||||
struct _libnet_SetPassword_in {
|
||||
const char *account_name;
|
||||
const char *domain_name;
|
||||
const char *newpassword;
|
||||
} in;
|
||||
|
||||
struct _libnet_SetPassword_out {
|
||||
const char *error_string;
|
||||
} out;
|
||||
} generic;
|
||||
|
||||
struct {
|
||||
enum libnet_SetPassword_level level;
|
||||
struct _libnet_SetPassword_samr_handle_in {
|
||||
const char *account_name; /* for debug only */
|
||||
struct policy_handle *user_handle;
|
||||
struct dcerpc_pipe *dcerpc_pipe;
|
||||
const char *newpassword;
|
||||
struct samr_UserInfo21 *info21; /* can be NULL,
|
||||
* for level 26,24 it must be NULL
|
||||
* for level 25,23 it must be non-NULL
|
||||
*/
|
||||
} in;
|
||||
struct _libnet_SetPassword_out out;
|
||||
} samr_handle;
|
||||
|
||||
struct {
|
||||
enum libnet_SetPassword_level level;
|
||||
struct _libnet_SetPassword_in in;
|
||||
struct _libnet_SetPassword_out out;
|
||||
} samr;
|
||||
|
||||
struct {
|
||||
enum libnet_SetPassword_level level;
|
||||
struct _libnet_SetPassword_in in;
|
||||
struct _libnet_SetPassword_out out;
|
||||
} krb5;
|
||||
|
||||
struct {
|
||||
enum libnet_SetPassword_level level;
|
||||
struct _libnet_SetPassword_in in;
|
||||
struct _libnet_SetPassword_out out;
|
||||
} ldap;
|
||||
|
||||
struct {
|
||||
enum libnet_ChangePassword_level level;
|
||||
struct _libnet_SetPassword_in in;
|
||||
struct _libnet_SetPassword_out out;
|
||||
} rap;
|
||||
};
|
||||
@@ -0,0 +1,871 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Rafal Szczesniak 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 "libnet/libnet.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
#include "librpc/gen_ndr/ndr_lsa_c.h"
|
||||
#include "librpc/gen_ndr/ndr_samr.h"
|
||||
|
||||
|
||||
struct rpc_connect_srv_state {
|
||||
struct libnet_context *ctx;
|
||||
struct libnet_RpcConnect r;
|
||||
const char *binding;
|
||||
};
|
||||
|
||||
|
||||
static void continue_pipe_connect(struct composite_context *ctx);
|
||||
|
||||
|
||||
/**
|
||||
* Initiates connection to rpc pipe on remote server
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return composite context of this call
|
||||
**/
|
||||
|
||||
static struct composite_context* libnet_RpcConnectSrv_send(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_srv_state *s;
|
||||
struct dcerpc_binding *b;
|
||||
struct composite_context *pipe_connect_req;
|
||||
|
||||
/* composite context allocation and setup */
|
||||
c = talloc_zero(mem_ctx, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct rpc_connect_srv_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = ctx->event_ctx;
|
||||
|
||||
s->ctx = ctx;
|
||||
s->r = *r;
|
||||
ZERO_STRUCT(s->r.out);
|
||||
|
||||
/* prepare binding string */
|
||||
switch (r->level) {
|
||||
case LIBNET_RPC_CONNECT_SERVER:
|
||||
s->binding = talloc_asprintf(s, "ncacn_np:%s", r->in.name);
|
||||
break;
|
||||
case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
|
||||
s->binding = talloc_asprintf(s, "ncacn_np:%s", r->in.address);
|
||||
break;
|
||||
|
||||
case LIBNET_RPC_CONNECT_BINDING:
|
||||
s->binding = talloc_strdup(s, r->in.binding);
|
||||
break;
|
||||
|
||||
case LIBNET_RPC_CONNECT_DC:
|
||||
case LIBNET_RPC_CONNECT_PDC:
|
||||
/* this should never happen - DC and PDC level has a separate
|
||||
composite function */
|
||||
case LIBNET_RPC_CONNECT_DC_INFO:
|
||||
/* this should never happen - DC_INFO level has a separate
|
||||
composite function */
|
||||
composite_error(c, NT_STATUS_INVALID_LEVEL);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* parse binding string to the structure */
|
||||
c->status = dcerpc_parse_binding(c, s->binding, &b);
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", s->binding));
|
||||
composite_error(c, c->status);
|
||||
return c;
|
||||
}
|
||||
|
||||
if (r->level == LIBNET_RPC_CONNECT_SERVER_ADDRESS) {
|
||||
b->target_hostname = talloc_reference(b, r->in.name);
|
||||
if (composite_nomem(b->target_hostname, c)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/* connect to remote dcerpc pipe */
|
||||
pipe_connect_req = dcerpc_pipe_connect_b_send(c, b, r->in.dcerpc_iface,
|
||||
ctx->cred, c->event_ctx);
|
||||
if (composite_nomem(pipe_connect_req, c)) return c;
|
||||
|
||||
composite_continue(c, pipe_connect_req, continue_pipe_connect, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 2 of RpcConnectSrv - get rpc connection
|
||||
*/
|
||||
static void continue_pipe_connect(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_srv_state *s;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
|
||||
|
||||
/* receive result of rpc pipe connection */
|
||||
c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->r.out.dcerpc_pipe);
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives result of connection to rpc pipe on remote server
|
||||
*
|
||||
* @param c composite context
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return nt status of rpc connection
|
||||
**/
|
||||
|
||||
static NTSTATUS libnet_RpcConnectSrv_recv(struct composite_context *c,
|
||||
struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct rpc_connect_srv_state *s = talloc_get_type(c->private_data,
|
||||
struct rpc_connect_srv_state);
|
||||
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
/* move the returned rpc pipe between memory contexts */
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
|
||||
r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
|
||||
|
||||
/* reference created pipe structure to long-term libnet_context
|
||||
so that it can be used by other api functions even after short-term
|
||||
mem_ctx is freed */
|
||||
if (r->in.dcerpc_iface == &dcerpc_table_samr) {
|
||||
ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
|
||||
|
||||
} else if (r->in.dcerpc_iface == &dcerpc_table_lsarpc) {
|
||||
ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
|
||||
}
|
||||
|
||||
r->out.error_string = talloc_strdup(mem_ctx, "Success");
|
||||
|
||||
} else {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
struct rpc_connect_dc_state {
|
||||
struct libnet_context *ctx;
|
||||
struct libnet_RpcConnect r;
|
||||
struct libnet_RpcConnect r2;
|
||||
struct libnet_LookupDCs f;
|
||||
const char *connect_name;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg *);
|
||||
};
|
||||
|
||||
|
||||
static void continue_lookup_dc(struct composite_context *ctx);
|
||||
static void continue_rpc_connect(struct composite_context *ctx);
|
||||
|
||||
|
||||
/**
|
||||
* Initiates connection to rpc pipe on domain pdc
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return composite context of this call
|
||||
**/
|
||||
|
||||
static struct composite_context* libnet_RpcConnectDC_send(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dc_state *s;
|
||||
struct composite_context *lookup_dc_req;
|
||||
|
||||
/* composite context allocation and setup */
|
||||
c = talloc_zero(mem_ctx, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct rpc_connect_dc_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = ctx->event_ctx;
|
||||
|
||||
s->ctx = ctx;
|
||||
s->r = *r;
|
||||
ZERO_STRUCT(s->r.out);
|
||||
|
||||
switch (r->level) {
|
||||
case LIBNET_RPC_CONNECT_PDC:
|
||||
s->f.in.name_type = NBT_NAME_PDC;
|
||||
break;
|
||||
|
||||
case LIBNET_RPC_CONNECT_DC:
|
||||
s->f.in.name_type = NBT_NAME_LOGON;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
s->f.in.domain_name = r->in.name;
|
||||
s->f.out.num_dcs = 0;
|
||||
s->f.out.dcs = NULL;
|
||||
|
||||
/* find the domain pdc first */
|
||||
lookup_dc_req = libnet_LookupDCs_send(ctx, c, &s->f);
|
||||
if (composite_nomem(lookup_dc_req, c)) return c;
|
||||
|
||||
composite_continue(c, lookup_dc_req, continue_lookup_dc, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 2 of RpcConnectDC: get domain controller name and
|
||||
initiate RpcConnect to it
|
||||
*/
|
||||
static void continue_lookup_dc(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dc_state *s;
|
||||
struct composite_context *rpc_connect_req;
|
||||
struct monitor_msg msg;
|
||||
struct msg_net_lookup_dc data;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
|
||||
|
||||
/* receive result of domain controller lookup */
|
||||
c->status = libnet_LookupDCs_recv(ctx, c, &s->f);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
/* decide on preferred address type depending on DC type */
|
||||
s->connect_name = s->f.out.dcs[0].name;
|
||||
|
||||
/* prepare a monitor message and post it */
|
||||
msg.type = net_lookup_dc;
|
||||
msg.data = &data;
|
||||
msg.data_size = sizeof(data);
|
||||
|
||||
data.domain_name = s->f.in.domain_name;
|
||||
data.hostname = s->f.out.dcs[0].name;
|
||||
data.address = s->f.out.dcs[0].address;
|
||||
|
||||
if (s->monitor_fn) s->monitor_fn(&msg);
|
||||
|
||||
/* ok, pdc has been found so do attempt to rpc connect */
|
||||
s->r2.level = LIBNET_RPC_CONNECT_SERVER_ADDRESS;
|
||||
|
||||
/* this will cause yet another name resolution, but at least
|
||||
* we pass the right name down the stack now */
|
||||
s->r2.in.name = talloc_strdup(s, s->connect_name);
|
||||
s->r2.in.address = talloc_steal(s, s->f.out.dcs[0].address);
|
||||
s->r2.in.dcerpc_iface = s->r.in.dcerpc_iface;
|
||||
|
||||
/* send rpc connect request to the server */
|
||||
rpc_connect_req = libnet_RpcConnectSrv_send(s->ctx, c, &s->r2);
|
||||
if (composite_nomem(rpc_connect_req, c)) return;
|
||||
|
||||
composite_continue(c, rpc_connect_req, continue_rpc_connect, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 3 of RpcConnectDC: get rpc connection to the server
|
||||
*/
|
||||
static void continue_rpc_connect(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dc_state *s;
|
||||
struct monitor_msg msg;
|
||||
struct msg_net_pipe_connected data;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
|
||||
|
||||
c->status = libnet_RpcConnectSrv_recv(ctx, s->ctx, c, &s->r2);
|
||||
|
||||
/* error string is to be passed anyway */
|
||||
s->r.out.error_string = s->r2.out.error_string;
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
s->r.out.dcerpc_pipe = s->r2.out.dcerpc_pipe;
|
||||
|
||||
/* prepare a monitor message and post it */
|
||||
data.host = s->r.out.dcerpc_pipe->binding->host;
|
||||
data.endpoint = s->r.out.dcerpc_pipe->binding->endpoint;
|
||||
data.transport = s->r.out.dcerpc_pipe->binding->transport;
|
||||
|
||||
msg.type = net_pipe_connected;
|
||||
msg.data = (void*)&data;
|
||||
msg.data_size = sizeof(data);
|
||||
|
||||
if (s->monitor_fn) s->monitor_fn(&msg);
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives result of connection to rpc pipe on domain pdc
|
||||
*
|
||||
* @param c composite context
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return nt status of rpc connection
|
||||
**/
|
||||
|
||||
static NTSTATUS libnet_RpcConnectDC_recv(struct composite_context *c,
|
||||
struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct rpc_connect_dc_state *s = talloc_get_type(c->private_data,
|
||||
struct rpc_connect_dc_state);
|
||||
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
/* move connected rpc pipe between memory contexts */
|
||||
r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
|
||||
|
||||
/* reference created pipe structure to long-term libnet_context
|
||||
so that it can be used by other api functions even after short-term
|
||||
mem_ctx is freed */
|
||||
if (r->in.dcerpc_iface == &dcerpc_table_samr) {
|
||||
ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
|
||||
|
||||
} else if (r->in.dcerpc_iface == &dcerpc_table_lsarpc) {
|
||||
ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
|
||||
}
|
||||
|
||||
} else {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Failed to rpc connect: %s",
|
||||
nt_errstr(status));
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct rpc_connect_dci_state {
|
||||
struct libnet_context *ctx;
|
||||
struct libnet_RpcConnect r;
|
||||
struct libnet_RpcConnect rpc_conn;
|
||||
struct policy_handle lsa_handle;
|
||||
struct lsa_QosInfo qos;
|
||||
struct lsa_ObjectAttribute attr;
|
||||
struct lsa_OpenPolicy2 lsa_open_policy;
|
||||
struct dcerpc_pipe *lsa_pipe;
|
||||
struct lsa_QueryInfoPolicy2 lsa_query_info2;
|
||||
struct lsa_QueryInfoPolicy lsa_query_info;
|
||||
struct dcerpc_binding *final_binding;
|
||||
struct dcerpc_pipe *final_pipe;
|
||||
};
|
||||
|
||||
|
||||
static void continue_dci_rpc_connect(struct composite_context *ctx);
|
||||
static void continue_lsa_policy(struct rpc_request *req);
|
||||
static void continue_lsa_query_info(struct rpc_request *req);
|
||||
static void continue_lsa_query_info2(struct rpc_request *req);
|
||||
static void continue_epm_map_binding(struct composite_context *ctx);
|
||||
static void continue_secondary_conn(struct composite_context *ctx);
|
||||
static void continue_epm_map_binding_send(struct composite_context *c);
|
||||
|
||||
|
||||
/**
|
||||
* Initiates connection to rpc pipe on remote server or pdc. Received result
|
||||
* contains info on the domain name, domain sid and realm.
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values. Must be a talloc context
|
||||
* @return composite context of this call
|
||||
**/
|
||||
|
||||
static struct composite_context* libnet_RpcConnectDCInfo_send(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
struct composite_context *c, *conn_req;
|
||||
struct rpc_connect_dci_state *s;
|
||||
|
||||
/* composite context allocation and setup */
|
||||
c = talloc_zero(mem_ctx, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct rpc_connect_dci_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = ctx->event_ctx;
|
||||
|
||||
s->ctx = ctx;
|
||||
s->r = *r;
|
||||
ZERO_STRUCT(s->r.out);
|
||||
|
||||
/* proceed to pure rpc connection if the binding string is provided,
|
||||
otherwise try to connect domain controller */
|
||||
if (r->in.binding == NULL) {
|
||||
s->rpc_conn.in.name = r->in.name;
|
||||
s->rpc_conn.level = LIBNET_RPC_CONNECT_DC;
|
||||
} else {
|
||||
s->rpc_conn.in.binding = r->in.binding;
|
||||
s->rpc_conn.level = LIBNET_RPC_CONNECT_BINDING;
|
||||
}
|
||||
|
||||
/* we need to query information on lsarpc interface first */
|
||||
s->rpc_conn.in.dcerpc_iface = &dcerpc_table_lsarpc;
|
||||
|
||||
/* request connection to the lsa pipe on the pdc */
|
||||
conn_req = libnet_RpcConnect_send(ctx, c, &s->rpc_conn);
|
||||
if (composite_nomem(c, conn_req)) return c;
|
||||
|
||||
composite_continue(c, conn_req, continue_dci_rpc_connect, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 2 of RpcConnectDCInfo: receive opened rpc pipe and open
|
||||
lsa policy handle
|
||||
*/
|
||||
static void continue_dci_rpc_connect(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dci_state *s;
|
||||
struct rpc_request *open_pol_req;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpc_conn);
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prepare to open a policy handle on lsa pipe */
|
||||
s->lsa_pipe = s->ctx->lsa.pipe;
|
||||
|
||||
s->qos.len = 0;
|
||||
s->qos.impersonation_level = 2;
|
||||
s->qos.context_mode = 1;
|
||||
s->qos.effective_only = 0;
|
||||
|
||||
s->attr.sec_qos = &s->qos;
|
||||
|
||||
s->lsa_open_policy.in.attr = &s->attr;
|
||||
s->lsa_open_policy.in.system_name = talloc_asprintf(c, "\\");
|
||||
if (composite_nomem(s->lsa_open_policy.in.system_name, c)) return;
|
||||
|
||||
s->lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
s->lsa_open_policy.out.handle = &s->lsa_handle;
|
||||
|
||||
open_pol_req = dcerpc_lsa_OpenPolicy2_send(s->lsa_pipe, c, &s->lsa_open_policy);
|
||||
if (composite_nomem(open_pol_req, c)) return;
|
||||
|
||||
composite_continue_rpc(c, open_pol_req, continue_lsa_policy, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 3 of RpcConnectDCInfo: Get policy handle and query lsa info
|
||||
for kerberos realm (dns name) and guid. The query may fail.
|
||||
*/
|
||||
static void continue_lsa_policy(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dci_state *s;
|
||||
struct rpc_request *query_info_req;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NT_STATUS_EQUAL(s->lsa_open_policy.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
|
||||
s->r.out.realm = NULL;
|
||||
s->r.out.guid = NULL;
|
||||
s->r.out.domain_name = NULL;
|
||||
s->r.out.domain_sid = NULL;
|
||||
/* Skip to the creating the actual connection, no info available on this transport */
|
||||
continue_epm_map_binding_send(c);
|
||||
return;
|
||||
} else if (!NT_STATUS_IS_OK(s->lsa_open_policy.out.result)) {
|
||||
composite_error(c, s->lsa_open_policy.out.result);
|
||||
return;
|
||||
}
|
||||
|
||||
/* query lsa info for dns domain name and guid */
|
||||
s->lsa_query_info2.in.handle = &s->lsa_handle;
|
||||
s->lsa_query_info2.in.level = LSA_POLICY_INFO_DNS;
|
||||
|
||||
query_info_req = dcerpc_lsa_QueryInfoPolicy2_send(s->lsa_pipe, c, &s->lsa_query_info2);
|
||||
if (composite_nomem(query_info_req, c)) return;
|
||||
|
||||
composite_continue_rpc(c, query_info_req, continue_lsa_query_info2, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 4 of RpcConnectDCInfo: Get realm and guid if provided (rpc call
|
||||
may result in failure) and query lsa info for domain name and sid.
|
||||
*/
|
||||
static void continue_lsa_query_info2(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dci_state *s;
|
||||
struct rpc_request *query_info_req;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
|
||||
/* In case of error just null the realm and guid and proceed
|
||||
to the next step. After all, it doesn't have to be AD domain
|
||||
controller we talking to - NT-style PDC also counts */
|
||||
|
||||
if (NT_STATUS_EQUAL(c->status, NT_STATUS_NET_WRITE_FAULT)) {
|
||||
s->r.out.realm = NULL;
|
||||
s->r.out.guid = NULL;
|
||||
|
||||
} else {
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
s->r.out.error_string = talloc_asprintf(c,
|
||||
"lsa_QueryInfoPolicy2 failed: %s",
|
||||
nt_errstr(c->status));
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(s->lsa_query_info2.out.result)) {
|
||||
s->r.out.error_string = talloc_asprintf(c,
|
||||
"lsa_QueryInfoPolicy2 failed: %s",
|
||||
nt_errstr(s->lsa_query_info2.out.result));
|
||||
composite_error(c, s->lsa_query_info2.out.result);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy the dns domain name and guid from the query result */
|
||||
|
||||
/* this should actually be a conversion from lsa_StringLarge */
|
||||
s->r.out.realm = s->lsa_query_info2.out.info->dns.dns_domain.string;
|
||||
s->r.out.guid = talloc(c, struct GUID);
|
||||
if (composite_nomem(s->r.out.guid, c)) {
|
||||
s->r.out.error_string = NULL;
|
||||
return;
|
||||
}
|
||||
*s->r.out.guid = s->lsa_query_info2.out.info->dns.domain_guid;
|
||||
}
|
||||
|
||||
/* query lsa info for domain name and sid */
|
||||
s->lsa_query_info.in.handle = &s->lsa_handle;
|
||||
s->lsa_query_info.in.level = LSA_POLICY_INFO_DOMAIN;
|
||||
|
||||
query_info_req = dcerpc_lsa_QueryInfoPolicy_send(s->lsa_pipe, c, &s->lsa_query_info);
|
||||
if (composite_nomem(query_info_req, c)) return;
|
||||
|
||||
composite_continue_rpc(c, query_info_req, continue_lsa_query_info, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 5 of RpcConnectDCInfo: Get domain name and sid
|
||||
*/
|
||||
static void continue_lsa_query_info(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dci_state *s;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
s->r.out.error_string = talloc_asprintf(c,
|
||||
"lsa_QueryInfoPolicy failed: %s",
|
||||
nt_errstr(c->status));
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy the domain name and sid from the query result */
|
||||
s->r.out.domain_sid = s->lsa_query_info.out.info->domain.sid;
|
||||
s->r.out.domain_name = s->lsa_query_info.out.info->domain.name.string;
|
||||
|
||||
continue_epm_map_binding_send(c);
|
||||
}
|
||||
|
||||
/*
|
||||
Step 5 (continued) of RpcConnectDCInfo: request endpoint
|
||||
map binding.
|
||||
|
||||
We may short-cut to this step if we dont' support LSA OpenPolicy on this transport
|
||||
*/
|
||||
static void continue_epm_map_binding_send(struct composite_context *c)
|
||||
{
|
||||
struct rpc_connect_dci_state *s;
|
||||
struct composite_context *epm_map_req;
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
/* prepare to get endpoint mapping for the requested interface */
|
||||
s->final_binding = talloc(s, struct dcerpc_binding);
|
||||
if (composite_nomem(s->final_binding, c)) return;
|
||||
|
||||
*s->final_binding = *s->lsa_pipe->binding;
|
||||
/* Ensure we keep hold of the member elements */
|
||||
if (composite_nomem(talloc_reference(s->final_binding, s->lsa_pipe->binding), c)) return;
|
||||
|
||||
epm_map_req = dcerpc_epm_map_binding_send(c, s->final_binding, s->r.in.dcerpc_iface,
|
||||
s->lsa_pipe->conn->event_ctx);
|
||||
if (composite_nomem(epm_map_req, c)) return;
|
||||
|
||||
composite_continue(c, epm_map_req, continue_epm_map_binding, c);
|
||||
}
|
||||
|
||||
/*
|
||||
Step 6 of RpcConnectDCInfo: Receive endpoint mapping and create secondary
|
||||
rpc connection derived from already used pipe but connected to the requested
|
||||
one (as specified in libnet_RpcConnect structure)
|
||||
*/
|
||||
static void continue_epm_map_binding(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c, *sec_conn_req;
|
||||
struct rpc_connect_dci_state *s;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
c->status = dcerpc_epm_map_binding_recv(ctx);
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
s->r.out.error_string = talloc_asprintf(c,
|
||||
"failed to map pipe with endpoint mapper - %s",
|
||||
nt_errstr(c->status));
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* create secondary connection derived from lsa pipe */
|
||||
sec_conn_req = dcerpc_secondary_connection_send(s->lsa_pipe, s->final_binding);
|
||||
if (composite_nomem(sec_conn_req, c)) return;
|
||||
|
||||
composite_continue(c, sec_conn_req, continue_secondary_conn, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Step 7 of RpcConnectDCInfo: Get actual pipe to be returned
|
||||
and complete this composite call
|
||||
*/
|
||||
static void continue_secondary_conn(struct composite_context *ctx)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct rpc_connect_dci_state *s;
|
||||
|
||||
c = talloc_get_type(ctx->async.private_data, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
|
||||
|
||||
c->status = dcerpc_secondary_connection_recv(ctx, &s->final_pipe);
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
s->r.out.error_string = talloc_asprintf(c,
|
||||
"secondary connection failed: %s",
|
||||
nt_errstr(c->status));
|
||||
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
s->r.out.dcerpc_pipe = s->final_pipe;
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives result of connection to rpc pipe and gets basic
|
||||
* domain info (name, sid, realm, guid)
|
||||
*
|
||||
* @param c composite context
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing return values
|
||||
* @return nt status of rpc connection
|
||||
**/
|
||||
|
||||
static NTSTATUS libnet_RpcConnectDCInfo_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct rpc_connect_dci_state *s = talloc_get_type(c->private_data,
|
||||
struct rpc_connect_dci_state);
|
||||
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
r->out.realm = talloc_steal(mem_ctx, s->r.out.realm);
|
||||
r->out.guid = talloc_steal(mem_ctx, s->r.out.guid);
|
||||
r->out.domain_name = talloc_steal(mem_ctx, s->r.out.domain_name);
|
||||
r->out.domain_sid = talloc_steal(mem_ctx, s->r.out.domain_sid);
|
||||
|
||||
r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
|
||||
|
||||
/* reference created pipe structure to long-term libnet_context
|
||||
so that it can be used by other api functions even after short-term
|
||||
mem_ctx is freed */
|
||||
if (r->in.dcerpc_iface == &dcerpc_table_samr) {
|
||||
ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
|
||||
|
||||
} else if (r->in.dcerpc_iface == &dcerpc_table_lsarpc) {
|
||||
ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (s->r.out.error_string) {
|
||||
r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
|
||||
} else {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx, "Connection to DC failed: %s", nt_errstr(status));
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initiates connection to rpc pipe on remote server or pdc, optionally
|
||||
* providing domain info
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return composite context of this call
|
||||
**/
|
||||
|
||||
struct composite_context* libnet_RpcConnect_send(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
struct composite_context *c;
|
||||
|
||||
switch (r->level) {
|
||||
case LIBNET_RPC_CONNECT_SERVER:
|
||||
case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
|
||||
case LIBNET_RPC_CONNECT_BINDING:
|
||||
c = libnet_RpcConnectSrv_send(ctx, mem_ctx, r);
|
||||
break;
|
||||
|
||||
case LIBNET_RPC_CONNECT_PDC:
|
||||
case LIBNET_RPC_CONNECT_DC:
|
||||
c = libnet_RpcConnectDC_send(ctx, mem_ctx, r);
|
||||
break;
|
||||
|
||||
case LIBNET_RPC_CONNECT_DC_INFO:
|
||||
c = libnet_RpcConnectDCInfo_send(ctx, mem_ctx, r);
|
||||
break;
|
||||
|
||||
default:
|
||||
c = talloc_zero(mem_ctx, struct composite_context);
|
||||
composite_error(c, NT_STATUS_INVALID_LEVEL);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives result of connection to rpc pipe on remote server or pdc
|
||||
*
|
||||
* @param c composite context
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return nt status of rpc connection
|
||||
**/
|
||||
|
||||
NTSTATUS libnet_RpcConnect_recv(struct composite_context *c, struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
|
||||
{
|
||||
switch (r->level) {
|
||||
case LIBNET_RPC_CONNECT_SERVER:
|
||||
case LIBNET_RPC_CONNECT_BINDING:
|
||||
return libnet_RpcConnectSrv_recv(c, ctx, mem_ctx, r);
|
||||
|
||||
case LIBNET_RPC_CONNECT_PDC:
|
||||
case LIBNET_RPC_CONNECT_DC:
|
||||
return libnet_RpcConnectDC_recv(c, ctx, mem_ctx, r);
|
||||
|
||||
case LIBNET_RPC_CONNECT_DC_INFO:
|
||||
return libnet_RpcConnectDCInfo_recv(c, ctx, mem_ctx, r);
|
||||
|
||||
default:
|
||||
ZERO_STRUCT(r->out);
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect to a rpc pipe on a remote server - sync version
|
||||
*
|
||||
* @param ctx initialised libnet context
|
||||
* @param mem_ctx memory context of this call
|
||||
* @param r data structure containing necessary parameters and return values
|
||||
* @return nt status of rpc connection
|
||||
**/
|
||||
|
||||
NTSTATUS libnet_RpcConnect(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_RpcConnect *r)
|
||||
{
|
||||
struct composite_context *c;
|
||||
|
||||
c = libnet_RpcConnect_send(ctx, mem_ctx, r);
|
||||
return libnet_RpcConnect_recv(c, ctx, mem_ctx, r);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 "librpc/rpc/dcerpc.h"
|
||||
|
||||
/*
|
||||
* struct definition for connecting to a dcerpc inferface
|
||||
*/
|
||||
|
||||
enum libnet_RpcConnect_level {
|
||||
LIBNET_RPC_CONNECT_SERVER, /* connect to a standalone rpc server */
|
||||
LIBNET_RPC_CONNECT_SERVER_ADDRESS, /* connect to a standalone rpc server,
|
||||
knowing both name and address */
|
||||
LIBNET_RPC_CONNECT_PDC, /* connect to a domain pdc (resolves domain
|
||||
name to a pdc address before connecting) */
|
||||
LIBNET_RPC_CONNECT_DC, /* connect to any DC (resolves domain
|
||||
name to a DC address before connecting) */
|
||||
LIBNET_RPC_CONNECT_BINDING, /* specified binding string */
|
||||
LIBNET_RPC_CONNECT_DC_INFO /* connect to a DC and provide basic domain
|
||||
information (name, realm, sid, guid) */
|
||||
};
|
||||
|
||||
struct libnet_RpcConnect {
|
||||
enum libnet_RpcConnect_level level;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
const char *address;
|
||||
const char *binding;
|
||||
const struct dcerpc_interface_table *dcerpc_iface;
|
||||
} in;
|
||||
struct {
|
||||
struct dcerpc_pipe *dcerpc_pipe;
|
||||
|
||||
/* parameters provided in LIBNET_RPC_CONNECT_DC_INFO level, null otherwise */
|
||||
const char *domain_name;
|
||||
struct dom_sid *domain_sid;
|
||||
const char *realm; /* these parameters are only present if */
|
||||
struct GUID *guid; /* the remote server is known to be AD */
|
||||
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Monitor messages sent from libnet_rpc.c functions
|
||||
*/
|
||||
|
||||
struct msg_net_lookup_dc {
|
||||
const char *domain_name;
|
||||
const char *hostname;
|
||||
const char *address;
|
||||
};
|
||||
|
||||
|
||||
struct msg_net_pipe_connected {
|
||||
const char *host;
|
||||
const char *endpoint;
|
||||
enum dcerpc_transport_t transport;
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Extract the user/system database from a remote SamSync server
|
||||
|
||||
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 "libnet/libnet.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "samba3/samba3.h"
|
||||
#include "libcli/security/security.h"
|
||||
|
||||
|
||||
struct samdump_secret {
|
||||
struct samdump_secret *prev, *next;
|
||||
DATA_BLOB secret;
|
||||
char *name;
|
||||
NTTIME mtime;
|
||||
};
|
||||
|
||||
struct samdump_trusted_domain {
|
||||
struct samdump_trusted_domain *prev, *next;
|
||||
struct dom_sid *sid;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct samdump_state {
|
||||
struct samdump_secret *secrets;
|
||||
struct samdump_trusted_domain *trusted_domains;
|
||||
};
|
||||
|
||||
static NTSTATUS vampire_samdump_handle_user(TALLOC_CTX *mem_ctx,
|
||||
struct netr_DELTA_ENUM *delta)
|
||||
{
|
||||
uint32_t rid = delta->delta_id_union.rid;
|
||||
struct netr_DELTA_USER *user = delta->delta_union.user;
|
||||
const char *username = user->account_name.string;
|
||||
char *hex_lm_password;
|
||||
char *hex_nt_password;
|
||||
|
||||
hex_lm_password = smbpasswd_sethexpwd(mem_ctx,
|
||||
user->lm_password_present ? &user->lmpassword : NULL,
|
||||
user->acct_flags);
|
||||
hex_nt_password = smbpasswd_sethexpwd(mem_ctx,
|
||||
user->nt_password_present ? &user->ntpassword : NULL,
|
||||
user->acct_flags);
|
||||
|
||||
printf("%s:%d:%s:%s:%s:LCT-%08X\n", username,
|
||||
rid, hex_lm_password, hex_nt_password,
|
||||
smbpasswd_encode_acb_info(mem_ctx, user->acct_flags),
|
||||
(unsigned int)nt_time_to_unix(user->last_password_change));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS vampire_samdump_handle_secret(TALLOC_CTX *mem_ctx,
|
||||
struct samdump_state *samdump_state,
|
||||
struct netr_DELTA_ENUM *delta)
|
||||
{
|
||||
struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
|
||||
const char *name = delta->delta_id_union.name;
|
||||
struct samdump_secret *new = talloc(samdump_state, struct samdump_secret);
|
||||
|
||||
new->name = talloc_strdup(new, name);
|
||||
new->secret = data_blob_talloc(new, secret->current_cipher.cipher_data, secret->current_cipher.maxlen);
|
||||
new->mtime = secret->current_cipher_set_time;
|
||||
|
||||
DLIST_ADD(samdump_state->secrets, new);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS vampire_samdump_handle_trusted_domain(TALLOC_CTX *mem_ctx,
|
||||
struct samdump_state *samdump_state,
|
||||
struct netr_DELTA_ENUM *delta)
|
||||
{
|
||||
struct netr_DELTA_TRUSTED_DOMAIN *trusted_domain = delta->delta_union.trusted_domain;
|
||||
struct dom_sid *dom_sid = delta->delta_id_union.sid;
|
||||
|
||||
struct samdump_trusted_domain *new = talloc(samdump_state, struct samdump_trusted_domain);
|
||||
|
||||
new->name = talloc_strdup(new, trusted_domain->domain_name.string);
|
||||
new->sid = talloc_steal(new, dom_sid);
|
||||
|
||||
DLIST_ADD(samdump_state->trusted_domains, new);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_samdump_fn(TALLOC_CTX *mem_ctx,
|
||||
void *private,
|
||||
enum netr_SamDatabaseID database,
|
||||
struct netr_DELTA_ENUM *delta,
|
||||
char **error_string)
|
||||
{
|
||||
NTSTATUS nt_status = NT_STATUS_OK;
|
||||
struct samdump_state *samdump_state = private;
|
||||
|
||||
*error_string = NULL;
|
||||
switch (delta->delta_type) {
|
||||
case NETR_DELTA_USER:
|
||||
{
|
||||
/* not interested in builtin users */
|
||||
if (database == SAM_DATABASE_DOMAIN) {
|
||||
nt_status = vampire_samdump_handle_user(mem_ctx,
|
||||
delta);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NETR_DELTA_SECRET:
|
||||
{
|
||||
nt_status = vampire_samdump_handle_secret(mem_ctx,
|
||||
samdump_state,
|
||||
delta);
|
||||
break;
|
||||
}
|
||||
case NETR_DELTA_TRUSTED_DOMAIN:
|
||||
{
|
||||
nt_status = vampire_samdump_handle_trusted_domain(mem_ctx,
|
||||
samdump_state,
|
||||
delta);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Can't dump them all right now */
|
||||
break;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_SamDump(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamDump *r)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct libnet_SamSync r2;
|
||||
struct samdump_state *samdump_state = talloc(mem_ctx, struct samdump_state);
|
||||
|
||||
struct samdump_trusted_domain *t;
|
||||
struct samdump_secret *s;
|
||||
|
||||
if (!samdump_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
samdump_state->secrets = NULL;
|
||||
samdump_state->trusted_domains = NULL;
|
||||
|
||||
r2.out.error_string = NULL;
|
||||
r2.in.binding_string = r->in.binding_string;
|
||||
r2.in.init_fn = NULL;
|
||||
r2.in.delta_fn = libnet_samdump_fn;
|
||||
r2.in.fn_ctx = samdump_state;
|
||||
r2.in.machine_account = r->in.machine_account;
|
||||
nt_status = libnet_SamSync_netlogon(ctx, samdump_state, &r2);
|
||||
r->out.error_string = r2.out.error_string;
|
||||
talloc_steal(mem_ctx, r->out.error_string);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(samdump_state);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
printf("Trusted domains, sids and secrets:\n");
|
||||
for (t=samdump_state->trusted_domains; t; t=t->next) {
|
||||
char *secret_name = talloc_asprintf(mem_ctx, "G$$%s", t->name);
|
||||
for (s=samdump_state->secrets; s; s=s->next) {
|
||||
char *secret_string;
|
||||
if (strcasecmp_m(s->name, secret_name) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
|
||||
s->secret.data, s->secret.length,
|
||||
(void **)&secret_string) == -1) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Could not convert secret for domain %s to a string",
|
||||
t->name);
|
||||
talloc_free(samdump_state);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
printf("%s\t%s\t%s\n",
|
||||
t->name, dom_sid_string(mem_ctx, t->sid),
|
||||
secret_string);
|
||||
}
|
||||
}
|
||||
talloc_free(samdump_state);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Extract kerberos keys from a remote SamSync server
|
||||
|
||||
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 "libnet/libnet.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
static NTSTATUS samdump_keytab_handle_user(TALLOC_CTX *mem_ctx,
|
||||
const char *keytab_name,
|
||||
struct netr_DELTA_ENUM *delta)
|
||||
{
|
||||
struct netr_DELTA_USER *user = delta->delta_union.user;
|
||||
const char *username = user->account_name.string;
|
||||
struct cli_credentials *credentials;
|
||||
int ret;
|
||||
|
||||
if (!user->nt_password_present) {
|
||||
/* We can't do anything here */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
credentials = cli_credentials_init(mem_ctx);
|
||||
if (!credentials) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cli_credentials_set_conf(credentials);
|
||||
cli_credentials_set_username(credentials, username, CRED_SPECIFIED);
|
||||
|
||||
/* We really should consult ldap in the main SamSync code, and
|
||||
* pass a value in here */
|
||||
cli_credentials_set_kvno(credentials, 0);
|
||||
cli_credentials_set_nt_hash(credentials, &user->ntpassword, CRED_SPECIFIED);
|
||||
ret = cli_credentials_set_keytab_name(credentials, keytab_name, CRED_SPECIFIED);
|
||||
if (ret) {
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_update_keytab(credentials);
|
||||
if (ret) {
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_samdump_keytab_fn(TALLOC_CTX *mem_ctx,
|
||||
void *private,
|
||||
enum netr_SamDatabaseID database,
|
||||
struct netr_DELTA_ENUM *delta,
|
||||
char **error_string)
|
||||
{
|
||||
NTSTATUS nt_status = NT_STATUS_OK;
|
||||
const char *keytab_name = private;
|
||||
|
||||
*error_string = NULL;
|
||||
switch (delta->delta_type) {
|
||||
case NETR_DELTA_USER:
|
||||
{
|
||||
/* not interested in builtin users */
|
||||
if (database == SAM_DATABASE_DOMAIN) {
|
||||
nt_status = samdump_keytab_handle_user(mem_ctx,
|
||||
keytab_name,
|
||||
delta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
/* Can't dump them all right now */
|
||||
break;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_SamDump_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamDump_keytab *r)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct libnet_SamSync r2;
|
||||
|
||||
r2.out.error_string = NULL;
|
||||
r2.in.binding_string = r->in.binding_string;
|
||||
r2.in.init_fn = NULL;
|
||||
r2.in.delta_fn = libnet_samdump_keytab_fn;
|
||||
r2.in.fn_ctx = discard_const(r->in.keytab_name);
|
||||
r2.in.machine_account = r->in.machine_account;
|
||||
nt_status = libnet_SamSync_netlogon(ctx, mem_ctx, &r2);
|
||||
r->out.error_string = r2.out.error_string;
|
||||
talloc_steal(mem_ctx, r->out.error_string);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Grégory LEOCADIE <gleocadie@idealx.com>
|
||||
|
||||
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 "libnet/libnet.h"
|
||||
#include "librpc/gen_ndr/ndr_srvsvc_c.h"
|
||||
|
||||
|
||||
NTSTATUS libnet_ListShares(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_ListShares *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct libnet_RpcConnect c;
|
||||
struct srvsvc_NetShareEnumAll s;
|
||||
uint32_t resume_handle = 0;
|
||||
struct srvsvc_NetShareCtr0 ctr0;
|
||||
struct srvsvc_NetShareCtr1 ctr1;
|
||||
struct srvsvc_NetShareCtr2 ctr2;
|
||||
struct srvsvc_NetShareCtr501 ctr501;
|
||||
struct srvsvc_NetShareCtr502 ctr502;
|
||||
|
||||
c.level = LIBNET_RPC_CONNECT_SERVER;
|
||||
c.in.name = r->in.server_name;
|
||||
c.in.dcerpc_iface = &dcerpc_table_srvsvc;
|
||||
|
||||
s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", c.in.name);
|
||||
|
||||
status = libnet_RpcConnect(ctx, mem_ctx, &c);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to SRVSVC pipe of server %s "
|
||||
"failed: %s",
|
||||
r->in.server_name,
|
||||
nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
s.in.level = r->in.level;
|
||||
switch (s.in.level) {
|
||||
case 0:
|
||||
s.in.ctr.ctr0 = &ctr0;
|
||||
ZERO_STRUCT(ctr0);
|
||||
break;
|
||||
case 1:
|
||||
s.in.ctr.ctr1 = &ctr1;
|
||||
ZERO_STRUCT(ctr1);
|
||||
break;
|
||||
case 2:
|
||||
s.in.ctr.ctr2 = &ctr2;
|
||||
ZERO_STRUCT(ctr2);
|
||||
break;
|
||||
case 501:
|
||||
s.in.ctr.ctr501 = &ctr501;
|
||||
ZERO_STRUCT(ctr501);
|
||||
break;
|
||||
case 502:
|
||||
s.in.ctr.ctr502 = &ctr502;
|
||||
ZERO_STRUCT(ctr502);
|
||||
break;
|
||||
default:
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"libnet_ListShares: Invalid info level requested: %d",
|
||||
s.in.level);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
s.in.max_buffer = ~0;
|
||||
s.in.resume_handle = &resume_handle;
|
||||
|
||||
|
||||
status = dcerpc_srvsvc_NetShareEnumAll(c.out.dcerpc_pipe, mem_ctx, &s);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetShareEnumAll on server '%s' failed"
|
||||
": %s",
|
||||
r->in.server_name, nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
if (!W_ERROR_IS_OK(s.out.result) && !W_ERROR_EQUAL(s.out.result, WERR_MORE_DATA)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetShareEnumAll on server '%s' failed: %s",
|
||||
r->in.server_name, win_errstr(s.out.result));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
r->out.ctr = s.out.ctr;
|
||||
|
||||
disconnect:
|
||||
talloc_free(c.out.dcerpc_pipe);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_AddShare(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_AddShare *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct libnet_RpcConnect c;
|
||||
struct srvsvc_NetShareAdd s;
|
||||
|
||||
c.level = LIBNET_RPC_CONNECT_SERVER;
|
||||
c.in.name = r->in.server_name;
|
||||
c.in.dcerpc_iface = &dcerpc_table_srvsvc;
|
||||
|
||||
status = libnet_RpcConnect(ctx, mem_ctx, &c);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to SRVSVC pipe of server %s "
|
||||
"failed: %s",
|
||||
r->in.server_name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
s.in.level = 2;
|
||||
s.in.info.info2 = &r->in.share;
|
||||
s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", r->in.server_name);
|
||||
|
||||
status = dcerpc_srvsvc_NetShareAdd(c.out.dcerpc_pipe, mem_ctx, &s);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetShareAdd '%s' on server '%s' failed"
|
||||
": %s",
|
||||
r->in.share.name, r->in.server_name,
|
||||
nt_errstr(status));
|
||||
} else if (!W_ERROR_IS_OK(s.out.result)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetShareAdd '%s' on server '%s' failed"
|
||||
": %s",
|
||||
r->in.share.name, r->in.server_name,
|
||||
win_errstr(s.out.result));
|
||||
status = werror_to_ntstatus(s.out.result);
|
||||
}
|
||||
|
||||
talloc_free(c.out.dcerpc_pipe);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS libnet_DelShare(struct libnet_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, struct libnet_DelShare *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct libnet_RpcConnect c;
|
||||
struct srvsvc_NetShareDel s;
|
||||
|
||||
c.level = LIBNET_RPC_CONNECT_SERVER;
|
||||
c.in.name = r->in.server_name;
|
||||
c.in.dcerpc_iface = &dcerpc_table_srvsvc;
|
||||
|
||||
status = libnet_RpcConnect(ctx, mem_ctx, &c);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to SRVSVC pipe of server %s "
|
||||
"failed: %s",
|
||||
r->in.server_name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", r->in.server_name);
|
||||
s.in.share_name = r->in.share_name;
|
||||
|
||||
status = dcerpc_srvsvc_NetShareDel(c.out.dcerpc_pipe, mem_ctx, &s);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetShareDel '%s' on server '%s' failed"
|
||||
": %s",
|
||||
r->in.share_name, r->in.server_name,
|
||||
nt_errstr(status));
|
||||
} else if (!W_ERROR_IS_OK(s.out.result)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetShareDel '%s' on server '%s' failed"
|
||||
": %s",
|
||||
r->in.share_name, r->in.server_name,
|
||||
win_errstr(s.out.result));
|
||||
status = werror_to_ntstatus(s.out.result);
|
||||
}
|
||||
|
||||
talloc_free(c.out.dcerpc_pipe);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Grégory LEOCADIE <gleocadie@idealx.com>
|
||||
|
||||
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 "librpc/gen_ndr/srvsvc.h"
|
||||
|
||||
enum libnet_ListShares_level {
|
||||
LIBNET_LIST_SHARES_GENERIC,
|
||||
LIBNET_LIST_SHARES_SRVSVC
|
||||
};
|
||||
|
||||
struct libnet_ListShares {
|
||||
struct {
|
||||
const char *server_name;
|
||||
uint32_t *resume_handle;
|
||||
uint32_t level;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
union srvsvc_NetShareCtr ctr;
|
||||
uint32_t *resume_handle;
|
||||
} out;
|
||||
};
|
||||
|
||||
enum libnet_AddShare_level {
|
||||
LIBNET_ADD_SHARE_GENERIC,
|
||||
LIBNET_ADD_SHARE_SRVSVC
|
||||
};
|
||||
|
||||
struct libnet_AddShare {
|
||||
enum libnet_AddShare_level level;
|
||||
struct {
|
||||
const char * server_name;
|
||||
struct srvsvc_NetShareInfo2 share;
|
||||
} in;
|
||||
struct {
|
||||
const char* error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
enum libnet_DelShare_level {
|
||||
LIBNET_DEL_SHARE_GENERIC,
|
||||
LIBNET_DEL_SHARE_SRVSVC
|
||||
};
|
||||
|
||||
struct libnet_DelShare {
|
||||
enum libnet_DelShare_level level;
|
||||
struct {
|
||||
const char *server_name;
|
||||
const char *share_name;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Brad Henry 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 "libnet/libnet.h"
|
||||
#include "libcli/cldap/cldap.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
|
||||
/*
|
||||
* 1. Setup a CLDAP socket.
|
||||
* 2. Lookup the default Site-Name.
|
||||
*/
|
||||
NTSTATUS libnet_FindSite(TALLOC_CTX *ctx, struct libnet_JoinSite *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
char *site_name_str;
|
||||
char *config_dn_str;
|
||||
char *server_dn_str;
|
||||
|
||||
struct cldap_socket *cldap = NULL;
|
||||
struct cldap_netlogon search;
|
||||
|
||||
tmp_ctx = talloc_named(ctx, 0, "libnet_FindSite temp context");
|
||||
if (!tmp_ctx) {
|
||||
r->out.error_string = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Resolve the site name. */
|
||||
ZERO_STRUCT(search);
|
||||
search.in.dest_address = r->in.dest_address;
|
||||
search.in.acct_control = -1;
|
||||
search.in.version = 6;
|
||||
|
||||
cldap = cldap_socket_init(tmp_ctx, NULL);
|
||||
status = cldap_netlogon(cldap, tmp_ctx, &search);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/*
|
||||
If cldap_netlogon() returns in error,
|
||||
default to using Default-First-Site-Name.
|
||||
*/
|
||||
site_name_str = talloc_asprintf(tmp_ctx, "%s",
|
||||
"Default-First-Site-Name");
|
||||
if (!site_name_str) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
} else {
|
||||
site_name_str = talloc_asprintf(tmp_ctx, "%s",
|
||||
search.out.netlogon.logon5.client_site);
|
||||
if (!site_name_str) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate the CN=Configuration,... DN. */
|
||||
/* TODO: look it up! */
|
||||
config_dn_str = talloc_asprintf(tmp_ctx, "CN=Configuration,%s", r->in.domain_dn_str);
|
||||
if (!config_dn_str) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Generate the CN=Servers,... DN. */
|
||||
server_dn_str = talloc_asprintf(tmp_ctx, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
|
||||
r->in.netbios_name, site_name_str, config_dn_str);
|
||||
if (!server_dn_str) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
r->out.site_name_str = site_name_str;
|
||||
talloc_steal(r, site_name_str);
|
||||
|
||||
r->out.config_dn_str = config_dn_str;
|
||||
talloc_steal(r, config_dn_str);
|
||||
|
||||
r->out.server_dn_str = server_dn_str;
|
||||
talloc_steal(r, server_dn_str);
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* find out Site specific stuff:
|
||||
* 1. Lookup the Site name.
|
||||
* 2. Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
|
||||
* TODO: 3.) use DsAddEntry() to create CN=NTDS Settings,CN=<netbios name>,CN=Servers,CN=<site name>,...
|
||||
*/
|
||||
NTSTATUS libnet_JoinSite(struct ldb_context *remote_ldb,
|
||||
struct libnet_JoinDomain *libnet_r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
struct libnet_JoinSite *r;
|
||||
|
||||
struct ldb_dn *server_dn;
|
||||
struct ldb_message *msg;
|
||||
int rtn;
|
||||
|
||||
const char *server_dn_str;
|
||||
const char *config_dn_str;
|
||||
|
||||
tmp_ctx = talloc_named(libnet_r, 0, "libnet_JoinSite temp context");
|
||||
if (!tmp_ctx) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
r = talloc(tmp_ctx, struct libnet_JoinSite);
|
||||
if (!r) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Resolve the site name and AD DN's. */
|
||||
r->in.dest_address = libnet_r->out.samr_binding->host;
|
||||
r->in.netbios_name = libnet_r->in.netbios_name;
|
||||
r->in.domain_dn_str = libnet_r->out.domain_dn_str;
|
||||
|
||||
status = libnet_FindSite(tmp_ctx, r);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
libnet_r->out.error_string =
|
||||
talloc_steal(libnet_r, r->out.error_string);
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
config_dn_str = r->out.config_dn_str;
|
||||
server_dn_str = r->out.server_dn_str;
|
||||
|
||||
/*
|
||||
Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
|
||||
*/
|
||||
msg = ldb_msg_new(tmp_ctx);
|
||||
if (!msg) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
rtn = ldb_msg_add_string(msg, "objectClass", "server");
|
||||
if (rtn != 0) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
rtn = ldb_msg_add_string(msg, "systemFlags", "50000000");
|
||||
if (rtn != 0) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
rtn = ldb_msg_add_string(msg, "serverReference", libnet_r->out.account_dn_str);
|
||||
if (rtn != 0) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
server_dn = ldb_dn_new(tmp_ctx, remote_ldb, server_dn_str);
|
||||
if ( ! ldb_dn_validate(server_dn)) {
|
||||
libnet_r->out.error_string = talloc_asprintf(libnet_r,
|
||||
"Invalid server dn: %s",
|
||||
server_dn_str);
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
msg->dn = server_dn;
|
||||
|
||||
rtn = ldb_add(remote_ldb, msg);
|
||||
if (rtn == LDB_ERR_ENTRY_ALREADY_EXISTS) {
|
||||
int i;
|
||||
|
||||
/* make a 'modify' msg, and only for serverReference */
|
||||
msg = ldb_msg_new(tmp_ctx);
|
||||
if (!msg) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
msg->dn = server_dn;
|
||||
|
||||
rtn = ldb_msg_add_string(msg, "serverReference",libnet_r->out.account_dn_str);
|
||||
if (rtn != 0) {
|
||||
libnet_r->out.error_string = NULL;
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* mark all the message elements (should be just one)
|
||||
as LDB_FLAG_MOD_REPLACE */
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
|
||||
}
|
||||
|
||||
rtn = ldb_modify(remote_ldb, msg);
|
||||
if (rtn != 0) {
|
||||
libnet_r->out.error_string
|
||||
= talloc_asprintf(libnet_r,
|
||||
"Failed to modify server entry %s: %s: %d",
|
||||
server_dn_str,
|
||||
ldb_errstring(remote_ldb), rtn);
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
} else if (rtn != 0) {
|
||||
libnet_r->out.error_string
|
||||
= talloc_asprintf(libnet_r,
|
||||
"Failed to add server entry %s: %s: %d",
|
||||
server_dn_str, ldb_errstring(remote_ldb),
|
||||
rtn);
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
DEBUG(0, ("We still need to perform a DsAddEntry() so that we can create the CN=NTDS Settings container.\n"));
|
||||
|
||||
/* Store the server DN in libnet_r */
|
||||
libnet_r->out.server_dn_str = server_dn_str;
|
||||
talloc_steal(libnet_r, server_dn_str);
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Brad Henry 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.
|
||||
*/
|
||||
|
||||
struct libnet_JoinSite {
|
||||
struct {
|
||||
const char *dest_address;
|
||||
const char *netbios_name;
|
||||
const char *domain_dn_str;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
const char *error_string;
|
||||
const char *site_name_str;
|
||||
const char *config_dn_str;
|
||||
const char *server_dn_str;
|
||||
} out;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 "libnet/libnet.h"
|
||||
#include "system/time.h"
|
||||
#include "librpc/gen_ndr/ndr_srvsvc_c.h"
|
||||
|
||||
/*
|
||||
* get the remote time of a server via srvsvc_NetRemoteTOD
|
||||
*/
|
||||
static NTSTATUS libnet_RemoteTOD_srvsvc(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct libnet_RpcConnect c;
|
||||
struct srvsvc_NetRemoteTOD tod;
|
||||
struct tm tm;
|
||||
|
||||
/* prepare connect to the SRVSVC pipe of a timeserver */
|
||||
c.level = LIBNET_RPC_CONNECT_SERVER;
|
||||
c.in.name = r->srvsvc.in.server_name;
|
||||
c.in.dcerpc_iface = &dcerpc_table_srvsvc;
|
||||
|
||||
/* 1. connect to the SRVSVC pipe of a timeserver */
|
||||
status = libnet_RpcConnect(ctx, mem_ctx, &c);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to SRVSVC pipe of server '%s' failed: %s",
|
||||
r->srvsvc.in.server_name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* prepare srvsvc_NetrRemoteTOD */
|
||||
tod.in.server_unc = talloc_asprintf(mem_ctx, "\\%s", c.in.name);
|
||||
|
||||
/* 2. try srvsvc_NetRemoteTOD */
|
||||
status = dcerpc_srvsvc_NetRemoteTOD(c.out.dcerpc_pipe, mem_ctx, &tod);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetrRemoteTOD on server '%s' failed: %s",
|
||||
r->srvsvc.in.server_name, nt_errstr(status));
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* check result of srvsvc_NetrRemoteTOD */
|
||||
if (!W_ERROR_IS_OK(tod.out.result)) {
|
||||
r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
|
||||
"srvsvc_NetrRemoteTOD on server '%s' failed: %s",
|
||||
r->srvsvc.in.server_name, win_errstr(tod.out.result));
|
||||
status = werror_to_ntstatus(tod.out.result);
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* need to set the out parameters */
|
||||
tm.tm_sec = (int)tod.out.info->secs;
|
||||
tm.tm_min = (int)tod.out.info->mins;
|
||||
tm.tm_hour = (int)tod.out.info->hours;
|
||||
tm.tm_mday = (int)tod.out.info->day;
|
||||
tm.tm_mon = (int)tod.out.info->month -1;
|
||||
tm.tm_year = (int)tod.out.info->year - 1900;
|
||||
tm.tm_wday = -1;
|
||||
tm.tm_yday = -1;
|
||||
tm.tm_isdst = -1;
|
||||
|
||||
r->srvsvc.out.time = timegm(&tm);
|
||||
r->srvsvc.out.time_zone = tod.out.info->timezone * 60;
|
||||
|
||||
goto disconnect;
|
||||
|
||||
disconnect:
|
||||
/* close connection */
|
||||
talloc_free(c.out.dcerpc_pipe);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS libnet_RemoteTOD_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
union libnet_RemoteTOD r2;
|
||||
|
||||
r2.srvsvc.level = LIBNET_REMOTE_TOD_SRVSVC;
|
||||
r2.srvsvc.in.server_name = r->generic.in.server_name;
|
||||
|
||||
status = libnet_RemoteTOD(ctx, mem_ctx, &r2);
|
||||
|
||||
r->generic.out.time = r2.srvsvc.out.time;
|
||||
r->generic.out.time_zone = r2.srvsvc.out.time_zone;
|
||||
|
||||
r->generic.out.error_string = r2.srvsvc.out.error_string;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_RemoteTOD(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
|
||||
{
|
||||
switch (r->generic.level) {
|
||||
case LIBNET_REMOTE_TOD_GENERIC:
|
||||
return libnet_RemoteTOD_generic(ctx, mem_ctx, r);
|
||||
case LIBNET_REMOTE_TOD_SRVSVC:
|
||||
return libnet_RemoteTOD_srvsvc(ctx, mem_ctx, r);
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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.
|
||||
*/
|
||||
|
||||
/* struct and enum for getting the time of a remote system */
|
||||
enum libnet_RemoteTOD_level {
|
||||
LIBNET_REMOTE_TOD_GENERIC,
|
||||
LIBNET_REMOTE_TOD_SRVSVC
|
||||
};
|
||||
|
||||
union libnet_RemoteTOD {
|
||||
struct {
|
||||
enum libnet_RemoteTOD_level level;
|
||||
|
||||
struct _libnet_RemoteTOD_in {
|
||||
const char *server_name;
|
||||
} in;
|
||||
|
||||
struct _libnet_RemoteTOD_out {
|
||||
time_t time;
|
||||
int time_zone;
|
||||
const char *error_string;
|
||||
} out;
|
||||
} generic;
|
||||
|
||||
struct {
|
||||
enum libnet_RemoteTOD_level level;
|
||||
struct _libnet_RemoteTOD_in in;
|
||||
struct _libnet_RemoteTOD_out out;
|
||||
} srvsvc;
|
||||
};
|
||||
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 "libnet/libnet.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libcli/cldap/cldap.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "lib/db_wrap.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "dsdb/common/flags.h"
|
||||
#include "librpc/gen_ndr/ndr_drsuapi_c.h"
|
||||
|
||||
struct libnet_UnbecomeDC_state {
|
||||
struct composite_context *creq;
|
||||
|
||||
struct libnet_context *libnet;
|
||||
|
||||
struct {
|
||||
struct cldap_socket *sock;
|
||||
struct cldap_netlogon io;
|
||||
struct nbt_cldap_netlogon_5 netlogon5;
|
||||
} cldap;
|
||||
|
||||
struct {
|
||||
struct ldb_context *ldb;
|
||||
} ldap;
|
||||
|
||||
struct {
|
||||
struct dcerpc_binding *binding;
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct drsuapi_DsBind bind_r;
|
||||
struct GUID bind_guid;
|
||||
struct drsuapi_DsBindInfoCtr bind_info_ctr;
|
||||
struct drsuapi_DsBindInfo28 local_info28;
|
||||
struct drsuapi_DsBindInfo28 remote_info28;
|
||||
struct policy_handle bind_handle;
|
||||
struct drsuapi_DsRemoveDSServer rm_ds_srv_r;
|
||||
} drsuapi;
|
||||
|
||||
struct {
|
||||
/* input */
|
||||
const char *dns_name;
|
||||
const char *netbios_name;
|
||||
|
||||
/* constructed */
|
||||
struct GUID guid;
|
||||
const char *dn_str;
|
||||
} domain;
|
||||
|
||||
struct {
|
||||
/* constructed */
|
||||
const char *config_dn_str;
|
||||
} forest;
|
||||
|
||||
struct {
|
||||
/* input */
|
||||
const char *address;
|
||||
|
||||
/* constructed */
|
||||
const char *dns_name;
|
||||
const char *netbios_name;
|
||||
const char *site_name;
|
||||
} source_dsa;
|
||||
|
||||
struct {
|
||||
/* input */
|
||||
const char *netbios_name;
|
||||
|
||||
/* constructed */
|
||||
const char *dns_name;
|
||||
const char *site_name;
|
||||
const char *computer_dn_str;
|
||||
const char *server_dn_str;
|
||||
uint32_t user_account_control;
|
||||
} dest_dsa;
|
||||
};
|
||||
|
||||
static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s);
|
||||
|
||||
static void unbecomeDC_recv_cldap(struct cldap_request *req)
|
||||
{
|
||||
struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
|
||||
struct libnet_UnbecomeDC_state);
|
||||
struct composite_context *c = s->creq;
|
||||
|
||||
c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
s->cldap.netlogon5 = s->cldap.io.out.netlogon.logon5;
|
||||
|
||||
s->domain.dns_name = s->cldap.netlogon5.dns_domain;
|
||||
s->domain.netbios_name = s->cldap.netlogon5.domain;
|
||||
s->domain.guid = s->cldap.netlogon5.domain_uuid;
|
||||
|
||||
s->source_dsa.dns_name = s->cldap.netlogon5.pdc_dns_name;
|
||||
s->source_dsa.netbios_name = s->cldap.netlogon5.pdc_name;
|
||||
s->source_dsa.site_name = s->cldap.netlogon5.server_site;
|
||||
|
||||
s->dest_dsa.site_name = s->cldap.netlogon5.client_site;
|
||||
|
||||
unbecomeDC_connect_ldap(s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_send_cldap(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
struct composite_context *c = s->creq;
|
||||
struct cldap_request *req;
|
||||
|
||||
s->cldap.io.in.dest_address = s->source_dsa.address;
|
||||
s->cldap.io.in.realm = s->domain.dns_name;
|
||||
s->cldap.io.in.host = s->dest_dsa.netbios_name;
|
||||
s->cldap.io.in.user = NULL;
|
||||
s->cldap.io.in.domain_guid = NULL;
|
||||
s->cldap.io.in.domain_sid = NULL;
|
||||
s->cldap.io.in.acct_control = -1;
|
||||
s->cldap.io.in.version = 6;
|
||||
|
||||
s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx);
|
||||
if (composite_nomem(s->cldap.sock, c)) return;
|
||||
|
||||
req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
|
||||
if (composite_nomem(req, c)) return;
|
||||
req->async.fn = unbecomeDC_recv_cldap;
|
||||
req->async.private = s;
|
||||
}
|
||||
|
||||
static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
char *url;
|
||||
|
||||
url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(url);
|
||||
|
||||
s->ldap.ldb = ldb_wrap_connect(s, url,
|
||||
NULL,
|
||||
s->libnet->cred,
|
||||
0, NULL);
|
||||
talloc_free(url);
|
||||
if (s->ldap.ldb == NULL) {
|
||||
return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unbecomeDC_ldap_rootdse(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_result *r;
|
||||
struct ldb_dn *basedn;
|
||||
static const char *attrs[] = {
|
||||
"defaultNamingContext",
|
||||
"configurationNamingContext",
|
||||
NULL
|
||||
};
|
||||
|
||||
basedn = ldb_dn_new(s, s->ldap.ldb, NULL);
|
||||
NT_STATUS_HAVE_NO_MEMORY(basedn);
|
||||
|
||||
ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE,
|
||||
"(objectClass=*)", attrs, &r);
|
||||
talloc_free(basedn);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return NT_STATUS_LDAP(ret);
|
||||
} else if (r->count != 1) {
|
||||
talloc_free(r);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
talloc_steal(s, r);
|
||||
|
||||
s->domain.dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "defaultNamingContext", NULL);
|
||||
if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
talloc_steal(s, s->domain.dn_str);
|
||||
|
||||
s->forest.config_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "configurationNamingContext", NULL);
|
||||
if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
talloc_steal(s, s->forest.config_dn_str);
|
||||
|
||||
s->dest_dsa.server_dn_str = talloc_asprintf(s, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
|
||||
s->dest_dsa.netbios_name,
|
||||
s->dest_dsa.site_name,
|
||||
s->forest.config_dn_str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.server_dn_str);
|
||||
|
||||
talloc_free(r);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unbecomeDC_ldap_computer_object(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_result *r;
|
||||
struct ldb_dn *basedn;
|
||||
char *filter;
|
||||
static const char *attrs[] = {
|
||||
"distinguishedName",
|
||||
"userAccountControl",
|
||||
NULL
|
||||
};
|
||||
|
||||
basedn = ldb_dn_new(s, s->ldap.ldb, s->domain.dn_str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(basedn);
|
||||
|
||||
filter = talloc_asprintf(basedn, "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
|
||||
s->dest_dsa.netbios_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(filter);
|
||||
|
||||
ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_SUBTREE,
|
||||
filter, attrs, &r);
|
||||
talloc_free(basedn);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return NT_STATUS_LDAP(ret);
|
||||
} else if (r->count != 1) {
|
||||
talloc_free(r);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
|
||||
s->dest_dsa.computer_dn_str = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
|
||||
if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
talloc_steal(s, s->dest_dsa.computer_dn_str);
|
||||
|
||||
s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);
|
||||
|
||||
talloc_free(r);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unbecomeDC_ldap_modify_computer(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_message *msg;
|
||||
uint32_t user_account_control = UF_WORKSTATION_TRUST_ACCOUNT;
|
||||
uint32_t i;
|
||||
|
||||
/* as the value is already as we want it to be, we're done */
|
||||
if (s->dest_dsa.user_account_control == user_account_control) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* make a 'modify' msg, and only for serverReference */
|
||||
msg = ldb_msg_new(s);
|
||||
NT_STATUS_HAVE_NO_MEMORY(msg);
|
||||
msg->dn = ldb_dn_new(msg, s->ldap.ldb, s->dest_dsa.computer_dn_str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(msg->dn);
|
||||
|
||||
ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
|
||||
if (ret != 0) {
|
||||
talloc_free(msg);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* mark all the message elements (should be just one)
|
||||
as LDB_FLAG_MOD_REPLACE */
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
|
||||
}
|
||||
|
||||
ret = ldb_modify(s->ldap.ldb, msg);
|
||||
talloc_free(msg);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return NT_STATUS_LDAP(ret);
|
||||
}
|
||||
|
||||
s->dest_dsa.user_account_control = user_account_control;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unbecomeDC_ldap_move_computer(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_result *r;
|
||||
struct ldb_dn *basedn;
|
||||
struct ldb_dn *old_dn;
|
||||
struct ldb_dn *new_dn;
|
||||
static const char *_1_1_attrs[] = {
|
||||
"1.1",
|
||||
NULL
|
||||
};
|
||||
|
||||
basedn = ldb_dn_new_fmt(s, s->ldap.ldb, "<WKGUID=aa312825768811d1aded00c04fd8d5cd,%s>",
|
||||
s->domain.dn_str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(basedn);
|
||||
|
||||
ret = ldb_search(s->ldap.ldb, basedn, LDB_SCOPE_BASE,
|
||||
"(objectClass=*)", _1_1_attrs, &r);
|
||||
talloc_free(basedn);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return NT_STATUS_LDAP(ret);
|
||||
} else if (r->count != 1) {
|
||||
talloc_free(r);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
|
||||
old_dn = ldb_dn_new(r, s->ldap.ldb, s->dest_dsa.computer_dn_str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(old_dn);
|
||||
|
||||
new_dn = r->msgs[0]->dn;
|
||||
|
||||
if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
|
||||
talloc_free(r);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (ldb_dn_compare(old_dn, new_dn) == 0) {
|
||||
/* we don't need to rename if the old and new dn match */
|
||||
talloc_free(r);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
ret = ldb_rename(s->ldap.ldb, old_dn, new_dn);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
talloc_free(r);
|
||||
return NT_STATUS_LDAP(ret);
|
||||
}
|
||||
|
||||
s->dest_dsa.computer_dn_str = ldb_dn_alloc_linearized(s, new_dn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.computer_dn_str);
|
||||
|
||||
talloc_free(r);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s);
|
||||
|
||||
static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
struct composite_context *c = s->creq;
|
||||
|
||||
c->status = unbecomeDC_ldap_connect(s);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
c->status = unbecomeDC_ldap_rootdse(s);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
c->status = unbecomeDC_ldap_computer_object(s);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
c->status = unbecomeDC_ldap_modify_computer(s);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
c->status = unbecomeDC_ldap_move_computer(s);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
unbecomeDC_drsuapi_connect_send(s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_connect_recv(struct composite_context *creq);
|
||||
|
||||
static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
struct composite_context *c = s->creq;
|
||||
struct composite_context *creq;
|
||||
char *binding_str;
|
||||
|
||||
binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[seal]", s->source_dsa.dns_name);
|
||||
if (composite_nomem(binding_str, c)) return;
|
||||
|
||||
c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi.binding);
|
||||
talloc_free(binding_str);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
creq = dcerpc_pipe_connect_b_send(s, s->drsuapi.binding, &dcerpc_table_drsuapi,
|
||||
s->libnet->cred, s->libnet->event_ctx);
|
||||
composite_continue(c, creq, unbecomeDC_drsuapi_connect_recv, s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s);
|
||||
|
||||
static void unbecomeDC_drsuapi_connect_recv(struct composite_context *req)
|
||||
{
|
||||
struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
|
||||
struct libnet_UnbecomeDC_state);
|
||||
struct composite_context *c = s->creq;
|
||||
|
||||
c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi.pipe);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
unbecomeDC_drsuapi_bind_send(s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req);
|
||||
|
||||
static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
struct composite_context *c = s->creq;
|
||||
struct rpc_request *req;
|
||||
struct drsuapi_DsBindInfo28 *bind_info28;
|
||||
|
||||
GUID_from_string(DRSUAPI_DS_BIND_GUID, &s->drsuapi.bind_guid);
|
||||
|
||||
bind_info28 = &s->drsuapi.local_info28;
|
||||
bind_info28->supported_extensions = 0;
|
||||
bind_info28->site_guid = GUID_zero();
|
||||
bind_info28->u1 = 508;
|
||||
bind_info28->repl_epoch = 0;
|
||||
|
||||
s->drsuapi.bind_info_ctr.length = 28;
|
||||
s->drsuapi.bind_info_ctr.info.info28 = *bind_info28;
|
||||
|
||||
s->drsuapi.bind_r.in.bind_guid = &s->drsuapi.bind_guid;
|
||||
s->drsuapi.bind_r.in.bind_info = &s->drsuapi.bind_info_ctr;
|
||||
s->drsuapi.bind_r.out.bind_handle = &s->drsuapi.bind_handle;
|
||||
|
||||
req = dcerpc_drsuapi_DsBind_send(s->drsuapi.pipe, s, &s->drsuapi.bind_r);
|
||||
composite_continue_rpc(c, req, unbecomeDC_drsuapi_bind_recv, s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s);
|
||||
|
||||
static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req)
|
||||
{
|
||||
struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
|
||||
struct libnet_UnbecomeDC_state);
|
||||
struct composite_context *c = s->creq;
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
if (!W_ERROR_IS_OK(s->drsuapi.bind_r.out.result)) {
|
||||
composite_error(c, werror_to_ntstatus(s->drsuapi.bind_r.out.result));
|
||||
return;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(s->drsuapi.remote_info28);
|
||||
if (s->drsuapi.bind_r.out.bind_info) {
|
||||
switch (s->drsuapi.bind_r.out.bind_info->length) {
|
||||
case 24: {
|
||||
struct drsuapi_DsBindInfo24 *info24;
|
||||
info24 = &s->drsuapi.bind_r.out.bind_info->info.info24;
|
||||
s->drsuapi.remote_info28.supported_extensions = info24->supported_extensions;
|
||||
s->drsuapi.remote_info28.site_guid = info24->site_guid;
|
||||
s->drsuapi.remote_info28.u1 = info24->u1;
|
||||
s->drsuapi.remote_info28.repl_epoch = 0;
|
||||
break;
|
||||
}
|
||||
case 28:
|
||||
s->drsuapi.remote_info28 = s->drsuapi.bind_r.out.bind_info->info.info28;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unbecomeDC_drsuapi_remove_ds_server_send(s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_remove_ds_server_recv(struct rpc_request *req);
|
||||
|
||||
static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s)
|
||||
{
|
||||
struct composite_context *c = s->creq;
|
||||
struct rpc_request *req;
|
||||
struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
|
||||
|
||||
r->in.bind_handle = &s->drsuapi.bind_handle;
|
||||
r->in.level = 1;
|
||||
r->in.req.req1.server_dn= s->dest_dsa.server_dn_str;
|
||||
r->in.req.req1.domain_dn= s->domain.dn_str;
|
||||
r->in.req.req1.unknown = 0x00000001;
|
||||
|
||||
req = dcerpc_drsuapi_DsRemoveDSServer_send(s->drsuapi.pipe, s, r);
|
||||
composite_continue_rpc(c, req, unbecomeDC_drsuapi_remove_ds_server_recv, s);
|
||||
}
|
||||
|
||||
static void unbecomeDC_drsuapi_remove_ds_server_recv(struct rpc_request *req)
|
||||
{
|
||||
struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private,
|
||||
struct libnet_UnbecomeDC_state);
|
||||
struct composite_context *c = s->creq;
|
||||
struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
|
||||
|
||||
c->status = dcerpc_ndr_request_recv(req);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
if (!W_ERROR_IS_OK(r->out.result)) {
|
||||
composite_error(c, werror_to_ntstatus(r->out.result));
|
||||
return;
|
||||
}
|
||||
|
||||
if (r->out.level != 1) {
|
||||
composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!W_ERROR_IS_OK(r->out.res.res1.status)) {
|
||||
composite_error(c, werror_to_ntstatus(r->out.res.res1.status));
|
||||
return;
|
||||
}
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
struct composite_context *libnet_UnbecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct libnet_UnbecomeDC_state *s;
|
||||
char *tmp_name;
|
||||
|
||||
c = composite_create(mem_ctx, ctx->event_ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct libnet_UnbecomeDC_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
c->private_data = s;
|
||||
s->creq = c;
|
||||
s->libnet = ctx;
|
||||
|
||||
/* Domain input */
|
||||
s->domain.dns_name = talloc_strdup(s, r->in.domain_dns_name);
|
||||
if (composite_nomem(s->domain.dns_name, c)) return c;
|
||||
s->domain.netbios_name = talloc_strdup(s, r->in.domain_netbios_name);
|
||||
if (composite_nomem(s->domain.netbios_name, c)) return c;
|
||||
|
||||
/* Source DSA input */
|
||||
s->source_dsa.address = talloc_strdup(s, r->in.source_dsa_address);
|
||||
if (composite_nomem(s->source_dsa.address, c)) return c;
|
||||
|
||||
/* Destination DSA input */
|
||||
s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
|
||||
if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
|
||||
|
||||
/* Destination DSA dns_name construction */
|
||||
tmp_name = strlower_talloc(s, s->dest_dsa.netbios_name);
|
||||
if (composite_nomem(tmp_name, c)) return c;
|
||||
s->dest_dsa.dns_name = talloc_asprintf_append(tmp_name, ".%s",
|
||||
s->domain.dns_name);
|
||||
if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
|
||||
|
||||
unbecomeDC_send_cldap(s);
|
||||
return c;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_UnbecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
ZERO_STRUCT(r->out);
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_UnbecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct composite_context *c;
|
||||
c = libnet_UnbecomeDC_send(ctx, mem_ctx, r);
|
||||
status = libnet_UnbecomeDC_recv(c, mem_ctx, r);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 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 libnet_UnbecomeDC {
|
||||
struct {
|
||||
const char *domain_dns_name;
|
||||
const char *domain_netbios_name;
|
||||
const char *source_dsa_address;
|
||||
const char *dest_dsa_netbios_name;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak <mimir@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.
|
||||
*/
|
||||
|
||||
|
||||
struct libnet_CreateUser {
|
||||
struct {
|
||||
const char *user_name;
|
||||
const char *domain_name;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_DeleteUser {
|
||||
struct {
|
||||
const char *user_name;
|
||||
const char *domain_name;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_ModifyUser {
|
||||
struct {
|
||||
const char *user_name;
|
||||
const char *domain_name;
|
||||
|
||||
const char *account_name;
|
||||
const char *full_name;
|
||||
const char *description;
|
||||
const char *home_directory;
|
||||
const char *home_drive;
|
||||
const char *comment;
|
||||
const char *logon_script;
|
||||
const char *profile_path;
|
||||
struct timeval *acct_expiry;
|
||||
struct timeval *allow_password_change;
|
||||
struct timeval *force_password_change;
|
||||
struct timeval *last_password_change;
|
||||
uint32_t acct_flags;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
#define SET_FIELD_LSA_STRING(new, current, mod, field, flag) \
|
||||
if (new.field != NULL && \
|
||||
!strequal_w(current->field.string, new.field)) { \
|
||||
\
|
||||
mod->field = talloc_strdup(mem_ctx, new.field); \
|
||||
if (mod->field == NULL) return NT_STATUS_NO_MEMORY; \
|
||||
\
|
||||
mod->fields |= flag; \
|
||||
}
|
||||
|
||||
#define SET_FIELD_NTTIME(new, current, mod, field, flag) \
|
||||
if (new.field != 0) { \
|
||||
NTTIME newval = timeval_to_nttime(new.field); \
|
||||
if (newval != current->field) { \
|
||||
mod->field = talloc_memdup(mem_ctx, new.field, sizeof(*new.field)); \
|
||||
if (mod->field == NULL) return NT_STATUS_NO_MEMORY; \
|
||||
mod->fields |= flag; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
struct libnet_UserInfo {
|
||||
struct {
|
||||
const char *user_name;
|
||||
const char *domain_name;
|
||||
} in;
|
||||
struct {
|
||||
const char *account_name;
|
||||
const char *full_name;
|
||||
const char *description;
|
||||
const char *home_directory;
|
||||
const char *home_drive;
|
||||
const char *comment;
|
||||
const char *logon_script;
|
||||
const char *profile_path;
|
||||
struct timeval *acct_expiry;
|
||||
struct timeval *allow_password_change;
|
||||
struct timeval *force_password_change;
|
||||
struct timeval *last_logon;
|
||||
struct timeval *last_logoff;
|
||||
struct timeval *last_password_change;
|
||||
uint32_t acct_flags;
|
||||
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_UserList {
|
||||
struct {
|
||||
const char *domain_name;
|
||||
int page_size;
|
||||
uint resume_index;
|
||||
} in;
|
||||
struct {
|
||||
int count;
|
||||
uint resume_index;
|
||||
|
||||
struct userlist {
|
||||
const char *sid;
|
||||
const char *username;
|
||||
} *users;
|
||||
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Extract the user/system database from a remote SamSync server
|
||||
|
||||
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 "libnet/libnet.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/schannel_proto.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon_c.h"
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt and extract the user's passwords.
|
||||
*
|
||||
* The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure
|
||||
*/
|
||||
static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState *creds,
|
||||
enum netr_SamDatabaseID database,
|
||||
struct netr_DELTA_ENUM *delta,
|
||||
char **error_string)
|
||||
{
|
||||
|
||||
uint32_t rid = delta->delta_id_union.rid;
|
||||
struct netr_DELTA_USER *user = delta->delta_union.user;
|
||||
struct samr_Password lm_hash;
|
||||
struct samr_Password nt_hash;
|
||||
const char *username = user->account_name.string;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
if (user->lm_password_present) {
|
||||
sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
|
||||
user->lmpassword = lm_hash;
|
||||
}
|
||||
|
||||
if (user->nt_password_present) {
|
||||
sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
|
||||
user->ntpassword = nt_hash;
|
||||
}
|
||||
|
||||
if (user->user_private_info.SensitiveData) {
|
||||
DATA_BLOB data;
|
||||
struct netr_USER_KEYS keys;
|
||||
data.data = user->user_private_info.SensitiveData;
|
||||
data.length = user->user_private_info.DataLength;
|
||||
creds_arcfour_crypt(creds, data.data, data.length);
|
||||
user->user_private_info.SensitiveData = data.data;
|
||||
user->user_private_info.DataLength = data.length;
|
||||
|
||||
nt_status = ndr_pull_struct_blob(&data, mem_ctx, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
if (keys.keys.keys2.lmpassword.length == 16) {
|
||||
sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
|
||||
user->lmpassword = lm_hash;
|
||||
user->lm_password_present = True;
|
||||
}
|
||||
if (keys.keys.keys2.ntpassword.length == 16) {
|
||||
sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
|
||||
user->ntpassword = nt_hash;
|
||||
user->nt_password_present = True;
|
||||
}
|
||||
} else {
|
||||
*error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:", username);
|
||||
dump_data(10, data.data, data.length);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt and extract the secrets
|
||||
*
|
||||
* The writes decrypted secrets back into the structure
|
||||
*/
|
||||
static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState *creds,
|
||||
enum netr_SamDatabaseID database,
|
||||
struct netr_DELTA_ENUM *delta,
|
||||
char **error_string)
|
||||
{
|
||||
struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
|
||||
creds_arcfour_crypt(creds, secret->current_cipher.cipher_data,
|
||||
secret->current_cipher.maxlen);
|
||||
|
||||
creds_arcfour_crypt(creds, secret->old_cipher.cipher_data,
|
||||
secret->old_cipher.maxlen);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix up the delta, dealing with encryption issues so that the final
|
||||
* callback need only do the printing or application logic
|
||||
*/
|
||||
|
||||
static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState *creds,
|
||||
enum netr_SamDatabaseID database,
|
||||
struct netr_DELTA_ENUM *delta,
|
||||
char **error_string)
|
||||
{
|
||||
NTSTATUS nt_status = NT_STATUS_OK;
|
||||
*error_string = NULL;
|
||||
switch (delta->delta_type) {
|
||||
case NETR_DELTA_USER:
|
||||
{
|
||||
nt_status = fix_user(mem_ctx,
|
||||
creds,
|
||||
database,
|
||||
delta,
|
||||
error_string);
|
||||
break;
|
||||
}
|
||||
case NETR_DELTA_SECRET:
|
||||
{
|
||||
nt_status = fix_secret(mem_ctx,
|
||||
creds,
|
||||
database,
|
||||
delta,
|
||||
error_string);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r)
|
||||
{
|
||||
NTSTATUS nt_status, dbsync_nt_status;
|
||||
TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx;
|
||||
struct creds_CredentialState *creds;
|
||||
struct netr_DatabaseSync dbsync;
|
||||
struct cli_credentials *machine_account;
|
||||
struct dcerpc_pipe *p;
|
||||
struct libnet_context *machine_net_ctx;
|
||||
struct libnet_RpcConnect *c;
|
||||
struct libnet_SamSync_state *state;
|
||||
const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS};
|
||||
int i;
|
||||
|
||||
samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context");
|
||||
|
||||
if (!r->in.machine_account) {
|
||||
machine_account = cli_credentials_init(samsync_ctx);
|
||||
if (!machine_account) {
|
||||
talloc_free(samsync_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cli_credentials_set_conf(machine_account);
|
||||
nt_status = cli_credentials_set_machine_account(machine_account);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
} else {
|
||||
machine_account = r->in.machine_account;
|
||||
}
|
||||
|
||||
/* We cannot do this unless we are a BDC. Check, before we get odd errors later */
|
||||
if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
|
||||
r->out.error_string
|
||||
= talloc_asprintf(mem_ctx,
|
||||
"Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
|
||||
cli_credentials_get_domain(machine_account),
|
||||
cli_credentials_get_secure_channel_type(machine_account));
|
||||
talloc_free(samsync_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
c = talloc(samsync_ctx, struct libnet_RpcConnect);
|
||||
if (!c) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(samsync_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
c->level = LIBNET_RPC_CONNECT_DC_INFO;
|
||||
if (r->in.binding_string) {
|
||||
c->in.binding = r->in.binding_string;
|
||||
c->in.name = NULL;
|
||||
} else {
|
||||
c->in.binding = NULL;
|
||||
c->in.name = cli_credentials_get_domain(machine_account);
|
||||
}
|
||||
|
||||
/* prepare connect to the NETLOGON pipe of PDC */
|
||||
c->in.dcerpc_iface = &dcerpc_table_netlogon;
|
||||
|
||||
/* We must do this as the machine, not as any command-line
|
||||
* user. So we override the credentials in the
|
||||
* libnet_context */
|
||||
machine_net_ctx = talloc(samsync_ctx, struct libnet_context);
|
||||
if (!machine_net_ctx) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(samsync_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
*machine_net_ctx = *ctx;
|
||||
machine_net_ctx->cred = machine_account;
|
||||
|
||||
/* connect to the NETLOGON pipe of the PDC */
|
||||
nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
if (r->in.binding_string) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to NETLOGON pipe of DC %s failed: %s",
|
||||
r->in.binding_string, c->out.error_string);
|
||||
} else {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Connection to NETLOGON pipe of DC for %s failed: %s",
|
||||
c->in.name, c->out.error_string);
|
||||
}
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* This makes a new pipe, on which we can do schannel. We
|
||||
* should do this in the RpcConnect code, but the abstaction
|
||||
* layers do not suit yet */
|
||||
|
||||
nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p,
|
||||
c->out.dcerpc_pipe->binding);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"Secondary connection to NETLOGON pipe of DC %s failed: %s",
|
||||
dcerpc_server_name(p), nt_errstr(nt_status));
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &dcerpc_table_netlogon,
|
||||
machine_account, DCERPC_AUTH_LEVEL_PRIVACY);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx,
|
||||
"SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s",
|
||||
dcerpc_server_name(p), nt_errstr(nt_status));
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
state = talloc(samsync_ctx, struct libnet_SamSync_state);
|
||||
if (!state) {
|
||||
r->out.error_string = NULL;
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
state->domain_name = c->out.domain_name;
|
||||
state->domain_sid = c->out.domain_sid;
|
||||
state->realm = c->out.realm;
|
||||
state->domain_guid = c->out.guid;
|
||||
state->machine_net_ctx = machine_net_ctx;
|
||||
state->netlogon_pipe = p;
|
||||
|
||||
/* initialise the callback layer. It may wish to contact the
|
||||
* server with ldap, now we know the name */
|
||||
|
||||
if (r->in.init_fn) {
|
||||
char *error_string;
|
||||
nt_status = r->in.init_fn(samsync_ctx,
|
||||
r->in.fn_ctx,
|
||||
state,
|
||||
&error_string);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_steal(mem_ctx, error_string);
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* get NETLOGON credentails */
|
||||
|
||||
nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* Setup details for the synchronisation */
|
||||
dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p));
|
||||
dbsync.in.computername = cli_credentials_get_workstation(machine_account);
|
||||
dbsync.in.preferredmaximumlength = (uint32_t)-1;
|
||||
ZERO_STRUCT(dbsync.in.return_authenticator);
|
||||
|
||||
for (i=0;i< ARRAY_SIZE(database_ids); i++) {
|
||||
dbsync.in.sync_context = 0;
|
||||
dbsync.in.database_id = database_ids[i];
|
||||
|
||||
do {
|
||||
int d;
|
||||
loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context");
|
||||
creds_client_authenticator(creds, &dbsync.in.credential);
|
||||
|
||||
dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync);
|
||||
if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
|
||||
!NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) {
|
||||
r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed");
|
||||
talloc_free(samsync_ctx);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
dbsync.in.sync_context = dbsync.out.sync_context;
|
||||
|
||||
/* For every single remote 'delta' entry: */
|
||||
for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) {
|
||||
char *error_string = NULL;
|
||||
delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
|
||||
/* 'Fix' elements, by decrypting and
|
||||
* de-obfuscating the data */
|
||||
nt_status = fix_delta(delta_ctx,
|
||||
creds,
|
||||
dbsync.in.database_id,
|
||||
&dbsync.out.delta_enum_array->delta_enum[d],
|
||||
&error_string);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_steal(mem_ctx, error_string);
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* Now call the callback. This will
|
||||
* do something like print the data or
|
||||
* write to an ldb */
|
||||
nt_status = r->in.delta_fn(delta_ctx,
|
||||
r->in.fn_ctx,
|
||||
dbsync.in.database_id,
|
||||
&dbsync.out.delta_enum_array->delta_enum[d],
|
||||
&error_string);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
r->out.error_string = talloc_steal(mem_ctx, error_string);
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
talloc_free(delta_ctx);
|
||||
}
|
||||
talloc_free(loop_ctx);
|
||||
} while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
|
||||
|
||||
if (!NT_STATUS_IS_OK(dbsync_nt_status)) {
|
||||
r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status));
|
||||
talloc_free(samsync_ctx);
|
||||
return dbsync_nt_status;
|
||||
}
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
talloc_free(samsync_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
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 "librpc/gen_ndr/netlogon.h"
|
||||
|
||||
struct libnet_SamSync_state {
|
||||
struct libnet_context *machine_net_ctx;
|
||||
struct dcerpc_pipe *netlogon_pipe;
|
||||
const char *domain_name;
|
||||
const struct dom_sid *domain_sid;
|
||||
const char *realm;
|
||||
struct GUID *domain_guid;
|
||||
};
|
||||
|
||||
/* struct and enum for doing a remote domain vampire dump */
|
||||
struct libnet_SamSync {
|
||||
struct {
|
||||
const char *binding_string;
|
||||
NTSTATUS (*init_fn)(TALLOC_CTX *mem_ctx,
|
||||
void *private,
|
||||
struct libnet_SamSync_state *samsync_state,
|
||||
char **error_string);
|
||||
NTSTATUS (*delta_fn)(TALLOC_CTX *mem_ctx,
|
||||
void *private,
|
||||
enum netr_SamDatabaseID database,
|
||||
struct netr_DELTA_ENUM *delta,
|
||||
char **error_string);
|
||||
void *fn_ctx;
|
||||
struct cli_credentials *machine_account;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
struct libnet_SamDump {
|
||||
struct {
|
||||
const char *binding_string;
|
||||
struct cli_credentials *machine_account;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
struct libnet_SamDump_keytab {
|
||||
struct {
|
||||
const char *binding_string;
|
||||
const char *keytab_name;
|
||||
struct cli_credentials *machine_account;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
struct libnet_samsync_ldb {
|
||||
struct {
|
||||
const char *binding_string;
|
||||
struct cli_credentials *machine_account;
|
||||
struct auth_session_info *session_info;
|
||||
} in;
|
||||
struct {
|
||||
const char *error_string;
|
||||
} out;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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 "libnet/libnet.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "librpc/ndr/libndr.h"
|
||||
#include "librpc/gen_ndr/samr.h"
|
||||
#include "librpc/gen_ndr/ndr_samr.h"
|
||||
#include "librpc/gen_ndr/lsa.h"
|
||||
#include "librpc/gen_ndr/ndr_lsa.h"
|
||||
|
||||
|
||||
struct composite_context* samr_domain_opened(struct libnet_context *ctx,
|
||||
const char *domain_name,
|
||||
struct composite_context *parent_ctx,
|
||||
struct libnet_DomainOpen *domain_open,
|
||||
void (*continue_fn)(struct composite_context*),
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *domopen_req;
|
||||
|
||||
if (domain_name == NULL) {
|
||||
/*
|
||||
* Try to guess the domain name from credentials,
|
||||
* if it's not been explicitly specified.
|
||||
*/
|
||||
|
||||
if (policy_handle_empty(&ctx->samr.handle)) {
|
||||
domain_open->in.type = DOMAIN_SAMR;
|
||||
domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
|
||||
domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
|
||||
} else {
|
||||
composite_error(parent_ctx, NT_STATUS_INVALID_PARAMETER);
|
||||
return parent_ctx;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* The domain name has been specified, so check whether the same
|
||||
* domain is already opened. If it is - just return NULL. Start
|
||||
* opening a new domain otherwise.
|
||||
*/
|
||||
|
||||
if (policy_handle_empty(&ctx->samr.handle) ||
|
||||
!strequal(domain_name, ctx->samr.name)) {
|
||||
domain_open->in.type = DOMAIN_SAMR;
|
||||
domain_open->in.domain_name = domain_name;
|
||||
domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
|
||||
} else {
|
||||
/* domain has already been opened and it's the same domain
|
||||
as requested */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* send request to open the domain */
|
||||
domopen_req = libnet_DomainOpen_send(ctx, domain_open, monitor);
|
||||
if (composite_nomem(domopen_req, parent_ctx)) return parent_ctx;
|
||||
|
||||
composite_continue(parent_ctx, domopen_req, continue_fn, parent_ctx);
|
||||
return parent_ctx;
|
||||
}
|
||||
|
||||
|
||||
struct composite_context* lsa_domain_opened(struct libnet_context *ctx,
|
||||
const char *domain_name,
|
||||
struct composite_context *parent_ctx,
|
||||
struct libnet_DomainOpen *domain_open,
|
||||
void (*continue_fn)(struct composite_context*),
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *domopen_req;
|
||||
|
||||
if (domain_name == NULL) {
|
||||
/*
|
||||
* Try to guess the domain name from credentials,
|
||||
* if it's not been explicitly specified.
|
||||
*/
|
||||
|
||||
if (policy_handle_empty(&ctx->lsa.handle)) {
|
||||
domain_open->in.type = DOMAIN_LSA;
|
||||
domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
|
||||
domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
|
||||
} else {
|
||||
composite_error(parent_ctx, NT_STATUS_INVALID_PARAMETER);
|
||||
return parent_ctx;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* The domain name has been specified, so check whether the same
|
||||
* domain is already opened. If it is - just return NULL. Start
|
||||
* opening a new domain otherwise.
|
||||
*/
|
||||
|
||||
if (policy_handle_empty(&ctx->lsa.handle) ||
|
||||
!strequal(domain_name, ctx->lsa.name)) {
|
||||
domain_open->in.type = DOMAIN_LSA;
|
||||
domain_open->in.domain_name = domain_name;
|
||||
domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
|
||||
} else {
|
||||
/* domain has already been opened and it's the same domain
|
||||
as requested */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* send request to open the domain */
|
||||
domopen_req = libnet_DomainOpen_send(ctx, domain_open, monitor);
|
||||
if (composite_nomem(domopen_req, parent_ctx)) return parent_ctx;
|
||||
|
||||
composite_continue(parent_ctx, domopen_req, continue_fn, parent_ctx);
|
||||
return parent_ctx;
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
a composite function for getting user information via samr pipe
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libnet/composite.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libnet/userman.h"
|
||||
#include "libnet/userinfo.h"
|
||||
#include "librpc/gen_ndr/ndr_samr_c.h"
|
||||
|
||||
static void userinfo_handler(struct rpc_request *req);
|
||||
|
||||
enum userinfo_stage { USERINFO_LOOKUP, USERINFO_OPENUSER, USERINFO_GETUSER, USERINFO_CLOSEUSER };
|
||||
|
||||
struct userinfo_state {
|
||||
enum userinfo_stage stage;
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct rpc_request *req;
|
||||
struct policy_handle domain_handle;
|
||||
struct policy_handle user_handle;
|
||||
uint16_t level;
|
||||
struct samr_LookupNames lookup;
|
||||
struct samr_OpenUser openuser;
|
||||
struct samr_QueryUserInfo queryuserinfo;
|
||||
struct samr_Close samrclose;
|
||||
union samr_UserInfo *info;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg *);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stage 1 (optional): Look for a username in SAM server.
|
||||
*/
|
||||
static NTSTATUS userinfo_lookup(struct composite_context *c,
|
||||
struct userinfo_state *s)
|
||||
{
|
||||
/* receive samr_Lookup reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* there could be a problem with name resolving itself */
|
||||
NT_STATUS_NOT_OK_RETURN(s->lookup.out.result);
|
||||
|
||||
/* have we actually got name resolved
|
||||
- we're looking for only one at the moment */
|
||||
if (s->lookup.out.rids.count == 0) {
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
/* TODO: find proper status code for more than one rid found */
|
||||
|
||||
/* prepare parameters for LookupNames */
|
||||
s->openuser.in.domain_handle = &s->domain_handle;
|
||||
s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
s->openuser.in.rid = s->lookup.out.rids.ids[0];
|
||||
s->openuser.out.user_handle = &s->user_handle;
|
||||
|
||||
/* send request */
|
||||
s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
|
||||
if (s->req == NULL) goto failure;
|
||||
|
||||
s->req->async.callback = userinfo_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERINFO_OPENUSER;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failure:
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 2: Open user policy handle.
|
||||
*/
|
||||
static NTSTATUS userinfo_openuser(struct composite_context *c,
|
||||
struct userinfo_state *s)
|
||||
{
|
||||
/* receive samr_OpenUser reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* prepare parameters for QueryUserInfo call */
|
||||
s->queryuserinfo.in.user_handle = &s->user_handle;
|
||||
s->queryuserinfo.in.level = s->level;
|
||||
|
||||
/* queue rpc call, set event handling and new state */
|
||||
s->req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuserinfo);
|
||||
if (s->req == NULL) goto failure;
|
||||
|
||||
s->req->async.callback = userinfo_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERINFO_GETUSER;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failure:
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 3: Get requested user information.
|
||||
*/
|
||||
static NTSTATUS userinfo_getuser(struct composite_context *c,
|
||||
struct userinfo_state *s)
|
||||
{
|
||||
/* receive samr_QueryUserInfo reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* check if queryuser itself went ok */
|
||||
NT_STATUS_NOT_OK_RETURN(s->queryuserinfo.out.result);
|
||||
|
||||
s->info = talloc_steal(s, s->queryuserinfo.out.info);
|
||||
|
||||
/* prepare arguments for Close call */
|
||||
s->samrclose.in.handle = &s->user_handle;
|
||||
s->samrclose.out.handle = &s->user_handle;
|
||||
|
||||
/* queue rpc call, set event handling and new state */
|
||||
s->req = dcerpc_samr_Close_send(s->pipe, c, &s->samrclose);
|
||||
|
||||
s->req->async.callback = userinfo_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERINFO_CLOSEUSER;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 4: Close policy handle associated with opened user.
|
||||
*/
|
||||
static NTSTATUS userinfo_closeuser(struct composite_context *c,
|
||||
struct userinfo_state *s)
|
||||
{
|
||||
/* receive samr_Close reply */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for asynchronous request. Handles transition through
|
||||
* intermediate stages of the call.
|
||||
*
|
||||
* @param req rpc call context
|
||||
*/
|
||||
static void userinfo_handler(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c = req->async.private;
|
||||
struct userinfo_state *s = talloc_get_type(c->private_data, struct userinfo_state);
|
||||
struct monitor_msg msg;
|
||||
struct msg_rpc_lookup_name *msg_lookup;
|
||||
struct msg_rpc_open_user *msg_open;
|
||||
struct msg_rpc_query_user *msg_query;
|
||||
struct msg_rpc_close_user *msg_close;
|
||||
|
||||
/* Stages of the call */
|
||||
switch (s->stage) {
|
||||
case USERINFO_LOOKUP:
|
||||
c->status = userinfo_lookup(c, s);
|
||||
|
||||
msg.type = rpc_lookup_name;
|
||||
msg_lookup = talloc(s, struct msg_rpc_lookup_name);
|
||||
msg_lookup->rid = s->lookup.out.rids.ids;
|
||||
msg_lookup->count = s->lookup.out.rids.count;
|
||||
msg.data = (void*)msg_lookup;
|
||||
msg.data_size = sizeof(*msg_lookup);
|
||||
break;
|
||||
|
||||
case USERINFO_OPENUSER:
|
||||
c->status = userinfo_openuser(c, s);
|
||||
|
||||
msg.type = rpc_open_user;
|
||||
msg_open = talloc(s, struct msg_rpc_open_user);
|
||||
msg_open->rid = s->openuser.in.rid;
|
||||
msg_open->access_mask = s->openuser.in.access_mask;
|
||||
msg.data = (void*)msg_open;
|
||||
msg.data_size = sizeof(*msg_open);
|
||||
break;
|
||||
|
||||
case USERINFO_GETUSER:
|
||||
c->status = userinfo_getuser(c, s);
|
||||
|
||||
msg.type = rpc_query_user;
|
||||
msg_query = talloc(s, struct msg_rpc_query_user);
|
||||
msg_query->level = s->queryuserinfo.in.level;
|
||||
msg.data = (void*)msg_query;
|
||||
msg.data_size = sizeof(*msg_query);
|
||||
break;
|
||||
|
||||
case USERINFO_CLOSEUSER:
|
||||
c->status = userinfo_closeuser(c, s);
|
||||
|
||||
msg.type = rpc_close_user;
|
||||
msg_close = talloc(s, struct msg_rpc_close_user);
|
||||
msg_close->rid = s->openuser.in.rid;
|
||||
msg.data = (void*)msg_close;
|
||||
msg.data_size = sizeof(*msg_close);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
}
|
||||
|
||||
if (s->monitor_fn) {
|
||||
s->monitor_fn(&msg);
|
||||
}
|
||||
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous userinfo request
|
||||
*
|
||||
* @param p dce/rpc call pipe
|
||||
* @param io arguments and results of the call
|
||||
*/
|
||||
struct composite_context *libnet_rpc_userinfo_send(struct dcerpc_pipe *p,
|
||||
struct libnet_rpc_userinfo *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct userinfo_state *s;
|
||||
struct dom_sid *sid;
|
||||
|
||||
if (!p || !io) return NULL;
|
||||
|
||||
c = talloc_zero(p, struct composite_context);
|
||||
if (c == NULL) goto failure;
|
||||
|
||||
s = talloc_zero(c, struct userinfo_state);
|
||||
if (s == NULL) goto failure;
|
||||
|
||||
s->level = io->in.level;
|
||||
s->pipe = p;
|
||||
s->domain_handle = io->in.domain_handle;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = dcerpc_event_context(p);
|
||||
|
||||
if (io->in.sid) {
|
||||
sid = dom_sid_parse_talloc(s, io->in.sid);
|
||||
if (sid == NULL) goto failure;
|
||||
|
||||
s->openuser.in.domain_handle = &s->domain_handle;
|
||||
s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
s->openuser.in.rid = sid->sub_auths[sid->num_auths - 1];
|
||||
s->openuser.out.user_handle = &s->user_handle;
|
||||
|
||||
/* send request */
|
||||
s->req = dcerpc_samr_OpenUser_send(p, c, &s->openuser);
|
||||
if (s->req == NULL) goto failure;
|
||||
|
||||
s->stage = USERINFO_OPENUSER;
|
||||
|
||||
} else {
|
||||
/* preparing parameters to send rpc request */
|
||||
s->lookup.in.domain_handle = &s->domain_handle;
|
||||
s->lookup.in.num_names = 1;
|
||||
s->lookup.in.names = talloc_array(s, struct lsa_String, 1);
|
||||
|
||||
if (composite_nomem(s->lookup.in.names, c)) return c;
|
||||
s->lookup.in.names[0].string = talloc_strdup(s, io->in.username);
|
||||
|
||||
/* send request */
|
||||
s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookup);
|
||||
if (s->req == NULL) goto failure;
|
||||
|
||||
s->stage = USERINFO_LOOKUP;
|
||||
}
|
||||
|
||||
/* callback handler */
|
||||
s->req->async.callback = userinfo_handler;
|
||||
s->req->async.private = c;
|
||||
|
||||
return c;
|
||||
|
||||
failure:
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for and receives result of asynchronous userinfo call
|
||||
*
|
||||
* @param c composite context returned by asynchronous userinfo call
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_userinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_userinfo *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct userinfo_state *s;
|
||||
|
||||
/* wait for results of sending request */
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
s = talloc_get_type(c->private_data, struct userinfo_state);
|
||||
talloc_steal(mem_ctx, s->info);
|
||||
io->out.info = *s->info;
|
||||
}
|
||||
|
||||
/* memory context associated to composite context is no longer needed */
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of userinfo call
|
||||
*
|
||||
* @param pipe dce/rpc call pipe
|
||||
* @param mem_ctx memory context for the call
|
||||
* @param io arguments and results of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_userinfo(struct dcerpc_pipe *p,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_userinfo *io)
|
||||
{
|
||||
struct composite_context *c = libnet_rpc_userinfo_send(p, io, NULL);
|
||||
return libnet_rpc_userinfo_recv(c, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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 "librpc/gen_ndr/samr.h"
|
||||
|
||||
/*
|
||||
* IO structures for userinfo.c functions
|
||||
*/
|
||||
|
||||
struct libnet_rpc_userinfo {
|
||||
struct {
|
||||
struct policy_handle domain_handle;
|
||||
const char *username;
|
||||
const char *sid;
|
||||
uint16_t level;
|
||||
} in;
|
||||
struct {
|
||||
union samr_UserInfo info;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Monitor messages sent from userinfo.c functions
|
||||
*/
|
||||
|
||||
struct msg_rpc_open_user {
|
||||
uint32_t rid, access_mask;
|
||||
};
|
||||
|
||||
struct msg_rpc_query_user {
|
||||
uint16_t level;
|
||||
};
|
||||
|
||||
struct msg_rpc_close_user {
|
||||
uint32_t rid;
|
||||
};
|
||||
@@ -0,0 +1,968 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
a composite functions for user management operations (add/del/chg)
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libnet/composite.h"
|
||||
#include "libnet/userman.h"
|
||||
#include "libnet/userinfo.h"
|
||||
#include "librpc/gen_ndr/ndr_samr_c.h"
|
||||
|
||||
/*
|
||||
* Composite USER ADD functionality
|
||||
*/
|
||||
|
||||
static void useradd_handler(struct rpc_request*);
|
||||
|
||||
enum useradd_stage { USERADD_CREATE };
|
||||
|
||||
struct useradd_state {
|
||||
enum useradd_stage stage;
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct rpc_request *req;
|
||||
struct policy_handle domain_handle;
|
||||
struct samr_CreateUser createuser;
|
||||
struct policy_handle user_handle;
|
||||
uint32_t user_rid;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg *);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stage 1 (and the only one for now): Create user account.
|
||||
*/
|
||||
static NTSTATUS useradd_create(struct composite_context *c,
|
||||
struct useradd_state *s)
|
||||
{
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for asynchronous request. Handles transition through
|
||||
* intermediate stages of the call.
|
||||
*
|
||||
* @param req rpc call context
|
||||
*/
|
||||
static void useradd_handler(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c = req->async.private;
|
||||
struct useradd_state *s = talloc_get_type(c->private_data, struct useradd_state);
|
||||
struct monitor_msg msg;
|
||||
struct msg_rpc_create_user *rpc_create;
|
||||
|
||||
switch (s->stage) {
|
||||
case USERADD_CREATE:
|
||||
c->status = useradd_create(c, s);
|
||||
|
||||
/* prepare a message to pass to monitor function */
|
||||
msg.type = rpc_create_user;
|
||||
rpc_create = talloc(s, struct msg_rpc_create_user);
|
||||
rpc_create->rid = *s->createuser.out.rid;
|
||||
msg.data = (void*)rpc_create;
|
||||
msg.data_size = sizeof(*rpc_create);
|
||||
break;
|
||||
}
|
||||
|
||||
/* are we ok so far ? */
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
}
|
||||
|
||||
/* call monitor function provided the pointer has been passed */
|
||||
if (s->monitor_fn) {
|
||||
s->monitor_fn(&msg);
|
||||
}
|
||||
|
||||
/* are we done yet ? */
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous useradd request
|
||||
*
|
||||
* @param p dce/rpc call pipe
|
||||
* @param io arguments and results of the call
|
||||
* @param monitor monitor function for providing information about the progress
|
||||
*/
|
||||
|
||||
struct composite_context *libnet_rpc_useradd_send(struct dcerpc_pipe *p,
|
||||
struct libnet_rpc_useradd *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct useradd_state *s;
|
||||
|
||||
/* composite allocation and setup */
|
||||
c = talloc_zero(p, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct useradd_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = dcerpc_event_context(p);
|
||||
|
||||
/* put passed arguments to the state structure */
|
||||
s->domain_handle = io->in.domain_handle;
|
||||
s->pipe = p;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
/* preparing parameters to send rpc request */
|
||||
s->createuser.in.domain_handle = &io->in.domain_handle;
|
||||
s->createuser.in.account_name = talloc_zero(c, struct lsa_String);
|
||||
s->createuser.in.account_name->string = talloc_strdup(c, io->in.username);
|
||||
s->createuser.out.user_handle = &s->user_handle;
|
||||
s->createuser.out.rid = &s->user_rid;
|
||||
|
||||
/* send the request */
|
||||
s->req = dcerpc_samr_CreateUser_send(p, c, &s->createuser);
|
||||
if (composite_nomem(s->req, c)) return c;
|
||||
|
||||
/* callback handler for continuation */
|
||||
s->req->async.callback = useradd_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERADD_CREATE;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for and receives result of asynchronous useradd call
|
||||
*
|
||||
* @param c composite context returned by asynchronous useradd call
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_useradd *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct useradd_state *s;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
/* get and return result of the call */
|
||||
s = talloc_get_type(c->private_data, struct useradd_state);
|
||||
io->out.user_handle = s->user_handle;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of useradd call
|
||||
*
|
||||
* @param pipe dce/rpc call pipe
|
||||
* @param mem_ctx memory context for the call
|
||||
* @param io arguments and results of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_useradd(struct dcerpc_pipe *p,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_useradd *io)
|
||||
{
|
||||
struct composite_context *c = libnet_rpc_useradd_send(p, io, NULL);
|
||||
return libnet_rpc_useradd_recv(c, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Composite USER DELETE functionality
|
||||
*/
|
||||
|
||||
static void userdel_handler(struct rpc_request*);
|
||||
|
||||
enum userdel_stage { USERDEL_LOOKUP, USERDEL_OPEN, USERDEL_DELETE };
|
||||
|
||||
struct userdel_state {
|
||||
enum userdel_stage stage;
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct rpc_request *req;
|
||||
struct policy_handle domain_handle;
|
||||
struct policy_handle user_handle;
|
||||
struct samr_LookupNames lookupname;
|
||||
struct samr_OpenUser openuser;
|
||||
struct samr_DeleteUser deleteuser;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg *);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stage 1: Lookup the user name and resolve it to rid
|
||||
*/
|
||||
static NTSTATUS userdel_lookup(struct composite_context *c,
|
||||
struct userdel_state *s)
|
||||
{
|
||||
/* receive samr_LookupNames result */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* what to do when there's no user account to delete
|
||||
and what if there's more than one rid resolved */
|
||||
if (!s->lookupname.out.rids.count) {
|
||||
c->status = NT_STATUS_NO_SUCH_USER;
|
||||
composite_error(c, c->status);
|
||||
|
||||
} else if (!s->lookupname.out.rids.count > 1) {
|
||||
c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
|
||||
composite_error(c, c->status);
|
||||
}
|
||||
|
||||
/* prepare the next rpc call arguments */
|
||||
s->openuser.in.domain_handle = &s->domain_handle;
|
||||
s->openuser.in.rid = s->lookupname.out.rids.ids[0];
|
||||
s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
s->openuser.out.user_handle = &s->user_handle;
|
||||
|
||||
/* send rpc request */
|
||||
s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
|
||||
if (s->req == NULL) return NT_STATUS_NO_MEMORY;
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = userdel_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERDEL_OPEN;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 2: Open user account.
|
||||
*/
|
||||
static NTSTATUS userdel_open(struct composite_context *c,
|
||||
struct userdel_state *s)
|
||||
{
|
||||
/* receive samr_OpenUser result */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* prepare the final rpc call arguments */
|
||||
s->deleteuser.in.user_handle = &s->user_handle;
|
||||
s->deleteuser.out.user_handle = &s->user_handle;
|
||||
|
||||
/* send rpc request */
|
||||
s->req = dcerpc_samr_DeleteUser_send(s->pipe, c, &s->deleteuser);
|
||||
if (s->req == NULL) return NT_STATUS_NO_MEMORY;
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = userdel_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERDEL_DELETE;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 3: Delete user account
|
||||
*/
|
||||
static NTSTATUS userdel_delete(struct composite_context *c,
|
||||
struct userdel_state *s)
|
||||
{
|
||||
/* receive samr_DeleteUser result */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for asynchronous request. Handles transition through
|
||||
* intermediate stages of the call.
|
||||
*
|
||||
* @param req rpc call context
|
||||
*/
|
||||
static void userdel_handler(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct userdel_state *s;
|
||||
struct monitor_msg msg;
|
||||
struct msg_rpc_lookup_name *msg_lookup;
|
||||
struct msg_rpc_open_user *msg_open;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct userdel_state);
|
||||
|
||||
switch (s->stage) {
|
||||
case USERDEL_LOOKUP:
|
||||
c->status = userdel_lookup(c, s);
|
||||
|
||||
/* monitor message */
|
||||
msg.type = rpc_lookup_name;
|
||||
msg_lookup = talloc(s, struct msg_rpc_lookup_name);
|
||||
|
||||
msg_lookup->rid = s->lookupname.out.rids.ids;
|
||||
msg_lookup->count = s->lookupname.out.rids.count;
|
||||
msg.data = (void*)msg_lookup;
|
||||
msg.data_size = sizeof(*msg_lookup);
|
||||
break;
|
||||
|
||||
case USERDEL_OPEN:
|
||||
c->status = userdel_open(c, s);
|
||||
|
||||
/* monitor message */
|
||||
msg.type = rpc_open_user;
|
||||
msg_open = talloc(s, struct msg_rpc_open_user);
|
||||
|
||||
msg_open->rid = s->openuser.in.rid;
|
||||
msg_open->access_mask = s->openuser.in.rid;
|
||||
msg.data = (void*)msg_open;
|
||||
msg.data_size = sizeof(*msg_open);
|
||||
break;
|
||||
|
||||
case USERDEL_DELETE:
|
||||
c->status = userdel_delete(c, s);
|
||||
|
||||
/* monitor message */
|
||||
msg.type = rpc_delete_user;
|
||||
msg.data = NULL;
|
||||
msg.data_size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* are we ok, so far ? */
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
}
|
||||
|
||||
/* call monitor function provided the pointer has been passed */
|
||||
if (s->monitor_fn) {
|
||||
s->monitor_fn(&msg);
|
||||
}
|
||||
|
||||
/* are we done yet */
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous userdel request
|
||||
*
|
||||
* @param p dce/rpc call pipe
|
||||
* @param io arguments and results of the call
|
||||
* @param monitor monitor function for providing information about the progress
|
||||
*/
|
||||
|
||||
struct composite_context *libnet_rpc_userdel_send(struct dcerpc_pipe *p,
|
||||
struct libnet_rpc_userdel *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct userdel_state *s;
|
||||
|
||||
/* composite context allocation and setup */
|
||||
c = talloc_zero(p, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct userdel_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = dcerpc_event_context(p);
|
||||
|
||||
/* store function parameters in the state structure */
|
||||
s->pipe = p;
|
||||
s->domain_handle = io->in.domain_handle;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
/* preparing parameters to send rpc request */
|
||||
s->lookupname.in.domain_handle = &io->in.domain_handle;
|
||||
s->lookupname.in.num_names = 1;
|
||||
s->lookupname.in.names = talloc_zero(s, struct lsa_String);
|
||||
s->lookupname.in.names->string = io->in.username;
|
||||
|
||||
/* send the request */
|
||||
s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = userdel_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERDEL_LOOKUP;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for and receives results of asynchronous userdel call
|
||||
*
|
||||
* @param c composite context returned by asynchronous userdel call
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_userdel *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct userdel_state *s;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) && io) {
|
||||
s = talloc_get_type(c->private_data, struct userdel_state);
|
||||
io->out.user_handle = s->user_handle;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of userdel call
|
||||
*
|
||||
* @param pipe dce/rpc call pipe
|
||||
* @param mem_ctx memory context for the call
|
||||
* @param io arguments and results of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_userdel(struct dcerpc_pipe *p,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_userdel *io)
|
||||
{
|
||||
struct composite_context *c = libnet_rpc_userdel_send(p, io, NULL);
|
||||
return libnet_rpc_userdel_recv(c, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* USER MODIFY functionality
|
||||
*/
|
||||
|
||||
static void usermod_handler(struct rpc_request*);
|
||||
|
||||
enum usermod_stage { USERMOD_LOOKUP, USERMOD_OPEN, USERMOD_QUERY, USERMOD_MODIFY };
|
||||
|
||||
struct usermod_state {
|
||||
enum usermod_stage stage;
|
||||
struct dcerpc_pipe *pipe;
|
||||
struct rpc_request *req;
|
||||
struct policy_handle domain_handle;
|
||||
struct policy_handle user_handle;
|
||||
struct usermod_change change;
|
||||
union samr_UserInfo info;
|
||||
struct samr_LookupNames lookupname;
|
||||
struct samr_OpenUser openuser;
|
||||
struct samr_SetUserInfo setuser;
|
||||
struct samr_QueryUserInfo queryuser;
|
||||
|
||||
/* information about the progress */
|
||||
void (*monitor_fn)(struct monitor_msg *);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Step 1: Lookup user name
|
||||
*/
|
||||
static NTSTATUS usermod_lookup(struct composite_context *c,
|
||||
struct usermod_state *s)
|
||||
{
|
||||
/* receive samr_LookupNames result */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* what to do when there's no user account to delete
|
||||
and what if there's more than one rid resolved */
|
||||
if (!s->lookupname.out.rids.count) {
|
||||
c->status = NT_STATUS_NO_SUCH_USER;
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
return c->status;
|
||||
|
||||
} else if (!s->lookupname.out.rids.count > 1) {
|
||||
c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
return c->status;
|
||||
}
|
||||
|
||||
/* prepare the next rpc call */
|
||||
s->openuser.in.domain_handle = &s->domain_handle;
|
||||
s->openuser.in.rid = s->lookupname.out.rids.ids[0];
|
||||
s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||||
s->openuser.out.user_handle = &s->user_handle;
|
||||
|
||||
/* send the rpc request */
|
||||
s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = usermod_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERMOD_OPEN;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Choose a proper level of samr_UserInfo structure depending on required
|
||||
* change specified by means of flags field. Subsequent calls of this
|
||||
* function are made until there's no flags set meaning that all of the
|
||||
* changes have been made.
|
||||
*/
|
||||
static uint32_t usermod_setfields(struct usermod_state *s, uint16_t *level,
|
||||
union samr_UserInfo *i)
|
||||
{
|
||||
if (s->change.fields == 0) return s->change.fields;
|
||||
|
||||
*level = 0;
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) &&
|
||||
(*level == 0 || *level == 7)) {
|
||||
*level = 7;
|
||||
i->info7.account_name.string = s->change.account_name;
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_FULL_NAME) &&
|
||||
(*level == 0 || *level == 8)) {
|
||||
*level = 8;
|
||||
i->info8.full_name.string = s->change.full_name;
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_FULL_NAME;
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_DESCRIPTION) &&
|
||||
(*level == 0 || *level == 13)) {
|
||||
*level = 13;
|
||||
i->info13.description.string = s->change.description;
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_DESCRIPTION;
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_COMMENT) &&
|
||||
(*level == 0 || *level == 2)) {
|
||||
*level = 2;
|
||||
|
||||
if (s->stage == USERMOD_QUERY) {
|
||||
/* the user info is obtained, so now set the required field */
|
||||
i->info2.comment.string = s->change.comment;
|
||||
s->change.fields ^= USERMOD_FIELD_COMMENT;
|
||||
|
||||
} else {
|
||||
/* we need to query the user info before setting one field in it */
|
||||
s->stage = USERMOD_QUERY;
|
||||
return s->change.fields;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) &&
|
||||
(*level == 0 || *level == 11)) {
|
||||
*level = 11;
|
||||
i->info11.logon_script.string = s->change.logon_script;
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_PROFILE_PATH) &&
|
||||
(*level == 0 || *level == 12)) {
|
||||
*level = 12;
|
||||
i->info12.profile_path.string = s->change.profile_path;
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_HOME_DIRECTORY) &&
|
||||
(*level == 0 || *level == 10)) {
|
||||
*level = 10;
|
||||
|
||||
if (s->stage == USERMOD_QUERY) {
|
||||
i->info10.home_directory.string = s->change.home_directory;
|
||||
s->change.fields ^= USERMOD_FIELD_HOME_DIRECTORY;
|
||||
} else {
|
||||
s->stage = USERMOD_QUERY;
|
||||
return s->change.fields;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_HOME_DRIVE) &&
|
||||
(*level == 0 || *level == 10)) {
|
||||
*level = 10;
|
||||
|
||||
if (s->stage == USERMOD_QUERY) {
|
||||
i->info10.home_drive.string = s->change.home_drive;
|
||||
s->change.fields ^= USERMOD_FIELD_HOME_DRIVE;
|
||||
} else {
|
||||
s->stage = USERMOD_QUERY;
|
||||
return s->change.fields;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) &&
|
||||
(*level == 0 || *level == 17)) {
|
||||
*level = 17;
|
||||
i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
|
||||
}
|
||||
|
||||
if ((s->change.fields & USERMOD_FIELD_ACCT_FLAGS) &&
|
||||
(*level == 0 || *level == 16)) {
|
||||
*level = 16;
|
||||
i->info16.acct_flags = s->change.acct_flags;
|
||||
|
||||
s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
|
||||
}
|
||||
|
||||
/* We're going to be here back again soon unless all fields have been set */
|
||||
if (s->change.fields) {
|
||||
s->stage = USERMOD_OPEN;
|
||||
} else {
|
||||
s->stage = USERMOD_MODIFY;
|
||||
}
|
||||
|
||||
return s->change.fields;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS usermod_change(struct composite_context *c,
|
||||
struct usermod_state *s)
|
||||
{
|
||||
union samr_UserInfo *i = &s->info;
|
||||
|
||||
/* set the level to invalid value, so that unless setfields routine
|
||||
gives it a valid value we report the error correctly */
|
||||
uint16_t level = 27;
|
||||
|
||||
/* prepare UserInfo level and data based on bitmask field */
|
||||
s->change.fields = usermod_setfields(s, &level, i);
|
||||
|
||||
if (level < 1 || level > 26) {
|
||||
/* apparently there's a field that the setfields routine
|
||||
does not know how to set */
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* If some specific level is used to set user account data and the change
|
||||
itself does not cover all fields then we need to query the user info
|
||||
first, right before changing the data. Otherwise we could set required
|
||||
fields and accidentally reset the others.
|
||||
*/
|
||||
if (s->stage == USERMOD_QUERY) {
|
||||
s->queryuser.in.user_handle = &s->user_handle;
|
||||
s->queryuser.in.level = level;
|
||||
|
||||
/* send query user info request to retrieve complete data of
|
||||
a particular info level */
|
||||
s->req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuser);
|
||||
|
||||
} else {
|
||||
s->setuser.in.user_handle = &s->user_handle;
|
||||
s->setuser.in.level = level;
|
||||
s->setuser.in.info = i;
|
||||
|
||||
/* send set user info request after making required change */
|
||||
s->req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
|
||||
}
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = usermod_handler;
|
||||
s->req->async.private = c;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 2: Open user account
|
||||
*/
|
||||
static NTSTATUS usermod_open(struct composite_context *c,
|
||||
struct usermod_state *s)
|
||||
{
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
return usermod_change(c, s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 2a (optional): Query the user information
|
||||
*/
|
||||
static NTSTATUS usermod_query(struct composite_context *c,
|
||||
struct usermod_state *s)
|
||||
{
|
||||
union samr_UserInfo *i = &s->info;
|
||||
uint16_t level;
|
||||
|
||||
/* receive samr_QueryUserInfo result */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
/* get returned user data and make a change (potentially one
|
||||
of many) */
|
||||
s->info = *s->queryuser.out.info;
|
||||
|
||||
s->change.fields = usermod_setfields(s, &level, i);
|
||||
|
||||
/* prepare rpc call arguments */
|
||||
s->setuser.in.user_handle = &s->user_handle;
|
||||
s->setuser.in.level = level;
|
||||
s->setuser.in.info = i;
|
||||
|
||||
/* send the rpc request */
|
||||
s->req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = usermod_handler;
|
||||
s->req->async.private = c;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stage 3: Set new user account data
|
||||
*/
|
||||
static NTSTATUS usermod_modify(struct composite_context *c,
|
||||
struct usermod_state *s)
|
||||
{
|
||||
/* receive samr_SetUserInfo result */
|
||||
c->status = dcerpc_ndr_request_recv(s->req);
|
||||
NT_STATUS_NOT_OK_RETURN(c->status);
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(s->setuser.out.result);
|
||||
|
||||
if (s->change.fields == 0) {
|
||||
/* all fields have been set - we're done */
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
} else {
|
||||
/* something's still not changed - repeat the procedure */
|
||||
return usermod_change(c, s);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for asynchronous request. Handles transition through
|
||||
* intermediate stages of the call.
|
||||
*
|
||||
* @param req rpc call context
|
||||
*/
|
||||
|
||||
static void usermod_handler(struct rpc_request *req)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct usermod_state *s;
|
||||
struct monitor_msg msg;
|
||||
struct msg_rpc_lookup_name *msg_lookup;
|
||||
struct msg_rpc_open_user *msg_open;
|
||||
|
||||
c = talloc_get_type(req->async.private, struct composite_context);
|
||||
s = talloc_get_type(c->private_data, struct usermod_state);
|
||||
|
||||
switch (s->stage) {
|
||||
case USERMOD_LOOKUP:
|
||||
c->status = usermod_lookup(c, s);
|
||||
|
||||
if (NT_STATUS_IS_OK(c->status)) {
|
||||
/* monitor message */
|
||||
msg.type = rpc_lookup_name;
|
||||
msg_lookup = talloc(s, struct msg_rpc_lookup_name);
|
||||
|
||||
msg_lookup->rid = s->lookupname.out.rids.ids;
|
||||
msg_lookup->count = s->lookupname.out.rids.count;
|
||||
msg.data = (void*)msg_lookup;
|
||||
msg.data_size = sizeof(*msg_lookup);
|
||||
}
|
||||
break;
|
||||
|
||||
case USERMOD_OPEN:
|
||||
c->status = usermod_open(c, s);
|
||||
|
||||
if (NT_STATUS_IS_OK(c->status)) {
|
||||
/* monitor message */
|
||||
msg.type = rpc_open_user;
|
||||
msg_open = talloc(s, struct msg_rpc_open_user);
|
||||
|
||||
msg_open->rid = s->openuser.in.rid;
|
||||
msg_open->access_mask = s->openuser.in.rid;
|
||||
msg.data = (void*)msg_open;
|
||||
msg.data_size = sizeof(*msg_open);
|
||||
}
|
||||
break;
|
||||
|
||||
case USERMOD_QUERY:
|
||||
c->status = usermod_query(c, s);
|
||||
|
||||
if (NT_STATUS_IS_OK(c->status)) {
|
||||
/* monitor message */
|
||||
msg.type = rpc_query_user;
|
||||
msg.data = NULL;
|
||||
msg.data_size = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case USERMOD_MODIFY:
|
||||
c->status = usermod_modify(c, s);
|
||||
|
||||
if (NT_STATUS_IS_OK(c->status)) {
|
||||
/* monitor message */
|
||||
msg.type = rpc_set_user;
|
||||
msg.data = NULL;
|
||||
msg.data_size = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* are we ok, so far ? */
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
}
|
||||
|
||||
/* call monitor function provided the pointer has been passed */
|
||||
if (s->monitor_fn) {
|
||||
s->monitor_fn(&msg);
|
||||
}
|
||||
|
||||
/* are we done yet ? */
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends asynchronous usermod request
|
||||
*
|
||||
* @param p dce/rpc call pipe
|
||||
* @param io arguments and results of the call
|
||||
* @param monitor monitor function for providing information about the progress
|
||||
*/
|
||||
|
||||
struct composite_context *libnet_rpc_usermod_send(struct dcerpc_pipe *p,
|
||||
struct libnet_rpc_usermod *io,
|
||||
void (*monitor)(struct monitor_msg*))
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct usermod_state *s;
|
||||
|
||||
/* composite context allocation and setup */
|
||||
c = talloc_zero(p, struct composite_context);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
s = talloc_zero(c, struct usermod_state);
|
||||
if (composite_nomem(s, c)) return c;
|
||||
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->private_data = s;
|
||||
c->event_ctx = dcerpc_event_context(p);
|
||||
|
||||
/* store parameters in the call structure */
|
||||
s->pipe = p;
|
||||
s->domain_handle = io->in.domain_handle;
|
||||
s->change = io->in.change;
|
||||
s->monitor_fn = monitor;
|
||||
|
||||
/* prepare rpc call arguments */
|
||||
s->lookupname.in.domain_handle = &io->in.domain_handle;
|
||||
s->lookupname.in.num_names = 1;
|
||||
s->lookupname.in.names = talloc_zero(s, struct lsa_String);
|
||||
s->lookupname.in.names->string = io->in.username;
|
||||
|
||||
/* send the rpc request */
|
||||
s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
|
||||
|
||||
/* callback handler setup */
|
||||
s->req->async.callback = usermod_handler;
|
||||
s->req->async.private = c;
|
||||
s->stage = USERMOD_LOOKUP;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for and receives results of asynchronous usermod call
|
||||
*
|
||||
* @param c composite context returned by asynchronous usermod call
|
||||
* @param mem_ctx memory context of the call
|
||||
* @param io pointer to results (and arguments) of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_usermod *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronous version of usermod call
|
||||
*
|
||||
* @param pipe dce/rpc call pipe
|
||||
* @param mem_ctx memory context for the call
|
||||
* @param io arguments and results of the call
|
||||
* @return nt status code of execution
|
||||
*/
|
||||
|
||||
NTSTATUS libnet_rpc_usermod(struct dcerpc_pipe *p,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct libnet_rpc_usermod *io)
|
||||
{
|
||||
struct composite_context *c = libnet_rpc_usermod_send(p, io, NULL);
|
||||
return libnet_rpc_usermod_recv(c, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Rafal Szczesniak 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.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* IO structures for userman.c functions
|
||||
*/
|
||||
|
||||
struct libnet_rpc_useradd {
|
||||
struct {
|
||||
struct policy_handle domain_handle;
|
||||
const char *username;
|
||||
} in;
|
||||
struct {
|
||||
struct policy_handle user_handle;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
struct libnet_rpc_userdel {
|
||||
struct {
|
||||
struct policy_handle domain_handle;
|
||||
const char *username;
|
||||
} in;
|
||||
struct {
|
||||
struct policy_handle user_handle;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
#define USERMOD_FIELD_ACCOUNT_NAME ( 0x00000001 )
|
||||
#define USERMOD_FIELD_FULL_NAME ( 0x00000002 )
|
||||
#define USERMOD_FIELD_DESCRIPTION ( 0x00000010 )
|
||||
#define USERMOD_FIELD_COMMENT ( 0x00000020 )
|
||||
#define USERMOD_FIELD_HOME_DIRECTORY ( 0x00000040 )
|
||||
#define USERMOD_FIELD_HOME_DRIVE ( 0x00000080 )
|
||||
#define USERMOD_FIELD_LOGON_SCRIPT ( 0x00000100 )
|
||||
#define USERMOD_FIELD_PROFILE_PATH ( 0x00000200 )
|
||||
#define USERMOD_FIELD_WORKSTATIONS ( 0x00000400 )
|
||||
#define USERMOD_FIELD_LOGON_HOURS ( 0x00002000 )
|
||||
#define USERMOD_FIELD_ACCT_EXPIRY ( 0x00004000 )
|
||||
#define USERMOD_FIELD_ACCT_FLAGS ( 0x00100000 )
|
||||
#define USERMOD_FIELD_PARAMETERS ( 0x00200000 )
|
||||
#define USERMOD_FIELD_COUNTRY_CODE ( 0x00400000 )
|
||||
#define USERMOD_FIELD_CODE_PAGE ( 0x00800000 )
|
||||
|
||||
struct libnet_rpc_usermod {
|
||||
struct {
|
||||
struct policy_handle domain_handle;
|
||||
const char *username;
|
||||
|
||||
struct usermod_change {
|
||||
uint32_t fields; /* bitmask field */
|
||||
|
||||
const char *account_name;
|
||||
const char *full_name;
|
||||
const char *description;
|
||||
const char *comment;
|
||||
const char *logon_script;
|
||||
const char *profile_path;
|
||||
const char *home_directory;
|
||||
const char *home_drive;
|
||||
const char *workstations;
|
||||
struct timeval *acct_expiry;
|
||||
struct timeval *allow_password_change;
|
||||
struct timeval *force_password_change;
|
||||
struct timeval *last_logon;
|
||||
struct timeval *last_logoff;
|
||||
struct timeval *last_password_change;
|
||||
uint32_t acct_flags;
|
||||
} change;
|
||||
} in;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Monitor messages sent from userman.c functions
|
||||
*/
|
||||
|
||||
struct msg_rpc_create_user {
|
||||
uint32_t rid;
|
||||
};
|
||||
|
||||
|
||||
struct msg_rpc_lookup_name {
|
||||
uint32_t *rid;
|
||||
uint32_t count;
|
||||
};
|
||||
Reference in New Issue
Block a user