/*
Unix SMB/CIFS implementation.
- Copyright (C) Rafal Szczesniak <mimir@samba.org> 2005
+ 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
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "auth/credentials/credentials.h"
#include "librpc/ndr/libndr.h"
#include "librpc/gen_ndr/samr.h"
-#include "librpc/gen_ndr/ndr_samr.h"
-
-
-/**
- * Verify, before actually doing anything with user accounts, whether
- * required domain is already opened and thus ready for operation.
- * If it is not, or if the opened domain is not the one requested, open
- * the requested domain.
- */
-static struct composite_context* 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.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.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;
-}
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
struct create_user_state {
struct composite_context *c;
struct create_user_state *s;
struct composite_context *create_req;
- struct composite_context *prereq_ctx;
+ bool prereq_met = false;
/* composite context allocation and setup */
- c = talloc_zero(mem_ctx, struct composite_context);
+ c = composite_create(mem_ctx, ctx->event_ctx);
if (c == NULL) return NULL;
s = talloc_zero(c, struct create_user_state);
if (composite_nomem(s, c)) return c;
- c->state = COMPOSITE_STATE_IN_PROGRESS;
c->private_data = s;
- c->event_ctx = ctx->event_ctx;
/* store arguments in the state structure */
s->ctx = ctx;
ZERO_STRUCT(s->r.out);
/* prerequisite: make sure the domain is opened */
- prereq_ctx = domain_opened(ctx, s->r.in.domain_name, c, &s->domain_open,
- continue_domain_open_create, monitor);
- if (prereq_ctx) return prereq_ctx;
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_create, monitor);
+ if (!prereq_met) return c;
/* prepare arguments for useradd call */
s->user_add.in.username = r->in.user_name;
s->user_add.in.domain_handle = ctx->samr.handle;
/* send the request */
- create_req = libnet_rpc_useradd_send(ctx->samr.pipe, &s->user_add, monitor);
+ create_req = libnet_rpc_useradd_send(ctx->samr.pipe, s, &s->user_add, monitor);
if (composite_nomem(create_req, c)) return c;
/* set the next stage */
struct composite_context *create_req;
struct monitor_msg msg;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct create_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct create_user_state);
/* receive result of DomainOpen call */
c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
s->user_add.in.domain_handle = s->ctx->samr.handle;
/* send the request */
- create_req = libnet_rpc_useradd_send(s->ctx->samr.pipe, &s->user_add, s->monitor_fn);
+ create_req = libnet_rpc_useradd_send(s->ctx->samr.pipe, s, &s->user_add, s->monitor_fn);
if (composite_nomem(create_req, c)) return;
/* set the next stage */
struct create_user_state *s;
struct monitor_msg msg;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct create_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct create_user_state);
/* receive result of the call */
c->status = libnet_rpc_useradd_recv(ctx, c, &s->user_add);
struct libnet_CreateUser *r)
{
NTSTATUS status;
- struct create_user_state *s;
r->out.error_string = NULL;
/* wait for result of async request and check status code */
status = composite_wait(c);
if (!NT_STATUS_IS_OK(status)) {
- s = talloc_get_type(c->private_data, struct create_user_state);
r->out.error_string = talloc_strdup(mem_ctx, nt_errstr(status));
}
+ talloc_free(c);
return status;
}
struct composite_context *c;
struct delete_user_state *s;
struct composite_context *delete_req;
- struct composite_context *prereq_ctx;
+ bool prereq_met = false;
/* composite context allocation and setup */
- c = talloc_zero(mem_ctx, struct composite_context);
+ c = composite_create(mem_ctx, ctx->event_ctx);
if (c == NULL) return NULL;
s = talloc_zero(c, struct delete_user_state);
if (composite_nomem(s, c)) return c;
c->private_data = s;
- c->state = COMPOSITE_STATE_IN_PROGRESS;
- c->event_ctx = ctx->event_ctx;
/* store arguments in state structure */
s->ctx = ctx;
ZERO_STRUCT(s->r.out);
/* prerequisite: make sure the domain is opened before proceeding */
- prereq_ctx = domain_opened(ctx, s->r.in.domain_name, c, &s->domain_open,
- continue_domain_open_delete, monitor);
- if (prereq_ctx) return prereq_ctx;
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_delete, monitor);
+ if (!prereq_met) return c;
/* prepare arguments for userdel call */
s->user_del.in.username = r->in.user_name;
struct composite_context *delete_req;
struct monitor_msg msg;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct delete_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct delete_user_state);
/* receive result of DomainOpen call */
c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
}
+/*
+ * Stage 1: receive result of userdel call and finish the composite function
+ */
static void continue_rpc_userdel(struct composite_context *ctx)
{
struct composite_context *c;
struct delete_user_state *s;
struct monitor_msg msg;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct delete_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct delete_user_state);
/* receive result of userdel call */
c->status = libnet_rpc_userdel_recv(ctx, c, &s->user_del);
/* wait for result of async request and check status code */
status = composite_wait(c);
if (!NT_STATUS_IS_OK(status)) {
- s = talloc_get_type(c->private_data, struct delete_user_state);
+ s = talloc_get_type_abort(c->private_data, struct delete_user_state);
r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
}
+ talloc_free(c);
return status;
}
static void continue_rpc_userinfo(struct composite_context *ctx);
+/**
+ * Sends request to modify user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @param monitor function pointer for receiving monitor messages
+ */
struct composite_context *libnet_ModifyUser_send(struct libnet_context *ctx,
TALLOC_CTX *mem_ctx,
struct libnet_ModifyUser *r,
void (*monitor)(struct monitor_msg*))
{
+ const uint16_t level = 21;
struct composite_context *c;
struct modify_user_state *s;
- struct composite_context *prereq_ctx;
struct composite_context *userinfo_req;
+ bool prereq_met = false;
- c = talloc_zero(mem_ctx, struct composite_context);
+ c = composite_create(mem_ctx, ctx->event_ctx);
if (c == NULL) return NULL;
s = talloc_zero(c, struct modify_user_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;
- prereq_ctx = domain_opened(ctx, s->r.in.domain_name, c, &s->domain_open,
- continue_domain_open_modify, monitor);
- if (prereq_ctx) return prereq_ctx;
+ prereq_met = samr_domain_opened(ctx, c, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_modify, monitor);
+ if (!prereq_met) return c;
- s->user_mod.in.username = r->in.user_name;
- s->user_mod.in.domain_handle = ctx->samr.handle;
+ s->user_info.in.username = r->in.user_name;
+ s->user_info.in.domain_handle = ctx->samr.handle;
+ s->user_info.in.level = level;
- userinfo_req = libnet_rpc_userinfo_send(ctx->samr.pipe, &s->user_info, monitor);
+ userinfo_req = libnet_rpc_userinfo_send(ctx->samr.pipe, s, &s->user_info, monitor);
if (composite_nomem(userinfo_req, c)) return c;
composite_continue(c, userinfo_req, continue_rpc_userinfo, c);
}
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send userinfo request
+ */
static void continue_domain_open_modify(struct composite_context *ctx)
{
const uint16_t level = 21;
struct composite_context *userinfo_req;
struct monitor_msg msg;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct modify_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct modify_user_state);
c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
if (!composite_is_ok(c)) return;
s->user_info.in.username = s->r.in.user_name;
s->user_info.in.level = level;
- userinfo_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe, &s->user_info, s->monitor_fn);
+ userinfo_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe, s, &s->user_info, s->monitor_fn);
if (composite_nomem(userinfo_req, c)) return;
composite_continue(c, userinfo_req, continue_rpc_userinfo, c);
}
+/*
+ * Stage 1: receive result of userinfo call, prepare user changes
+ * (set the fields a caller required to change) and send usermod request
+ */
static void continue_rpc_userinfo(struct composite_context *ctx)
{
struct composite_context *c;
struct modify_user_state *s;
struct composite_context *usermod_req;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct modify_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct modify_user_state);
c->status = libnet_rpc_userinfo_recv(ctx, c, &s->user_info);
if (!composite_is_ok(c)) return;
}
+/*
+ * Prepare user changes: compare userinfo result to requested changes and
+ * set the field values and flags accordingly for user modify call
+ */
static NTSTATUS set_user_changes(TALLOC_CTX *mem_ctx, struct usermod_change *mod,
struct libnet_rpc_userinfo *info, struct libnet_ModifyUser *r)
{
SET_FIELD_LSA_STRING(r->in, user, mod, full_name, USERMOD_FIELD_FULL_NAME);
/* description change */
- SET_FIELD_LSA_STRING(r->in, user, mod, comment, USERMOD_FIELD_DESCRIPTION);
+ SET_FIELD_LSA_STRING(r->in, user, mod, description, USERMOD_FIELD_DESCRIPTION);
/* comment change */
SET_FIELD_LSA_STRING(r->in, user, mod, comment, USERMOD_FIELD_COMMENT);
/* profile path change */
SET_FIELD_LSA_STRING(r->in, user, mod, profile_path, USERMOD_FIELD_PROFILE_PATH);
- /* allow password change time */
- SET_FIELD_NTTIME(r->in, user, mod, allow_password_change, USERMOD_FIELD_ALLOW_PASS_CHG);
-
- /* force password change time */
- SET_FIELD_NTTIME(r->in, user, mod, force_password_change, USERMOD_FIELD_FORCE_PASS_CHG);
-
/* account expiry change */
SET_FIELD_NTTIME(r->in, user, mod, acct_expiry, USERMOD_FIELD_ACCT_EXPIRY);
+ /* account flags change */
+ SET_FIELD_ACCT_FLAGS(r->in, user, mod, acct_flags, USERMOD_FIELD_ACCT_FLAGS);
+
return NT_STATUS_OK;
}
+/*
+ * Stage 2: receive result of usermod request and finish the composite function
+ */
static void continue_rpc_usermod(struct composite_context *ctx)
{
struct composite_context *c;
struct modify_user_state *s;
struct monitor_msg msg;
- c = talloc_get_type(ctx->async.private_data, struct composite_context);
- s = talloc_get_type(c->private_data, struct modify_user_state);
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct modify_user_state);
c->status = libnet_rpc_usermod_recv(ctx, c, &s->user_mod);
if (!composite_is_ok(c)) return;
}
+/**
+ * Receive result of ModifyUser call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
NTSTATUS libnet_ModifyUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
struct libnet_ModifyUser *r)
{
NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
return status;
}
+/**
+ * Synchronous version of ModifyUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
NTSTATUS libnet_ModifyUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
struct libnet_ModifyUser *r)
{
c = libnet_ModifyUser_send(ctx, mem_ctx, r, NULL);
return libnet_ModifyUser_recv(c, mem_ctx, r);
}
+
+
+struct user_info_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ enum libnet_UserInfo_level level;
+ const char *user_name;
+ const char *sid_string;
+ struct libnet_LookupName lookup;
+ struct libnet_DomainOpen domopen;
+ struct libnet_rpc_userinfo userinfo;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_name_found(struct composite_context *ctx);
+static void continue_domain_open_info(struct composite_context *ctx);
+static void continue_info_received(struct composite_context *ctx);
+
+
+/**
+ * Sends request to get user account information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_UserInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct user_info_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+ s->domain_name = talloc_strdup(c, r->in.domain_name);
+ s->level = r->in.level;
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ s->user_name = talloc_strdup(c, r->in.data.user_name);
+ s->sid_string = NULL;
+ break;
+ case USER_INFO_BY_SID:
+ s->user_name = NULL;
+ s->sid_string = dom_sid_string(c, r->in.data.user_sid);
+ break;
+ }
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, c, s->domain_name, &c, &s->domopen,
+ continue_domain_open_info, monitor);
+ if (!prereq_met) return c;
+
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.domain_name = s->domain_name;
+ s->lookup.in.name = s->user_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(ctx, c, &s->lookup,
+ s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case USER_INFO_BY_SID:
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->sid_string;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe,
+ s,
+ &s->userinfo,
+ s->monitor_fn);
+ if (composite_nomem(info_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+ break;
+ }
+
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send LookupName request
+ */
+static void continue_domain_open_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.domain_name = s->domain_name;
+ s->lookup.in.name = s->user_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+
+ case USER_INFO_BY_SID:
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->sid_string;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe,
+ s,
+ &s->userinfo,
+ s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+ break;
+ }
+}
+
+
+/*
+ * Stage 1: receive the name (if found) and send userinfo request
+ */
+static void continue_name_found(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *info_req;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+
+ /* receive result of LookupName call */
+ c->status = libnet_LookupName_recv(ctx, c, &s->lookup);
+ if (!composite_is_ok(c)) return;
+
+ /* we're only interested in user accounts this time */
+ if (s->lookup.out.sid_type != SID_NAME_USER) {
+ composite_error(c, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->lookup.out.sidstr;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe, s, &s->userinfo, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+}
+
+
+/*
+ * Stage 2: receive user account information and finish the composite function
+ */
+static void continue_info_received(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+
+ /* receive result of userinfo call */
+ c->status = libnet_rpc_userinfo_recv(ctx, c, &s->userinfo);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of UserInfo call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserInfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r)
+{
+ NTSTATUS status;
+ struct user_info_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && r != NULL) {
+ struct samr_UserInfo21 *info;
+
+ s = talloc_get_type_abort(c->private_data, struct user_info_state);
+ info = &s->userinfo.out.info.info21;
+
+ r->out.user_sid = dom_sid_add_rid(mem_ctx, s->ctx->samr.sid, info->rid);
+ r->out.primary_group_sid = dom_sid_add_rid(mem_ctx, s->ctx->samr.sid, info->primary_gid);
+
+ /* string fields */
+ r->out.account_name = talloc_steal(mem_ctx, info->account_name.string);
+ r->out.full_name = talloc_steal(mem_ctx, info->full_name.string);
+ r->out.description = talloc_steal(mem_ctx, info->description.string);
+ r->out.home_directory = talloc_steal(mem_ctx, info->home_directory.string);
+ r->out.home_drive = talloc_steal(mem_ctx, info->home_drive.string);
+ r->out.comment = talloc_steal(mem_ctx, info->comment.string);
+ r->out.logon_script = talloc_steal(mem_ctx, info->logon_script.string);
+ r->out.profile_path = talloc_steal(mem_ctx, info->profile_path.string);
+
+ /* time fields (allocation) */
+ r->out.acct_expiry = talloc(mem_ctx, struct timeval);
+ r->out.allow_password_change = talloc(mem_ctx, struct timeval);
+ r->out.force_password_change = talloc(mem_ctx, struct timeval);
+ r->out.last_logon = talloc(mem_ctx, struct timeval);
+ r->out.last_logoff = talloc(mem_ctx, struct timeval);
+ r->out.last_password_change = talloc(mem_ctx, struct timeval);
+
+ /* time fields (converting) */
+ nttime_to_timeval(r->out.acct_expiry, info->acct_expiry);
+ nttime_to_timeval(r->out.allow_password_change, info->allow_password_change);
+ nttime_to_timeval(r->out.force_password_change, info->force_password_change);
+ nttime_to_timeval(r->out.last_logon, info->last_logon);
+ nttime_to_timeval(r->out.last_logoff, info->last_logoff);
+ nttime_to_timeval(r->out.last_password_change, info->last_password_change);
+
+ /* flag and number fields */
+ r->out.acct_flags = info->acct_flags;
+
+ 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;
+}
+
+
+/**
+ * Synchronous version of UserInfo call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserInfo(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r)
+{
+ struct composite_context *c;
+
+ c = libnet_UserInfo_send(ctx, mem_ctx, r, NULL);
+ return libnet_UserInfo_recv(c, mem_ctx, r);
+}
+
+
+struct userlist_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ struct lsa_DomainInfo dominfo;
+ int page_size;
+ uint32_t resume_index;
+ struct userlist *users;
+ uint32_t count;
+
+ struct libnet_DomainOpen domain_open;
+ struct lsa_QueryInfoPolicy query_domain;
+ struct samr_EnumDomainUsers user_list;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_domain_opened(struct composite_context *ctx);
+static void continue_domain_queried(struct tevent_req *subreq);
+static void continue_samr_domain_opened(struct composite_context *ctx);
+static void continue_users_enumerated(struct tevent_req *subreq);
+
+
+/**
+ * Sends request to list (enumerate) user accounts
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_UserList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct tevent_req *subreq;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct userlist_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store the arguments in the state structure */
+ s->ctx = ctx;
+ s->page_size = r->in.page_size;
+ s->resume_index = r->in.resume_index;
+ s->domain_name = talloc_strdup(c, r->in.domain_name);
+ s->monitor_fn = monitor;
+
+ /* make sure we have lsa domain handle before doing anything */
+ prereq_met = lsa_domain_opened(ctx, c, s->domain_name, &c, &s->domain_open,
+ continue_lsa_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return c;
+
+ /* send the request */
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ ctx->lsa.pipe->binding_handle,
+ &s->query_domain);
+ if (composite_nomem(subreq, c)) return c;
+
+ tevent_req_set_callback(subreq, continue_domain_queried, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive lsa domain handle and send
+ * request to query domain info
+ */
+static void continue_lsa_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive lsa domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &s->ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_lsa_QueryInfoPolicy_r_send(s, c->event_ctx,
+ s->ctx->lsa.pipe->binding_handle,
+ &s->query_domain);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_domain_queried, c);
+}
+
+
+/*
+ * Stage 1: receive domain info and request to enum users,
+ * provided a valid samr handle is opened
+ */
+static void continue_domain_queried(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ bool prereq_met = false;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_lsa_QueryInfoPolicy_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* get the returned domain info */
+ s->dominfo = (*s->query_domain.out.info)->domain;
+
+ /* make sure we have samr domain handle before continuing */
+ prereq_met = samr_domain_opened(s->ctx, c, s->domain_name, &c, &s->domain_open,
+ continue_samr_domain_opened, s->monitor_fn);
+ if (!prereq_met) return;
+
+ /* prepare arguments of EnumDomainUsers call */
+ s->user_list.in.domain_handle = &s->ctx->samr.handle;
+ s->user_list.in.max_size = s->page_size;
+ s->user_list.in.resume_handle = &s->resume_index;
+ s->user_list.in.acct_flags = ACB_NORMAL;
+ s->user_list.out.resume_handle = &s->resume_index;
+ s->user_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->user_list.out.num_entries, c)) return;
+ s->user_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->user_list.out.sam, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomainUsers_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->user_list);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_users_enumerated, c);
+}
+
+
+/*
+ * Stage 1.5 (optional): receive samr domain handle
+ * and request to enumerate accounts
+ */
+static void continue_samr_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct tevent_req *subreq;
+
+ c = talloc_get_type_abort(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive samr domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of EnumDomainUsers call */
+ s->user_list.in.domain_handle = &s->ctx->samr.handle;
+ s->user_list.in.max_size = s->page_size;
+ s->user_list.in.resume_handle = &s->resume_index;
+ s->user_list.in.acct_flags = ACB_NORMAL;
+ s->user_list.out.resume_handle = &s->resume_index;
+ s->user_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->user_list.out.sam, c)) return;
+ s->user_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->user_list.out.num_entries, c)) return;
+
+ /* send the request */
+ subreq = dcerpc_samr_EnumDomainUsers_r_send(s, c->event_ctx,
+ s->ctx->samr.pipe->binding_handle,
+ &s->user_list);
+ if (composite_nomem(subreq, c)) return;
+
+ tevent_req_set_callback(subreq, continue_users_enumerated, c);
+}
+
+
+/*
+ * Stage 2: receive enumerated users and their rids
+ */
+static void continue_users_enumerated(struct tevent_req *subreq)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ uint32_t i;
+
+ c = tevent_req_callback_data(subreq, struct composite_context);
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_samr_EnumDomainUsers_r_recv(subreq, s);
+ TALLOC_FREE(subreq);
+ if (!composite_is_ok(c)) return;
+
+ /* get the actual status of the rpc call result
+ (instead of rpc layer status) */
+ c->status = s->user_list.out.result;
+
+ /* we're interested in status "ok" as well as two
+ enum-specific status codes */
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ /* get enumerated accounts counter and resume handle (the latter allows
+ making subsequent call to continue enumeration) */
+ s->resume_index = *s->user_list.out.resume_handle;
+ s->count = *s->user_list.out.num_entries;
+
+ /* prepare returned user accounts array */
+ s->users = talloc_array(c, struct userlist, (*s->user_list.out.sam)->count);
+ if (composite_nomem(s->users, c)) return;
+
+ for (i = 0; i < (*s->user_list.out.sam)->count; i++) {
+ struct dom_sid *user_sid;
+ struct samr_SamEntry *entry = &(*s->user_list.out.sam)->entries[i];
+ struct dom_sid *domain_sid = (*s->query_domain.out.info)->domain.sid;
+
+ /* construct user sid from returned rid and queried domain sid */
+ user_sid = dom_sid_add_rid(c, domain_sid, entry->idx);
+ if (composite_nomem(user_sid, c)) return;
+
+ /* username */
+ s->users[i].username = talloc_strdup(s->users, entry->name.string);
+ if (composite_nomem(s->users[i].username, c)) return;
+
+ /* sid string */
+ s->users[i].sid = dom_sid_string(s->users, user_sid);
+ if (composite_nomem(s->users[i].sid, c)) return;
+ }
+
+ /* that's it */
+ composite_done(c);
+ return;
+
+ } else {
+ /* something went wrong */
+ composite_error(c, c->status);
+ return;
+ }
+}
+
+
+/**
+ * Receive result of UserList call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserList_recv(struct composite_context* c, TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r)
+{
+ NTSTATUS status;
+ struct userlist_state *s;
+
+ if (c == NULL || mem_ctx == NULL || r == NULL) {
+ talloc_free(c);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ s = talloc_get_type_abort(c->private_data, struct userlist_state);
+
+ /* get results from composite context */
+ r->out.count = s->count;
+ r->out.resume_index = s->resume_index;
+ r->out.users = talloc_steal(mem_ctx, s->users);
+
+ if (NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+ } else {
+ /* success, but we're not done yet */
+ r->out.error_string = talloc_asprintf(mem_ctx, "Success (status: %s)",
+ nt_errstr(status));
+ }
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of UserList call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserList(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r)
+{
+ struct composite_context *c;
+
+ c = libnet_UserList_send(ctx, mem_ctx, r, NULL);
+ return libnet_UserList_recv(c, mem_ctx, r);
+}