summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/include/structs.h1
-rw-r--r--source4/libnet/config.mk1
-rw-r--r--source4/libnet/libnet_samsync_ldb.c414
-rw-r--r--source4/libnet/libnet_vampire.h10
-rw-r--r--source4/utils/net/net.c1
-rw-r--r--source4/utils/net/net_vampire.c40
6 files changed, 467 insertions, 0 deletions
diff --git a/source4/include/structs.h b/source4/include/structs.h
index 88f7311cc3..01b014a564 100644
--- a/source4/include/structs.h
+++ b/source4/include/structs.h
@@ -180,6 +180,7 @@ struct libnet_DelShare;
struct libnet_Lookup;
struct libnet_SamDump;
struct libnet_SamSync;
+struct libnet_samsync_ldb;
struct net_functable;
struct net_context;
diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk
index 1f85d81ea2..8d5f5dfccd 100644
--- a/source4/libnet/config.mk
+++ b/source4/libnet/config.mk
@@ -10,6 +10,7 @@ ADD_OBJ_FILES = \
libnet/libnet_join.o \
libnet/libnet_vampire.o \
libnet/libnet_samdump.o \
+ libnet/libnet_samsync_ldb.o \
libnet/libnet_user.o \
libnet/libnet_share.o \
libnet/libnet_lookup.o \
diff --git a/source4/libnet/libnet_samsync_ldb.c b/source4/libnet/libnet_samsync_ldb.c
new file mode 100644
index 0000000000..cedb1e233e
--- /dev/null
+++ b/source4/libnet/libnet_samsync_ldb.c
@@ -0,0 +1,414 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract the user/system database from a remote SamSync server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "dlinklist.h"
+#include "lib/ldb/include/ldb.h"
+
+struct samsync_ldb_secret {
+ struct samsync_ldb_secret *prev, *next;
+ DATA_BLOB secret;
+ char *name;
+ NTTIME mtime;
+};
+
+struct samsync_ldb_trusted_domain {
+ struct samsync_ldb_trusted_domain *prev, *next;
+ struct dom_sid *sid;
+ char *name;
+};
+
+struct samsync_ldb_state {
+ struct dom_sid *dom_sid[3];
+ struct ldb_context *sam_ldb;
+ char *base_dn[3];
+ struct samsync_ldb_secret *secrets;
+ struct samsync_ldb_trusted_domain *trusted_domains;
+};
+
+static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ struct creds_CredentialState *creds,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
+ const char *domain_name = domain->domain_name.string;
+ struct ldb_message *msg;
+ int ret;
+
+ if (database == SAM_DATABASE_DOMAIN) {
+ const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
+ struct ldb_message **msgs_domain;
+ int ret_domain;
+ ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
+ "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
+ domain_name);
+ if (ret_domain == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret_domain != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->base_dn[database]
+ = talloc_steal(state, samdb_result_string(msgs_domain[0],
+ "nCName", NULL));
+
+ state->dom_sid[database]
+ = talloc_steal(state,
+ samdb_search_dom_sid(state->sam_ldb, state,
+ state->base_dn[database], "objectSid",
+ "dn=%s", state->base_dn[database]));
+ } else if (database == SAM_DATABASE_BUILTIN) {
+ /* work out the builtin_dn - useful for so many calls its worth
+ fetching here */
+ state->base_dn[database]
+ = talloc_steal(state,
+ samdb_search_string(state->sam_ldb, mem_ctx, NULL,
+ "dn", "objectClass=builtinDomain"));
+ state->dom_sid[database]
+ = dom_sid_parse_talloc(state, SID_BUILTIN);
+ } else {
+ /* PRIVs DB */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(state->sam_ldb, mem_ctx,
+ msg, "oEMInformation", domain->comment.string);
+
+ samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
+ msg, "forceLogff", domain->force_logoff_time);
+
+ samdb_msg_add_uint(state->sam_ldb, mem_ctx,
+ msg, "minPwdLen", domain->min_password_length);
+
+ samdb_msg_add_int64(state->sam_ldb, mem_ctx,
+ msg, "maxPwdAge", domain->max_password_age);
+
+ samdb_msg_add_int64(state->sam_ldb, mem_ctx,
+ msg, "minPwdAge", domain->min_password_age);
+
+ samdb_msg_add_uint(state->sam_ldb, mem_ctx,
+ msg, "pwdHistoryLength", domain->password_history_length);
+
+ samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
+ msg, "modifiedCountAtLastProm",
+ domain->sequence_num);
+
+ samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
+ msg, "creationTime", domain->domain_create_time);
+
+ /* TODO: Account lockout, password properties */
+
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+
+ if (ret) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ struct creds_CredentialState *creds,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_USER *user = delta->delta_union.user;
+ const char *container, *obj_class;
+ char *cn_name;
+ int cn_name_len;
+
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ int ret;
+ uint32_t acb;
+ BOOL add = False;
+ const char *attrs[] = { NULL };
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the user, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=user)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ add = True;
+ } else if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
+ }
+
+
+ cn_name = talloc_strdup(mem_ctx, user->account_name.string);
+ NT_STATUS_HAVE_NO_MEMORY(cn_name);
+ cn_name_len = strlen(cn_name);
+
+#define ADD_OR_DEL(type, attrib, field) do {\
+ if (user->field) { \
+ samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
+ attrib, user->field); \
+ } else if (!add) { \
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
+ attrib); \
+ } \
+ } while (0);
+
+ ADD_OR_DEL(string, "samAccountName", account_name.string);
+ ADD_OR_DEL(string, "displayName", full_name.string);
+
+ if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
+ "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
+ ADD_OR_DEL(string, "homeDirectory", home_directory.string);
+ ADD_OR_DEL(string, "homeDrive", home_drive.string);
+ ADD_OR_DEL(string, "scriptPath", logon_script.string);
+ ADD_OR_DEL(string, "description", description.string);
+ ADD_OR_DEL(string, "userWorkstations", workstations.string);
+
+ ADD_OR_DEL(uint64, "lastLogon", last_logon);
+ ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
+
+ /* TODO: Logon hours */
+
+ ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
+ ADD_OR_DEL(uint, "logonCount", logon_count);
+
+ ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
+ ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
+
+ if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
+ "userAccountConrol", user->acct_flags) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Passwords */
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "unicodePwd");
+ if (user->lm_password_present) {
+ samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
+ "lmPwdHash", &user->lmpassword);
+ } else {
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "lmPwdHash");
+ }
+ if (user->nt_password_present) {
+ samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
+ "ntPwdHash", &user->ntpassword);
+ } else {
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "ntPwdHash");
+ }
+
+ ADD_OR_DEL(string, "comment", comment.string);
+ ADD_OR_DEL(string, "userParameters", parameters.string);
+ ADD_OR_DEL(uint, "countryCode", country_code);
+ ADD_OR_DEL(uint, "codePage", code_page);
+
+ ADD_OR_DEL(string, "profilePath", profile_path.string);
+
+ acb = user->acct_flags;
+ if (acb & (ACB_WSTRUST)) {
+ cn_name[cn_name_len - 1] = '\0';
+ container = "Computers";
+ obj_class = "computer";
+
+ } else if (acb & ACB_SVRTRUST) {
+ if (cn_name[cn_name_len - 1] != '$') {
+ return NT_STATUS_FOOBAR;
+ }
+ cn_name[cn_name_len - 1] = '\0';
+ container = "Domain Controllers";
+ obj_class = "computer";
+ } else {
+ container = "Users";
+ obj_class = "user";
+ }
+ if (add) {
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
+ "objectClass", obj_class);
+ msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
+ cn_name, container, state->base_dn[database]);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = samdb_add(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to create user record %s\n", msg->dn));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else {
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to modify user record %s\n", msg->dn));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ struct creds_CredentialState *creds,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_GROUP *group = delta->delta_union.group;
+ const char *groupname = group->group_name.string;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
+ void *private,
+ struct creds_CredentialState *creds,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ struct samsync_ldb_state *state = private;
+
+ *error_string = NULL;
+ switch (delta->delta_type) {
+ case NETR_DELTA_DOMAIN:
+ {
+ nt_status = samsync_ldb_handle_domain(mem_ctx,
+ state,
+ creds,
+ database,
+ delta);
+ break;
+ }
+ case NETR_DELTA_USER:
+ {
+ nt_status = samsync_ldb_handle_user(mem_ctx,
+ state,
+ creds,
+ database,
+ delta);
+ break;
+ }
+ case NETR_DELTA_GROUP:
+ {
+ nt_status = samsync_ldb_handle_group(mem_ctx,
+ state,
+ creds,
+ database,
+ delta);
+ break;
+ }
+ default:
+ /* Can't dump them all right now */
+ break;
+ }
+ return nt_status;
+}
+
+static NTSTATUS libnet_samsync_ldb_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
+{
+ NTSTATUS nt_status;
+ struct libnet_SamSync r2;
+ struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
+
+ if (!state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->secrets = NULL;
+ state->trusted_domains = NULL;
+
+ state->sam_ldb = samdb_connect(state);
+
+
+
+ r2.error_string = NULL;
+ r2.delta_fn = libnet_samsync_ldb_fn;
+ r2.fn_ctx = state;
+ r2.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
+ nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
+ r->error_string = r2.error_string;
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(state);
+ return nt_status;
+ }
+ talloc_free(state);
+ return nt_status;
+}
+
+
+
+static NTSTATUS libnet_samsync_ldb_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
+{
+ NTSTATUS nt_status;
+ struct libnet_samsync_ldb r2;
+ r2.level = LIBNET_SAMSYNC_LDB_NETLOGON;
+ r2.error_string = NULL;
+ nt_status = libnet_samsync_ldb(ctx, mem_ctx, &r2);
+ r->error_string = r2.error_string;
+
+ return nt_status;
+}
+
+NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
+{
+ switch (r->level) {
+ case LIBNET_SAMSYNC_LDB_GENERIC:
+ return libnet_samsync_ldb_generic(ctx, mem_ctx, r);
+ case LIBNET_SAMSYNC_LDB_NETLOGON:
+ return libnet_samsync_ldb_netlogon(ctx, mem_ctx, r);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/libnet/libnet_vampire.h b/source4/libnet/libnet_vampire.h
index f5bc2b1501..8a588de48c 100644
--- a/source4/libnet/libnet_vampire.h
+++ b/source4/libnet/libnet_vampire.h
@@ -43,3 +43,13 @@ struct libnet_SamDump {
char *error_string;
};
+enum libnet_samsync_ldb_level {
+ LIBNET_SAMSYNC_LDB_GENERIC,
+ LIBNET_SAMSYNC_LDB_NETLOGON,
+};
+
+struct libnet_samsync_ldb {
+ enum libnet_samsync_ldb_level level;
+ char *error_string;
+};
+
diff --git a/source4/utils/net/net.c b/source4/utils/net/net.c
index 51b860234d..787f71705b 100644
--- a/source4/utils/net/net.c
+++ b/source4/utils/net/net.c
@@ -106,6 +106,7 @@ static const struct net_functable net_functable[] = {
{"time", "get remote server's time\n", net_time, net_time_usage},
{"join", "join a domain\n", net_join, net_join_usage},
{"samdump", "dump the sam of a domain\n", net_samdump, net_samdump_usage},
+ {"samsync", "syncrosnise into the local ldb the sam of a domain\n", net_samsync_ldb, net_samsync_ldb_usage},
{"user", "manage user accounts\n", net_user, net_user_usage},
{NULL, NULL, NULL, NULL}
};
diff --git a/source4/utils/net/net_vampire.c b/source4/utils/net/net_vampire.c
index 72f791db66..e898352cfc 100644
--- a/source4/utils/net/net_vampire.c
+++ b/source4/utils/net/net_vampire.c
@@ -64,3 +64,43 @@ int net_samdump_help(struct net_context *ctx, int argc, const char **argv)
d_printf("Dumps the sam of the domain we are joined to.\n");
return 0;
}
+
+int net_samsync_ldb(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ struct libnet_samsync_ldb r;
+
+ libnetctx = libnet_context_init(NULL);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ r.level = LIBNET_SAMSYNC_LDB_GENERIC;
+ r.error_string = NULL;
+
+ status = libnet_samsync_ldb(libnetctx, ctx->mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("libnet_samsync_ldb returned %s: %s\n",
+ nt_errstr(status),
+ r.error_string));
+ return -1;
+ }
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+int net_samsync_ldb_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net samsync_ldb\n");
+ return 0;
+}
+
+int net_samsync_ldb_help(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Syncrosnise into the local ldb the SAM of a domain.\n");
+ return 0;
+}