summaryrefslogtreecommitdiff
path: root/source3/libnet
diff options
context:
space:
mode:
Diffstat (limited to 'source3/libnet')
-rw-r--r--source3/libnet/libnet.h29
-rw-r--r--source3/libnet/libnet_dssync.c720
-rw-r--r--source3/libnet/libnet_dssync.h57
-rw-r--r--source3/libnet/libnet_dssync_keytab.c641
-rw-r--r--source3/libnet/libnet_join.c2018
-rw-r--r--source3/libnet/libnet_keytab.c404
-rw-r--r--source3/libnet/libnet_keytab.h42
-rw-r--r--source3/libnet/libnet_proto.h78
-rw-r--r--source3/libnet/libnet_samsync.c411
-rw-r--r--source3/libnet/libnet_samsync.h73
-rw-r--r--source3/libnet/libnet_samsync_display.c303
-rw-r--r--source3/libnet/libnet_samsync_keytab.c193
-rw-r--r--source3/libnet/libnet_samsync_ldif.c1229
-rw-r--r--source3/libnet/libnet_samsync_passdb.c789
14 files changed, 6987 insertions, 0 deletions
diff --git a/source3/libnet/libnet.h b/source3/libnet/libnet.h
new file mode 100644
index 0000000000..570009c6f6
--- /dev/null
+++ b/source3/libnet/libnet.h
@@ -0,0 +1,29 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libnet Support
+ * Copyright (C) Guenther Deschner 2007
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBNET_H__
+#define __LIBNET_H__
+
+#include "libnet/libnet_keytab.h"
+#include "libnet/libnet_samsync.h"
+#include "libnet/libnet_dssync.h"
+#include "librpc/gen_ndr/libnet_join.h"
+#include "libnet/libnet_proto.h"
+
+#endif
diff --git a/source3/libnet/libnet_dssync.c b/source3/libnet/libnet_dssync.c
new file mode 100644
index 0000000000..bae03effed
--- /dev/null
+++ b/source3/libnet/libnet_dssync.c
@@ -0,0 +1,720 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan (metze) Metzmacher 2005
+ Copyright (C) Guenther Deschner 2008
+ Copyright (C) Michael Adam 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+
+/****************************************************************
+****************************************************************/
+
+static int libnet_dssync_free_context(struct dssync_context *ctx)
+{
+ if (!ctx) {
+ return 0;
+ }
+
+ if (is_valid_policy_hnd(&ctx->bind_handle) && ctx->cli) {
+ rpccli_drsuapi_DsUnbind(ctx->cli, ctx, &ctx->bind_handle, NULL);
+ }
+
+ return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS libnet_dssync_init_context(TALLOC_CTX *mem_ctx,
+ struct dssync_context **ctx_p)
+{
+ struct dssync_context *ctx;
+
+ ctx = TALLOC_ZERO_P(mem_ctx, struct dssync_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ talloc_set_destructor(ctx, libnet_dssync_free_context);
+ ctx->clean_old_entries = false;
+
+ *ctx_p = ctx;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static DATA_BLOB *decrypt_attr_val(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ uint32_t rid,
+ enum drsuapi_DsAttributeId id,
+ DATA_BLOB *raw_data)
+{
+ bool rcrypt = false;
+ DATA_BLOB out_data;
+
+ ZERO_STRUCT(out_data);
+
+ switch (id) {
+ case DRSUAPI_ATTRIBUTE_dBCSPwd:
+ case DRSUAPI_ATTRIBUTE_unicodePwd:
+ case DRSUAPI_ATTRIBUTE_ntPwdHistory:
+ case DRSUAPI_ATTRIBUTE_lmPwdHistory:
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_supplementalCredentials:
+ case DRSUAPI_ATTRIBUTE_priorValue:
+ case DRSUAPI_ATTRIBUTE_currentValue:
+ case DRSUAPI_ATTRIBUTE_trustAuthOutgoing:
+ case DRSUAPI_ATTRIBUTE_trustAuthIncoming:
+ case DRSUAPI_ATTRIBUTE_initialAuthOutgoing:
+ case DRSUAPI_ATTRIBUTE_initialAuthIncoming:
+ break;
+ default:
+ return raw_data;
+ }
+
+ out_data = decrypt_drsuapi_blob(mem_ctx, session_key, rcrypt,
+ rid, raw_data);
+
+ if (out_data.length) {
+ return (DATA_BLOB *)talloc_memdup(mem_ctx, &out_data, sizeof(DATA_BLOB));
+ }
+
+ return raw_data;
+}
+
+/****************************************************************
+****************************************************************/
+
+static void parse_obj_identifier(struct drsuapi_DsReplicaObjectIdentifier *id,
+ uint32_t *rid)
+{
+ if (!id || !rid) {
+ return;
+ }
+
+ *rid = 0;
+
+ if (id->sid.num_auths > 0) {
+ *rid = id->sid.sub_auths[id->sid.num_auths - 1];
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+static void parse_obj_attribute(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ uint32_t rid,
+ struct drsuapi_DsReplicaAttribute *attr)
+{
+ int i = 0;
+
+ for (i=0; i<attr->value_ctr.num_values; i++) {
+
+ DATA_BLOB *plain_data = NULL;
+
+ plain_data = decrypt_attr_val(mem_ctx,
+ session_key,
+ rid,
+ attr->attid,
+ attr->value_ctr.values[i].blob);
+
+ attr->value_ctr.values[i].blob = plain_data;
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_dssync_decrypt_attributes(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ struct drsuapi_DsReplicaObjectListItemEx *cur)
+{
+ for (; cur; cur = cur->next_object) {
+
+ uint32_t i;
+ uint32_t rid = 0;
+
+ parse_obj_identifier(cur->object.identifier, &rid);
+
+ for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
+
+ struct drsuapi_DsReplicaAttribute *attr;
+
+ attr = &cur->object.attribute_ctr.attributes[i];
+
+ if (attr->value_ctr.num_values < 1) {
+ continue;
+ }
+
+ if (!attr->value_ctr.values[0].blob) {
+ continue;
+ }
+
+ parse_obj_attribute(mem_ctx,
+ session_key,
+ rid,
+ attr);
+ }
+ }
+}
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnet_dssync_bind(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx)
+{
+ NTSTATUS status;
+ WERROR werr;
+
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr bind_info;
+ struct drsuapi_DsBindInfo28 info28;
+
+ ZERO_STRUCT(info28);
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
+
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+ info28.site_guid = GUID_zero();
+ info28.pid = 508;
+ info28.repl_epoch = 0;
+
+ bind_info.length = 28;
+ bind_info.info.info28 = info28;
+
+ status = rpccli_drsuapi_DsBind(ctx->cli, mem_ctx,
+ &bind_guid,
+ &bind_info,
+ &ctx->bind_handle,
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+
+ ZERO_STRUCT(ctx->remote_info28);
+ switch (bind_info.length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &bind_info.info.info24;
+ ctx->remote_info28.site_guid = info24->site_guid;
+ ctx->remote_info28.supported_extensions = info24->supported_extensions;
+ ctx->remote_info28.pid = info24->pid;
+ ctx->remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 28:
+ ctx->remote_info28 = bind_info.info.info28;
+ break;
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &bind_info.info.info48;
+ ctx->remote_info28.site_guid = info48->site_guid;
+ ctx->remote_info28.supported_extensions = info48->supported_extensions;
+ ctx->remote_info28.pid = info48->pid;
+ ctx->remote_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ default:
+ DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
+ bind_info.length));
+ break;
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnet_dssync_lookup_nc(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx)
+{
+ NTSTATUS status;
+ WERROR werr;
+ int32_t level = 1;
+ union drsuapi_DsNameRequest req;
+ int32_t level_out;
+ struct drsuapi_DsNameString names[1];
+ union drsuapi_DsNameCtr ctr;
+
+ names[0].str = talloc_asprintf(mem_ctx, "%s\\", ctx->domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(names[0].str);
+
+ req.req1.codepage = 1252; /* german */
+ req.req1.language = 0x00000407; /* german */
+ req.req1.count = 1;
+ req.req1.names = names;
+ req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+ req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_UKNOWN;
+ req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+
+ status = rpccli_drsuapi_DsCrackNames(ctx->cli, mem_ctx,
+ &ctx->bind_handle,
+ level,
+ &req,
+ &level_out,
+ &ctr,
+ &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to lookup DN for domain name: %s",
+ get_friendly_werror_msg(werr));
+ return status;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+
+ if (ctr.ctr1->count != 1) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ctx->nc_dn = talloc_strdup(mem_ctx, ctr.ctr1->array[0].result_name);
+ NT_STATUS_HAVE_NO_MEMORY(ctx->nc_dn);
+
+ if (!ctx->dns_domain_name) {
+ ctx->dns_domain_name = talloc_strdup_upper(mem_ctx,
+ ctr.ctr1->array[0].dns_domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnet_dssync_init(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx)
+{
+ NTSTATUS status;
+
+ status = libnet_dssync_bind(mem_ctx, ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!ctx->nc_dn) {
+ status = libnet_dssync_lookup_nc(mem_ctx, ctx);
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnet_dssync_build_request(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx,
+ const char *dn,
+ struct replUpToDateVectorBlob *utdv,
+ int32_t *plevel,
+ union drsuapi_DsGetNCChangesRequest *preq)
+{
+ NTSTATUS status;
+ uint32_t count;
+ int32_t level;
+ union drsuapi_DsGetNCChangesRequest req;
+ struct dom_sid null_sid;
+ enum drsuapi_DsExtendedOperation extended_op;
+ struct drsuapi_DsReplicaObjectIdentifier *nc = NULL;
+ struct drsuapi_DsReplicaCursorCtrEx *cursors = NULL;
+
+ uint32_t replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE |
+ DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP |
+ DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS |
+ DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS |
+ DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED;
+
+ ZERO_STRUCT(null_sid);
+ ZERO_STRUCT(req);
+
+ if (ctx->remote_info28.supported_extensions
+ & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8)
+ {
+ level = 8;
+ } else {
+ level = 5;
+ }
+
+ nc = TALLOC_ZERO_P(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
+ if (!nc) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ nc->dn = dn;
+ nc->guid = GUID_zero();
+ nc->sid = null_sid;
+
+ if (!ctx->single_object_replication &&
+ !ctx->force_full_replication && utdv)
+ {
+ cursors = TALLOC_ZERO_P(mem_ctx,
+ struct drsuapi_DsReplicaCursorCtrEx);
+ if (!cursors) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ switch (utdv->version) {
+ case 1:
+ cursors->count = utdv->ctr.ctr1.count;
+ cursors->cursors = utdv->ctr.ctr1.cursors;
+ break;
+ case 2:
+ cursors->count = utdv->ctr.ctr2.count;
+ cursors->cursors = talloc_array(cursors,
+ struct drsuapi_DsReplicaCursor,
+ cursors->count);
+ if (!cursors->cursors) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ for (count = 0; count < cursors->count; count++) {
+ cursors->cursors[count].source_dsa_invocation_id =
+ utdv->ctr.ctr2.cursors[count].source_dsa_invocation_id;
+ cursors->cursors[count].highest_usn =
+ utdv->ctr.ctr2.cursors[count].highest_usn;
+ }
+ break;
+ }
+ }
+
+ if (ctx->single_object_replication) {
+ extended_op = DRSUAPI_EXOP_REPL_OBJ;
+ } else {
+ extended_op = DRSUAPI_EXOP_NONE;
+ }
+
+ if (level == 8) {
+ req.req8.naming_context = nc;
+ req.req8.replica_flags = replica_flags;
+ req.req8.max_object_count = 402;
+ req.req8.max_ndr_size = 402116;
+ req.req8.uptodateness_vector = cursors;
+ req.req8.extended_op = extended_op;
+ } else if (level == 5) {
+ req.req5.naming_context = nc;
+ req.req5.replica_flags = replica_flags;
+ req.req5.max_object_count = 402;
+ req.req5.max_ndr_size = 402116;
+ req.req5.uptodateness_vector = cursors;
+ req.req5.extended_op = extended_op;
+ } else {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (plevel) {
+ *plevel = level;
+ }
+
+ if (preq) {
+ *preq = req;
+ }
+
+ return NT_STATUS_OK;
+
+fail:
+ TALLOC_FREE(nc);
+ TALLOC_FREE(cursors);
+ return status;
+}
+
+static NTSTATUS libnet_dssync_getncchanges(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx,
+ int32_t level,
+ union drsuapi_DsGetNCChangesRequest *req,
+ struct replUpToDateVectorBlob **pnew_utdv)
+{
+ NTSTATUS status;
+ WERROR werr;
+ union drsuapi_DsGetNCChangesCtr ctr;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
+ struct replUpToDateVectorBlob *new_utdv = NULL;
+ int32_t level_out = 0;
+ int32_t out_level = 0;
+ int y;
+ bool last_query;
+
+ if (!ctx->single_object_replication) {
+ new_utdv = TALLOC_ZERO_P(mem_ctx, struct replUpToDateVectorBlob);
+ if (!new_utdv) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+
+ for (y=0, last_query = false; !last_query; y++) {
+ struct drsuapi_DsReplicaObjectListItemEx *first_object = NULL;
+ struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr = NULL;
+
+ if (level == 8) {
+ DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)req->req8.highwatermark.tmp_highest_usn,
+ (long long)req->req8.highwatermark.highest_usn));
+ } else if (level == 5) {
+ DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)req->req5.highwatermark.tmp_highest_usn,
+ (long long)req->req5.highwatermark.highest_usn));
+ }
+
+ status = rpccli_drsuapi_DsGetNCChanges(ctx->cli, mem_ctx,
+ &ctx->bind_handle,
+ level,
+ req,
+ &level_out,
+ &ctr,
+ &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to get NC Changes: %s",
+ get_friendly_werror_msg(werr));
+ goto out;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto out;
+ }
+
+ if (level_out == 1) {
+ out_level = 1;
+ ctr1 = &ctr.ctr1;
+ } else if (level_out == 2) {
+ out_level = 1;
+ ctr1 = ctr.ctr2.ctr.mszip1.ctr1;
+ } else if (level_out == 6) {
+ out_level = 6;
+ ctr6 = &ctr.ctr6;
+ } else if (level_out == 7
+ && ctr.ctr7.level == 6
+ && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP) {
+ out_level = 6;
+ ctr6 = ctr.ctr7.ctr.mszip6.ctr6;
+ }
+
+ if (out_level == 1) {
+ DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)ctr1->new_highwatermark.tmp_highest_usn,
+ (long long)ctr1->new_highwatermark.highest_usn));
+
+ first_object = ctr1->first_object;
+ mapping_ctr = &ctr1->mapping_ctr;
+
+ if (ctr1->more_data) {
+ req->req5.highwatermark = ctr1->new_highwatermark;
+ } else {
+ last_query = true;
+ if (ctr1->uptodateness_vector &&
+ !ctx->single_object_replication)
+ {
+ new_utdv->version = 1;
+ new_utdv->ctr.ctr1.count =
+ ctr1->uptodateness_vector->count;
+ new_utdv->ctr.ctr1.cursors =
+ ctr1->uptodateness_vector->cursors;
+ }
+ }
+ } else if (out_level == 6) {
+ DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)ctr6->new_highwatermark.tmp_highest_usn,
+ (long long)ctr6->new_highwatermark.highest_usn));
+
+ first_object = ctr6->first_object;
+ mapping_ctr = &ctr6->mapping_ctr;
+
+ if (ctr6->more_data) {
+ req->req8.highwatermark = ctr6->new_highwatermark;
+ } else {
+ last_query = true;
+ if (ctr6->uptodateness_vector &&
+ !ctx->single_object_replication)
+ {
+ new_utdv->version = 2;
+ new_utdv->ctr.ctr2.count =
+ ctr6->uptodateness_vector->count;
+ new_utdv->ctr.ctr2.cursors =
+ ctr6->uptodateness_vector->cursors;
+ }
+ }
+ }
+
+ status = cli_get_session_key(mem_ctx, ctx->cli, &ctx->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to get Session Key: %s",
+ nt_errstr(status));
+ goto out;
+ }
+
+ libnet_dssync_decrypt_attributes(mem_ctx,
+ &ctx->session_key,
+ first_object);
+
+ if (ctx->ops->process_objects) {
+ status = ctx->ops->process_objects(ctx, mem_ctx,
+ first_object,
+ mapping_ctr);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to call processing function: %s",
+ nt_errstr(status));
+ goto out;
+ }
+ }
+ }
+
+ *pnew_utdv = new_utdv;
+
+out:
+ return status;
+}
+
+static NTSTATUS libnet_dssync_process(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx)
+{
+ NTSTATUS status;
+
+ int32_t level = 0;
+ union drsuapi_DsGetNCChangesRequest req;
+ struct replUpToDateVectorBlob *old_utdv = NULL;
+ struct replUpToDateVectorBlob *pnew_utdv = NULL;
+ const char **dns;
+ uint32_t dn_count;
+ uint32_t count;
+
+ status = ctx->ops->startup(ctx, mem_ctx, &old_utdv);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to call startup operation: %s",
+ nt_errstr(status));
+ goto out;
+ }
+
+ if (ctx->single_object_replication && ctx->object_dns) {
+ dns = ctx->object_dns;
+ dn_count = ctx->object_count;
+ } else {
+ dns = &ctx->nc_dn;
+ dn_count = 1;
+ }
+
+ for (count=0; count < dn_count; count++) {
+ status = libnet_dssync_build_request(mem_ctx, ctx,
+ dns[count],
+ old_utdv, &level,
+ &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = libnet_dssync_getncchanges(mem_ctx, ctx, level, &req,
+ &pnew_utdv);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to call DsGetNCCHanges: %s",
+ nt_errstr(status));
+ goto out;
+ }
+ }
+
+ status = ctx->ops->finish(ctx, mem_ctx, pnew_utdv);
+ if (!NT_STATUS_IS_OK(status)) {
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to call finishing operation: %s",
+ nt_errstr(status));
+ goto out;
+ }
+
+ out:
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS libnet_dssync(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = libnet_dssync_init(tmp_ctx, ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = libnet_dssync_process(tmp_ctx, ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ out:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
diff --git a/source3/libnet/libnet_dssync.h b/source3/libnet/libnet_dssync.h
new file mode 100644
index 0000000000..a5a00742c5
--- /dev/null
+++ b/source3/libnet/libnet_dssync.h
@@ -0,0 +1,57 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libnet Support
+ * Copyright (C) Guenther Deschner 2008
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+struct dssync_context;
+
+struct dssync_ops {
+ NTSTATUS (*startup)(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
+ struct replUpToDateVectorBlob **pold_utdv);
+ NTSTATUS (*process_objects)(struct dssync_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaObjectListItemEx *objects,
+ struct drsuapi_DsReplicaOIDMapping_Ctr *mappings);
+ NTSTATUS (*finish)(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
+ struct replUpToDateVectorBlob *new_utdv);
+};
+
+struct dssync_context {
+ const char *domain_name;
+ const char *dns_domain_name;
+ struct rpc_pipe_client *cli;
+ const char *nc_dn;
+ bool single_object_replication;
+ bool force_full_replication;
+ bool clean_old_entries;
+ uint32_t object_count;
+ const char **object_dns;
+ struct policy_handle bind_handle;
+ DATA_BLOB session_key;
+ const char *output_filename;
+ struct drsuapi_DsBindInfo28 remote_info28;
+
+ void *private_data;
+
+ const struct dssync_ops *ops;
+
+ char *result_message;
+ char *error_message;
+};
+
+extern const struct dssync_ops libnet_dssync_keytab_ops;
diff --git a/source3/libnet/libnet_dssync_keytab.c b/source3/libnet/libnet_dssync_keytab.c
new file mode 100644
index 0000000000..6ba2c3aa41
--- /dev/null
+++ b/source3/libnet/libnet_dssync_keytab.c
@@ -0,0 +1,641 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Guenther Deschner <gd@samba.org> 2008
+ Copyright (C) Michael Adam 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+
+#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
+
+/**
+ * Internal helper function to add data to the list
+ * of keytab entries. It builds the prefix from the input.
+ */
+static NTSTATUS add_to_keytab_entries(TALLOC_CTX *mem_ctx,
+ struct libnet_keytab_context *ctx,
+ uint32_t kvno,
+ const char *name,
+ const char *prefix,
+ const krb5_enctype enctype,
+ DATA_BLOB blob)
+{
+ struct libnet_keytab_entry entry;
+
+ entry.kvno = kvno;
+ entry.name = talloc_strdup(mem_ctx, name);
+ entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
+ prefix ? prefix : "",
+ prefix ? "/" : "",
+ name, ctx->dns_domain_name);
+ entry.enctype = enctype;
+ entry.password = blob;
+ NT_STATUS_HAVE_NO_MEMORY(entry.name);
+ NT_STATUS_HAVE_NO_MEMORY(entry.principal);
+ NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
+
+ ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
+ &ctx->entries, &ctx->count);
+ NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
+ struct replUpToDateVectorBlob **pold_utdv)
+{
+ krb5_error_code ret = 0;
+ struct libnet_keytab_context *keytab_ctx;
+ struct libnet_keytab_entry *entry;
+ struct replUpToDateVectorBlob *old_utdv = NULL;
+ char *principal;
+
+ ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
+ if (ret) {
+ return krb5_to_nt_status(ret);
+ }
+
+ keytab_ctx->dns_domain_name = ctx->dns_domain_name;
+ keytab_ctx->clean_old_entries = ctx->clean_old_entries;
+ ctx->private_data = keytab_ctx;
+
+ principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
+ ctx->nc_dn, ctx->dns_domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(principal);
+
+ entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
+ mem_ctx);
+ if (entry) {
+ enum ndr_err_code ndr_err;
+ old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);
+
+ ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv,
+ old_utdv,
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to pull UpToDateVector: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
+ }
+ }
+
+ if (pold_utdv) {
+ *pold_utdv = old_utdv;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
+ struct replUpToDateVectorBlob *new_utdv)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ krb5_error_code ret = 0;
+ struct libnet_keytab_context *keytab_ctx =
+ (struct libnet_keytab_context *)ctx->private_data;
+
+ if (new_utdv) {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
+ (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to push UpToDateVector: %s",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
+ ctx->nc_dn, "UTDV",
+ ENCTYPE_NULL,
+ blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+ ret = libnet_keytab_add(keytab_ctx);
+ if (ret) {
+ status = krb5_to_nt_status(ret);
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to add entries to keytab %s: %s",
+ keytab_ctx->keytab_name, error_message(ret));
+ goto done;
+ }
+
+ ctx->result_message = talloc_asprintf(ctx,
+ "Vampired %d accounts to keytab %s",
+ keytab_ctx->count,
+ keytab_ctx->keytab_name);
+
+done:
+ TALLOC_FREE(keytab_ctx);
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS parse_supplemental_credentials(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ struct package_PrimaryKerberosCtr3 **pkb3,
+ struct package_PrimaryKerberosCtr4 **pkb4)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct supplementalCredentialsBlob scb;
+ struct supplementalCredentialsPackage *scpk = NULL;
+ DATA_BLOB scpk_blob;
+ struct package_PrimaryKerberosBlob *pkb;
+ bool newer_keys = false;
+ uint32_t j;
+
+ ndr_err = ndr_pull_struct_blob_all(blob, mem_ctx, &scb,
+ (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto done;
+ }
+ if (scb.sub.signature !=
+ SUPPLEMENTAL_CREDENTIALS_SIGNATURE)
+ {
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
+ }
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ for (j=0; j < scb.sub.num_packages; j++) {
+ if (strcmp("Primary:Kerberos-Newer-Keys",
+ scb.sub.packages[j].name) == 0)
+ {
+ scpk = &scb.sub.packages[j];
+ if (!scpk->data || !scpk->data[0]) {
+ scpk = NULL;
+ continue;
+ }
+ newer_keys = true;
+ break;
+ } else if (strcmp("Primary:Kerberos",
+ scb.sub.packages[j].name) == 0)
+ {
+ /*
+ * grab this but don't break here:
+ * there might still be newer-keys ...
+ */
+ scpk = &scb.sub.packages[j];
+ if (!scpk->data || !scpk->data[0]) {
+ scpk = NULL;
+ }
+ }
+ }
+
+ if (!scpk) {
+ /* no data */
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ scpk_blob = strhex_to_data_blob(mem_ctx, scpk->data);
+ if (!scpk_blob.data) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ pkb = TALLOC_ZERO_P(mem_ctx, struct package_PrimaryKerberosBlob);
+ if (!pkb) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ ndr_err = ndr_pull_struct_blob(&scpk_blob, mem_ctx, pkb,
+ (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto done;
+ }
+
+ if (!newer_keys && pkb->version != 3) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (newer_keys && pkb->version != 4) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (pkb->version == 4 && pkb4) {
+ *pkb4 = &pkb->ctr.ctr4;
+ } else if (pkb->version == 3 && pkb3) {
+ *pkb3 = &pkb->ctr.ctr3;
+ }
+
+ status = NT_STATUS_OK;
+
+done:
+ return status;
+}
+
+static NTSTATUS parse_object(TALLOC_CTX *mem_ctx,
+ struct libnet_keytab_context *ctx,
+ struct drsuapi_DsReplicaObjectListItemEx *cur)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ uchar nt_passwd[16];
+ DATA_BLOB *blob;
+ int i = 0;
+ struct drsuapi_DsReplicaAttribute *attr;
+ bool got_pwd = false;
+
+ struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
+ struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
+
+ char *object_dn = NULL;
+ char *upn = NULL;
+ char **spn = NULL;
+ uint32_t num_spns = 0;
+ char *name = NULL;
+ uint32_t kvno = 0;
+ uint32_t uacc = 0;
+ uint32_t sam_type = 0;
+
+ uint32_t pwd_history_len = 0;
+ uint8_t *pwd_history = NULL;
+
+ ZERO_STRUCT(nt_passwd);
+
+ object_dn = talloc_strdup(mem_ctx, cur->object.identifier->dn);
+ if (!object_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(3, ("parsing object '%s'\n", object_dn));
+
+ for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
+
+ attr = &cur->object.attribute_ctr.attributes[i];
+
+ if (attr->attid == DRSUAPI_ATTRIBUTE_servicePrincipalName) {
+ uint32_t count;
+ num_spns = attr->value_ctr.num_values;
+ spn = TALLOC_ARRAY(mem_ctx, char *, num_spns);
+ for (count = 0; count < num_spns; count++) {
+ blob = attr->value_ctr.values[count].blob;
+ pull_string_talloc(spn, NULL, 0,
+ &spn[count],
+ blob->data, blob->length,
+ STR_UNICODE);
+ }
+ }
+
+ if (attr->value_ctr.num_values != 1) {
+ continue;
+ }
+
+ if (!attr->value_ctr.values[0].blob) {
+ continue;
+ }
+
+ blob = attr->value_ctr.values[0].blob;
+
+ switch (attr->attid) {
+ case DRSUAPI_ATTRIBUTE_unicodePwd:
+
+ if (blob->length != 16) {
+ break;
+ }
+
+ memcpy(&nt_passwd, blob->data, 16);
+ got_pwd = true;
+
+ /* pick the kvno from the meta_data version,
+ * thanks, metze, for explaining this */
+
+ if (!cur->meta_data_ctr) {
+ break;
+ }
+ if (cur->meta_data_ctr->count !=
+ cur->object.attribute_ctr.num_attributes) {
+ break;
+ }
+ kvno = cur->meta_data_ctr->meta_data[i].version;
+ break;
+ case DRSUAPI_ATTRIBUTE_ntPwdHistory:
+ pwd_history_len = blob->length / 16;
+ pwd_history = blob->data;
+ break;
+ case DRSUAPI_ATTRIBUTE_userPrincipalName:
+ pull_string_talloc(mem_ctx, NULL, 0, &upn,
+ blob->data, blob->length,
+ STR_UNICODE);
+ break;
+ case DRSUAPI_ATTRIBUTE_sAMAccountName:
+ pull_string_talloc(mem_ctx, NULL, 0, &name,
+ blob->data, blob->length,
+ STR_UNICODE);
+ break;
+ case DRSUAPI_ATTRIBUTE_sAMAccountType:
+ sam_type = IVAL(blob->data, 0);
+ break;
+ case DRSUAPI_ATTRIBUTE_userAccountControl:
+ uacc = IVAL(blob->data, 0);
+ break;
+ case DRSUAPI_ATTRIBUTE_supplementalCredentials:
+ status = parse_supplemental_credentials(mem_ctx,
+ blob,
+ &pkb3,
+ &pkb4);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("parsing of supplemental "
+ "credentials failed: %s\n",
+ nt_errstr(status)));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!got_pwd) {
+ DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (name) {
+ status = add_to_keytab_entries(mem_ctx, ctx, 0, object_dn,
+ "SAMACCOUNTNAME",
+ ENCTYPE_NULL,
+ data_blob_talloc(mem_ctx, name,
+ strlen(name) + 1));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ /* look into keytab ... */
+ struct libnet_keytab_entry *entry = NULL;
+ char *principal = NULL;
+
+ DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
+ object_dn, ctx->dns_domain_name));
+
+ principal = talloc_asprintf(mem_ctx, "%s/%s@%s",
+ "SAMACCOUNTNAME",
+ object_dn,
+ ctx->dns_domain_name);
+ if (!principal) {
+ DEBUG(1, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ entry = libnet_keytab_search(ctx, principal, 0, ENCTYPE_NULL,
+ mem_ctx);
+ if (entry) {
+ name = (char *)TALLOC_MEMDUP(mem_ctx,
+ entry->password.data,
+ entry->password.length);
+ if (!name) {
+ DEBUG(1, ("talloc failed!"));
+ return NT_STATUS_NO_MEMORY;
+ } else {
+ DEBUG(10, ("found name %s\n", name));
+ }
+ TALLOC_FREE(entry);
+ } else {
+ DEBUG(10, ("entry not found\n"));
+ }
+ TALLOC_FREE(principal);
+ }
+
+ if (!name) {
+ DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(1,("#%02d: %s:%d, ", ctx->count, name, kvno));
+ DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
+ sam_type, uacc));
+ if (upn) {
+ DEBUGADD(1,(", upn: %s", upn));
+ }
+ if (num_spns > 0) {
+ DEBUGADD(1, (", spns: ["));
+ for (i = 0; i < num_spns; i++) {
+ DEBUGADD(1, ("%s%s", spn[i],
+ (i+1 == num_spns)?"]":", "));
+ }
+ }
+ DEBUGADD(1,("\n"));
+
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno, name, NULL,
+ ENCTYPE_ARCFOUR_HMAC,
+ data_blob_talloc(mem_ctx, nt_passwd, 16));
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* add kerberos keys (if any) */
+
+ if (pkb4) {
+ for (i=0; i < pkb4->num_keys; i++) {
+ if (!pkb4->keys[i].value) {
+ continue;
+ }
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno,
+ name,
+ NULL,
+ pkb4->keys[i].keytype,
+ *pkb4->keys[i].value);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ for (i=0; i < pkb4->num_old_keys; i++) {
+ if (!pkb4->old_keys[i].value) {
+ continue;
+ }
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
+ name,
+ NULL,
+ pkb4->old_keys[i].keytype,
+ *pkb4->old_keys[i].value);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ for (i=0; i < pkb4->num_older_keys; i++) {
+ if (!pkb4->older_keys[i].value) {
+ continue;
+ }
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno - 2,
+ name,
+ NULL,
+ pkb4->older_keys[i].keytype,
+ *pkb4->older_keys[i].value);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+
+ if (pkb3) {
+ for (i=0; i < pkb3->num_keys; i++) {
+ if (!pkb3->keys[i].value) {
+ continue;
+ }
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno, name,
+ NULL,
+ pkb3->keys[i].keytype,
+ *pkb3->keys[i].value);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ for (i=0; i < pkb3->num_old_keys; i++) {
+ if (!pkb3->old_keys[i].value) {
+ continue;
+ }
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
+ name,
+ NULL,
+ pkb3->old_keys[i].keytype,
+ *pkb3->old_keys[i].value);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+
+ if ((kvno < 0) && (kvno < pwd_history_len)) {
+ return status;
+ }
+
+ /* add password history */
+
+ /* skip first entry */
+ if (got_pwd) {
+ kvno--;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ for (; i<pwd_history_len; i++) {
+ status = add_to_keytab_entries(mem_ctx, ctx, kvno--, name, NULL,
+ ENCTYPE_ARCFOUR_HMAC,
+ data_blob_talloc(mem_ctx, &pwd_history[i*16], 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ }
+
+ return status;
+}
+
+static bool dn_is_in_object_list(struct dssync_context *ctx,
+ const char *dn)
+{
+ uint32_t count;
+
+ if (ctx->object_count == 0) {
+ return true;
+ }
+
+ for (count = 0; count < ctx->object_count; count++) {
+ if (strequal(ctx->object_dns[count], dn)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaObjectListItemEx *cur,
+ struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct libnet_keytab_context *keytab_ctx =
+ (struct libnet_keytab_context *)ctx->private_data;
+
+ for (; cur; cur = cur->next_object) {
+ /*
+ * When not in single object replication mode,
+ * the object_dn list is used as a positive write filter.
+ */
+ if (!ctx->single_object_replication &&
+ !dn_is_in_object_list(ctx, cur->object.identifier->dn))
+ {
+ continue;
+ }
+
+ status = parse_object(mem_ctx, keytab_ctx, cur);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ out:
+ return status;
+}
+
+#else
+
+static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
+ struct replUpToDateVectorBlob **pold_utdv)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
+ struct replUpToDateVectorBlob *new_utdv)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaObjectListItemEx *cur,
+ struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
+
+const struct dssync_ops libnet_dssync_keytab_ops = {
+ .startup = keytab_startup,
+ .process_objects = keytab_process_objects,
+ .finish = keytab_finish,
+};
diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c
new file mode 100644
index 0000000000..a39dee676f
--- /dev/null
+++ b/source3/libnet/libnet_join.c
@@ -0,0 +1,2018 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libnet Join Support
+ * Copyright (C) Gerald (Jerry) Carter 2006
+ * Copyright (C) Guenther Deschner 2007-2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "libnet/libnet.h"
+
+/****************************************************************
+****************************************************************/
+
+#define LIBNET_JOIN_DUMP_CTX(ctx, r, f) \
+ do { \
+ char *str = NULL; \
+ str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_JoinCtx, f, r); \
+ DEBUG(1,("libnet_Join:\n%s", str)); \
+ TALLOC_FREE(str); \
+ } while (0)
+
+#define LIBNET_JOIN_IN_DUMP_CTX(ctx, r) \
+ LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
+#define LIBNET_JOIN_OUT_DUMP_CTX(ctx, r) \
+ LIBNET_JOIN_DUMP_CTX(ctx, r, NDR_OUT)
+
+#define LIBNET_UNJOIN_DUMP_CTX(ctx, r, f) \
+ do { \
+ char *str = NULL; \
+ str = NDR_PRINT_FUNCTION_STRING(ctx, libnet_UnjoinCtx, f, r); \
+ DEBUG(1,("libnet_Unjoin:\n%s", str)); \
+ TALLOC_FREE(str); \
+ } while (0)
+
+#define LIBNET_UNJOIN_IN_DUMP_CTX(ctx, r) \
+ LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_IN | NDR_SET_VALUES)
+#define LIBNET_UNJOIN_OUT_DUMP_CTX(ctx, r) \
+ LIBNET_UNJOIN_DUMP_CTX(ctx, r, NDR_OUT)
+
+#define W_ERROR_NOT_OK_GOTO_DONE(x) do { \
+ if (!W_ERROR_IS_OK(x)) {\
+ goto done;\
+ }\
+} while (0)
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_join_set_error_string(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (r->out.error_string) {
+ return;
+ }
+
+ va_start(args, format);
+ r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
+ va_end(args);
+}
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_unjoin_set_error_string(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (r->out.error_string) {
+ return;
+ }
+
+ va_start(args, format);
+ r->out.error_string = talloc_vasprintf(mem_ctx, format, args);
+ va_end(args);
+}
+
+#ifdef WITH_ADS
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_connect_ads(const char *dns_domain_name,
+ const char *netbios_domain_name,
+ const char *dc_name,
+ const char *user_name,
+ const char *password,
+ ADS_STRUCT **ads)
+{
+ ADS_STATUS status;
+ ADS_STRUCT *my_ads = NULL;
+
+ my_ads = ads_init(dns_domain_name,
+ netbios_domain_name,
+ dc_name);
+ if (!my_ads) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+
+ if (user_name) {
+ SAFE_FREE(my_ads->auth.user_name);
+ my_ads->auth.user_name = SMB_STRDUP(user_name);
+ }
+
+ if (password) {
+ SAFE_FREE(my_ads->auth.password);
+ my_ads->auth.password = SMB_STRDUP(password);
+ }
+
+ status = ads_connect_user_creds(my_ads);
+ if (!ADS_ERR_OK(status)) {
+ ads_destroy(&my_ads);
+ return status;
+ }
+
+ *ads = my_ads;
+ return ADS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+
+ status = libnet_connect_ads(r->out.dns_domain_name,
+ r->out.netbios_domain_name,
+ r->in.dc_name,
+ r->in.admin_account,
+ r->in.admin_password,
+ &r->in.ads);
+ if (!ADS_ERR_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to connect to AD: %s",
+ ads_errstr(status));
+ return status;
+ }
+
+ if (!r->out.netbios_domain_name) {
+ r->out.netbios_domain_name = talloc_strdup(mem_ctx,
+ r->in.ads->server.workgroup);
+ ADS_ERROR_HAVE_NO_MEMORY(r->out.netbios_domain_name);
+ }
+
+ if (!r->out.dns_domain_name) {
+ r->out.dns_domain_name = talloc_strdup(mem_ctx,
+ r->in.ads->config.realm);
+ ADS_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
+ }
+
+ r->out.domain_is_ad = true;
+
+ return ADS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_unjoin_connect_ads(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ ADS_STATUS status;
+
+ status = libnet_connect_ads(r->in.domain_name,
+ r->in.domain_name,
+ r->in.dc_name,
+ r->in.admin_account,
+ r->in.admin_password,
+ &r->in.ads);
+ if (!ADS_ERR_OK(status)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "failed to connect to AD: %s",
+ ads_errstr(status));
+ }
+
+ return status;
+}
+
+/****************************************************************
+ join a domain using ADS (LDAP mods)
+****************************************************************/
+
+static ADS_STATUS libnet_join_precreate_machine_acct(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ const char *attrs[] = { "dn", NULL };
+ bool moved = false;
+
+ status = ads_check_ou_dn(mem_ctx, r->in.ads, &r->in.account_ou);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ status = ads_search_dn(r->in.ads, &res, r->in.account_ou, attrs);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ if (ads_count_replies(r->in.ads, res) != 1) {
+ ads_msgfree(r->in.ads, res);
+ return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
+ }
+
+ ads_msgfree(r->in.ads, res);
+
+ /* Attempt to create the machine account and bail if this fails.
+ Assume that the admin wants exactly what they requested */
+
+ status = ads_create_machine_acct(r->in.ads,
+ r->in.machine_name,
+ r->in.account_ou);
+
+ if (ADS_ERR_OK(status)) {
+ DEBUG(1,("machine account creation created\n"));
+ return status;
+ } else if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
+ (status.err.rc == LDAP_ALREADY_EXISTS)) {
+ status = ADS_SUCCESS;
+ }
+
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("machine account creation failed\n"));
+ return status;
+ }
+
+ status = ads_move_machine_acct(r->in.ads,
+ r->in.machine_name,
+ r->in.account_ou,
+ &moved);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("failure to locate/move pre-existing "
+ "machine account\n"));
+ return status;
+ }
+
+ DEBUG(1,("The machine account %s the specified OU.\n",
+ moved ? "was moved into" : "already exists in"));
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_unjoin_remove_machine_acct(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ ADS_STATUS status;
+
+ if (!r->in.ads) {
+ return libnet_unjoin_connect_ads(mem_ctx, r);
+ }
+
+ status = ads_leave_realm(r->in.ads, r->in.machine_name);
+ if (!ADS_ERR_OK(status)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "failed to leave realm: %s",
+ ads_errstr(status));
+ return status;
+ }
+
+ return ADS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_find_machine_acct(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ char *dn = NULL;
+
+ if (!r->in.machine_name) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ status = ads_find_machine_acct(r->in.ads,
+ &res,
+ r->in.machine_name);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ if (ads_count_replies(r->in.ads, res) != 1) {
+ status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ goto done;
+ }
+
+ dn = ads_get_dn(r->in.ads, res);
+ if (!dn) {
+ status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ goto done;
+ }
+
+ r->out.dn = talloc_strdup(mem_ctx, dn);
+ if (!r->out.dn) {
+ status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ goto done;
+ }
+
+ done:
+ ads_msgfree(r->in.ads, res);
+ ads_memfree(r->in.ads, dn);
+
+ return status;
+}
+
+/****************************************************************
+ Set a machines dNSHostName and servicePrincipalName attributes
+****************************************************************/
+
+static ADS_STATUS libnet_join_set_machine_spn(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+ ADS_MODLIST mods;
+ fstring my_fqdn;
+ const char *spn_array[3] = {NULL, NULL, NULL};
+ char *spn = NULL;
+
+ /* Find our DN */
+
+ status = libnet_join_find_machine_acct(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ /* Windows only creates HOST/shortname & HOST/fqdn. */
+
+ spn = talloc_asprintf(mem_ctx, "HOST/%s", r->in.machine_name);
+ if (!spn) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+ strupper_m(spn);
+ spn_array[0] = spn;
+
+ if (name_to_fqdn(my_fqdn, r->in.machine_name) &&
+ !strequal(my_fqdn, r->in.machine_name)) {
+
+ strlower_m(my_fqdn);
+ spn = talloc_asprintf(mem_ctx, "HOST/%s", my_fqdn);
+ if (!spn) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+ spn_array[1] = spn;
+ }
+
+ mods = ads_init_mods(mem_ctx);
+ if (!mods) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+
+ /* fields of primary importance */
+
+ status = ads_mod_str(mem_ctx, &mods, "dNSHostName", my_fqdn);
+ if (!ADS_ERR_OK(status)) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+
+ status = ads_mod_strlist(mem_ctx, &mods, "servicePrincipalName",
+ spn_array);
+ if (!ADS_ERR_OK(status)) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+
+ return ads_gen_mod(r->in.ads, r->out.dn, mods);
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_set_machine_upn(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+ ADS_MODLIST mods;
+
+ if (!r->in.create_upn) {
+ return ADS_SUCCESS;
+ }
+
+ /* Find our DN */
+
+ status = libnet_join_find_machine_acct(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ if (!r->in.upn) {
+ r->in.upn = talloc_asprintf(mem_ctx,
+ "host/%s@%s",
+ r->in.machine_name,
+ r->out.dns_domain_name);
+ if (!r->in.upn) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ }
+
+ /* now do the mods */
+
+ mods = ads_init_mods(mem_ctx);
+ if (!mods) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+
+ /* fields of primary importance */
+
+ status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", r->in.upn);
+ if (!ADS_ERR_OK(status)) {
+ return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+ }
+
+ return ads_gen_mod(r->in.ads, r->out.dn, mods);
+}
+
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_set_os_attributes(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+ ADS_MODLIST mods;
+ char *os_sp = NULL;
+
+ if (!r->in.os_name || !r->in.os_version ) {
+ return ADS_SUCCESS;
+ }
+
+ /* Find our DN */
+
+ status = libnet_join_find_machine_acct(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ /* now do the mods */
+
+ mods = ads_init_mods(mem_ctx);
+ if (!mods) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ os_sp = talloc_asprintf(mem_ctx, "Samba %s", SAMBA_VERSION_STRING);
+ if (!os_sp) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ /* fields of primary importance */
+
+ status = ads_mod_str(mem_ctx, &mods, "operatingSystem",
+ r->in.os_name);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion",
+ r->in.os_version);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ status = ads_mod_str(mem_ctx, &mods, "operatingSystemServicePack",
+ os_sp);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ return ads_gen_mod(r->in.ads, r->out.dn, mods);
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ if (!lp_use_kerberos_keytab()) {
+ return true;
+ }
+
+ if (ads_keytab_create_default(r->in.ads) != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ uint32_t domain_func;
+ ADS_STATUS status;
+ const char *salt = NULL;
+ char *std_salt = NULL;
+
+ status = ads_domain_func_level(r->in.ads, &domain_func);
+ if (!ADS_ERR_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to determine domain functional level: %s",
+ ads_errstr(status));
+ return false;
+ }
+
+ /* go ahead and setup the default salt */
+
+ std_salt = kerberos_standard_des_salt();
+ if (!std_salt) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to obtain standard DES salt");
+ return false;
+ }
+
+ salt = talloc_strdup(mem_ctx, std_salt);
+ if (!salt) {
+ return false;
+ }
+
+ SAFE_FREE(std_salt);
+
+ /* if it's a Windows functional domain, we have to look for the UPN */
+
+ if (domain_func == DS_DOMAIN_FUNCTION_2000) {
+ char *upn;
+
+ upn = ads_get_upn(r->in.ads, mem_ctx,
+ r->in.machine_name);
+ if (upn) {
+ salt = talloc_strdup(mem_ctx, upn);
+ if (!salt) {
+ return false;
+ }
+ }
+ }
+
+ return kerberos_secrets_store_des_salt(salt);
+}
+
+/****************************************************************
+****************************************************************/
+
+static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ ADS_STATUS status;
+
+ if (!r->in.ads) {
+ status = libnet_join_connect_ads(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+ }
+
+ status = libnet_join_set_machine_spn(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to set machine spn: %s",
+ ads_errstr(status));
+ return status;
+ }
+
+ status = libnet_join_set_os_attributes(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to set machine os attributes: %s",
+ ads_errstr(status));
+ return status;
+ }
+
+ status = libnet_join_set_machine_upn(mem_ctx, r);
+ if (!ADS_ERR_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to set machine upn: %s",
+ ads_errstr(status));
+ return status;
+ }
+
+ if (!libnet_join_derive_salting_principal(mem_ctx, r)) {
+ return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ if (!libnet_join_create_keytab(mem_ctx, r)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to create kerberos keytab");
+ return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ return ADS_SUCCESS;
+}
+#endif /* WITH_ADS */
+
+/****************************************************************
+ Store the machine password and domain SID
+****************************************************************/
+
+static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ if (!secrets_store_domain_sid(r->out.netbios_domain_name,
+ r->out.domain_sid))
+ {
+ DEBUG(1,("Failed to save domain sid\n"));
+ return false;
+ }
+
+ if (!secrets_store_machine_password(r->in.machine_password,
+ r->out.netbios_domain_name,
+ r->in.secure_channel_type))
+ {
+ DEBUG(1,("Failed to save machine password\n"));
+ return false;
+ }
+
+ return true;
+}
+
+/****************************************************************
+ Connect dc's IPC$ share
+****************************************************************/
+
+static NTSTATUS libnet_join_connect_dc_ipc(const char *dc,
+ const char *user,
+ const char *pass,
+ bool use_kerberos,
+ struct cli_state **cli)
+{
+ int flags = 0;
+
+ if (use_kerberos) {
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+ }
+
+ if (use_kerberos && pass) {
+ flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+ }
+
+ return cli_full_connection(cli, NULL,
+ dc,
+ NULL, 0,
+ "IPC$", "IPC",
+ user,
+ NULL,
+ pass,
+ flags,
+ Undefined, NULL);
+}
+
+/****************************************************************
+ Lookup domain dc's info
+****************************************************************/
+
+static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r,
+ struct cli_state **cli)
+{
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ POLICY_HND lsa_pol;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ union lsa_PolicyInformation *info = NULL;
+
+ status = libnet_join_connect_dc_ipc(r->in.dc_name,
+ r->in.admin_account,
+ r->in.admin_password,
+ r->in.use_kerberos,
+ cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Error connecting to LSA pipe. Error was %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpccli_lsa_QueryInfoPolicy2(pipe_hnd, mem_ctx,
+ &lsa_pol,
+ LSA_POLICY_INFO_DNS,
+ &info);
+ if (NT_STATUS_IS_OK(status)) {
+ r->out.domain_is_ad = true;
+ r->out.netbios_domain_name = info->dns.name.string;
+ r->out.dns_domain_name = info->dns.dns_domain.string;
+ r->out.forest_name = info->dns.dns_forest.string;
+ r->out.domain_sid = sid_dup_talloc(mem_ctx, info->dns.sid);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &lsa_pol,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ r->out.netbios_domain_name = info->account_domain.name.string;
+ r->out.domain_sid = sid_dup_talloc(mem_ctx, info->account_domain.sid);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid);
+ }
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
+ TALLOC_FREE(pipe_hnd);
+
+ done:
+ return status;
+}
+
+/****************************************************************
+ Do the domain join
+****************************************************************/
+
+static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r,
+ struct cli_state *cli)
+{
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ POLICY_HND sam_pol, domain_pol, user_pol;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *acct_name;
+ struct lsa_String lsa_acct_name;
+ uint32_t user_rid;
+ uint32_t acct_flags = ACB_WSTRUST;
+ uchar md4_trust_password[16];
+ struct samr_Ids user_rids;
+ struct samr_Ids name_types;
+ union samr_UserInfo user_info;
+
+ struct samr_CryptPassword crypt_pwd;
+ struct samr_CryptPasswordEx crypt_pwd_ex;
+
+ ZERO_STRUCT(sam_pol);
+ ZERO_STRUCT(domain_pol);
+ ZERO_STRUCT(user_pol);
+
+ if (!r->in.machine_password) {
+ r->in.machine_password = talloc_strdup(mem_ctx, generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH));
+ NT_STATUS_HAVE_NO_MEMORY(r->in.machine_password);
+ }
+
+ /* Open the domain */
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &sam_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &sam_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ r->out.domain_sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* Create domain user */
+
+ acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
+ strlower_m(acct_name);
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ if (r->in.join_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE) {
+ uint32_t access_desired =
+ SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
+ SEC_STD_WRITE_DAC | SEC_STD_DELETE |
+ SAMR_USER_ACCESS_SET_PASSWORD |
+ SAMR_USER_ACCESS_GET_ATTRIBUTES |
+ SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ uint32_t access_granted = 0;
+
+ /* Don't try to set any acct_flags flags other than ACB_WSTRUST */
+
+ DEBUG(10,("Creating account with desired access mask: %d\n",
+ access_desired));
+
+ status = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
+ &domain_pol,
+ &lsa_acct_name,
+ ACB_WSTRUST,
+ access_desired,
+ &user_pol,
+ &access_granted,
+ &user_rid);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+
+ DEBUG(10,("Creation of workstation account failed: %s\n",
+ nt_errstr(status)));
+
+ /* If NT_STATUS_ACCESS_DENIED then we have a valid
+ username/password combo but the user does not have
+ administrator access. */
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "User specified does not have "
+ "administrator privileges");
+ }
+
+ goto done;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!(r->in.join_flags &
+ WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED)) {
+ goto done;
+ }
+ }
+
+ /* We *must* do this.... don't ask... */
+
+ if (NT_STATUS_IS_OK(status)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ }
+ }
+
+ status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &user_rids,
+ &name_types);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (name_types.ids[0] != SID_NAME_USER) {
+ DEBUG(0,("%s is not a user account (type=%d)\n",
+ acct_name, name_types.ids[0]));
+ status = NT_STATUS_INVALID_WORKSTATION;
+ goto done;
+ }
+
+ user_rid = user_rids.ids[0];
+
+ /* Open handle on user */
+
+ status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+ &domain_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ user_rid,
+ &user_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* Create a random machine account password and generate the hash */
+
+ E_md4hash(r->in.machine_password, md4_trust_password);
+
+ init_samr_CryptPasswordEx(r->in.machine_password,
+ &cli->user_session_key,
+ &crypt_pwd_ex);
+
+ /* Fill in the additional account flags now */
+
+ acct_flags |= ACB_PWNOEXP;
+ if (r->out.domain_is_ad) {
+#if !defined(ENCTYPE_ARCFOUR_HMAC)
+ acct_flags |= ACB_USE_DES_KEY_ONLY;
+#endif
+ ;;
+ }
+
+ /* Set password and account flags on machine account */
+
+ ZERO_STRUCT(user_info.info25);
+
+ user_info.info25.info.fields_present = ACCT_NT_PWD_SET |
+ ACCT_LM_PWD_SET |
+ SAMR_FIELD_ACCT_FLAGS;
+
+ user_info.info25.info.acct_flags = acct_flags;
+ memcpy(&user_info.info25.password.data, crypt_pwd_ex.data,
+ sizeof(crypt_pwd_ex.data));
+
+ status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+ &user_pol,
+ 25,
+ &user_info);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS(DCERPC_FAULT_INVALID_TAG))) {
+
+ /* retry with level 24 */
+
+ init_samr_CryptPassword(r->in.machine_password,
+ &cli->user_session_key,
+ &crypt_pwd);
+
+ init_samr_user_info24(&user_info.info24, crypt_pwd.data, 24);
+
+ status = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
+ &user_pol,
+ 24,
+ &user_info);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+
+ rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
+ &user_pol);
+
+ libnet_join_set_error_string(mem_ctx, r,
+ "Failed to set password for machine account (%s)\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = NT_STATUS_OK;
+
+ done:
+ if (!pipe_hnd) {
+ return status;
+ }
+
+ if (is_valid_policy_hnd(&sam_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
+ }
+ if (is_valid_policy_hnd(&domain_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+ }
+ if (is_valid_policy_hnd(&user_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ }
+ TALLOC_FREE(pipe_hnd);
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS libnet_join_ok(const char *netbios_domain_name,
+ const char *machine_name,
+ const char *dc_name)
+{
+ uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ NTSTATUS status;
+ char *machine_password = NULL;
+ char *machine_account = NULL;
+
+ if (!dc_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!secrets_init()) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ machine_password = secrets_fetch_machine_password(netbios_domain_name,
+ NULL, NULL);
+ if (!machine_password) {
+ return NT_STATUS_NO_TRUST_LSA_SECRET;
+ }
+
+ asprintf(&machine_account, "%s$", machine_name);
+ if (!machine_account) {
+ SAFE_FREE(machine_password);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_full_connection(&cli, NULL,
+ dc_name,
+ NULL, 0,
+ "IPC$", "IPC",
+ machine_account,
+ NULL,
+ machine_password,
+ 0,
+ Undefined, NULL);
+ free(machine_account);
+ free(machine_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ status = cli_full_connection(&cli, NULL,
+ dc_name,
+ NULL, 0,
+ "IPC$", "IPC",
+ "",
+ NULL,
+ "",
+ 0,
+ Undefined, NULL);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = get_schannel_session_key(cli, netbios_domain_name,
+ &neg_flags, &netlogon_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(0,("libnet_join_ok: failed to get schannel session "
+ "key from server %s for domain %s. Error was %s\n",
+ cli->desthost, netbios_domain_name, nt_errstr(status)));
+ cli_shutdown(cli);
+ return status;
+ }
+
+ if (!lp_client_schannel()) {
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ }
+
+ status = cli_rpc_pipe_open_schannel_with_key(
+ cli, &ndr_table_netlogon.syntax_id, PIPE_AUTH_LEVEL_PRIVACY,
+ netbios_domain_name, netlogon_pipe->dc, &pipe_hnd);
+
+ cli_shutdown(cli);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("libnet_join_ok: failed to open schannel session "
+ "on netlogon pipe to server %s for domain %s. "
+ "Error was %s\n",
+ cli->desthost, netbios_domain_name, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ NTSTATUS status;
+
+ status = libnet_join_ok(r->out.netbios_domain_name,
+ r->in.machine_name,
+ r->in.dc_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to verify domain membership after joining: %s",
+ get_friendly_nt_error_msg(status));
+ return WERR_SETUP_NOT_JOINED;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ if (!secrets_delete_machine_password_ex(lp_workgroup())) {
+ return false;
+ }
+
+ if (!secrets_delete_domain_sid(lp_workgroup())) {
+ return false;
+ }
+
+ return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ POLICY_HND sam_pol, domain_pol, user_pol;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *acct_name;
+ uint32_t user_rid;
+ struct lsa_String lsa_acct_name;
+ struct samr_Ids user_rids;
+ struct samr_Ids name_types;
+ union samr_UserInfo *info = NULL;
+
+ ZERO_STRUCT(sam_pol);
+ ZERO_STRUCT(domain_pol);
+ ZERO_STRUCT(user_pol);
+
+ status = libnet_join_connect_dc_ipc(r->in.dc_name,
+ r->in.admin_account,
+ r->in.admin_password,
+ r->in.use_kerberos,
+ &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* Open the domain */
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Error connecting to SAM pipe. Error was %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &sam_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &sam_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ r->in.domain_sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* Create domain user */
+
+ acct_name = talloc_asprintf(mem_ctx, "%s$", r->in.machine_name);
+ strlower_m(acct_name);
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ status = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (name_types.ids[0] != SID_NAME_USER) {
+ DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name,
+ name_types.ids[0]));
+ status = NT_STATUS_INVALID_WORKSTATION;
+ goto done;
+ }
+
+ user_rid = user_rids.ids[0];
+
+ /* Open handle on user */
+
+ status = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+ &domain_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ user_rid,
+ &user_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* Get user info */
+
+ status = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
+ &user_pol,
+ 16,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ goto done;
+ }
+
+ /* now disable and setuser info */
+
+ info->info16.acct_flags |= ACB_DISABLED;
+
+ status = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+ &user_pol,
+ 16,
+ info);
+
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+
+done:
+ if (pipe_hnd) {
+ if (is_valid_policy_hnd(&domain_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+ }
+ if (is_valid_policy_hnd(&sam_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &sam_pol);
+ }
+ TALLOC_FREE(pipe_hnd);
+ }
+
+ if (cli) {
+ cli_shutdown(cli);
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR do_join_modify_vals_config(struct libnet_JoinCtx *r)
+{
+ WERROR werr;
+ struct smbconf_ctx *ctx;
+
+ werr = smbconf_init_reg(r, &ctx, NULL);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
+
+ werr = smbconf_set_global_parameter(ctx, "security", "user");
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = smbconf_set_global_parameter(ctx, "workgroup",
+ r->in.domain_name);
+
+ smbconf_delete_global_parameter(ctx, "realm");
+ goto done;
+ }
+
+ werr = smbconf_set_global_parameter(ctx, "security", "domain");
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = smbconf_set_global_parameter(ctx, "workgroup",
+ r->out.netbios_domain_name);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ if (r->out.domain_is_ad) {
+ werr = smbconf_set_global_parameter(ctx, "security", "ads");
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = smbconf_set_global_parameter(ctx, "realm",
+ r->out.dns_domain_name);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+ }
+
+ done:
+ smbconf_shutdown(ctx);
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR do_unjoin_modify_vals_config(struct libnet_UnjoinCtx *r)
+{
+ WERROR werr = WERR_OK;
+ struct smbconf_ctx *ctx;
+
+ werr = smbconf_init_reg(r, &ctx, NULL);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+
+ werr = smbconf_set_global_parameter(ctx, "security", "user");
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = smbconf_delete_global_parameter(ctx, "workgroup");
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ smbconf_delete_global_parameter(ctx, "realm");
+ }
+
+ done:
+ smbconf_shutdown(ctx);
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR do_JoinConfig(struct libnet_JoinCtx *r)
+{
+ WERROR werr;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ return r->out.result;
+ }
+
+ if (!r->in.modify_config) {
+ return WERR_OK;
+ }
+
+ werr = do_join_modify_vals_config(r);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
+
+ r->out.modified_config = true;
+ r->out.result = werr;
+
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_unjoin_config(struct libnet_UnjoinCtx *r)
+{
+ WERROR werr;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ return r->out.result;
+ }
+
+ if (!r->in.modify_config) {
+ return WERR_OK;
+ }
+
+ werr = do_unjoin_modify_vals_config(r);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ lp_load(get_dyn_CONFIGFILE(),true,false,false,true);
+
+ r->out.modified_config = true;
+ r->out.result = werr;
+
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool libnet_parse_domain_dc(TALLOC_CTX *mem_ctx,
+ const char *domain_str,
+ const char **domain_p,
+ const char **dc_p)
+{
+ char *domain = NULL;
+ char *dc = NULL;
+ const char *p = NULL;
+
+ if (!domain_str || !domain_p || !dc_p) {
+ return false;
+ }
+
+ p = strchr_m(domain_str, '\\');
+
+ if (p != NULL) {
+ domain = talloc_strndup(mem_ctx, domain_str,
+ PTR_DIFF(p, domain_str));
+ dc = talloc_strdup(mem_ctx, p+1);
+ if (!dc) {
+ return false;
+ }
+ } else {
+ domain = talloc_strdup(mem_ctx, domain_str);
+ dc = NULL;
+ }
+ if (!domain) {
+ return false;
+ }
+
+ *domain_p = domain;
+
+ if (!*dc_p && dc) {
+ *dc_p = dc;
+ }
+
+ return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ if (!r->in.domain_name) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "No domain name defined");
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
+ &r->in.domain_name,
+ &r->in.dc_name)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "Failed to parse domain name");
+ return WERR_INVALID_PARAM;
+ }
+
+ if (IS_DC) {
+ return WERR_SETUP_DOMAIN_CONTROLLER;
+ }
+
+ if (!secrets_init()) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "Unable to open secrets database");
+ return WERR_CAN_NOT_COMPLETE;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static void libnet_join_add_dom_rids_to_builtins(struct dom_sid *domain_sid)
+{
+ NTSTATUS status;
+
+ /* Try adding dom admins to builtin\admins. Only log failures. */
+ status = create_builtin_administrators(domain_sid);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
+ DEBUG(10,("Unable to auto-add domain administrators to "
+ "BUILTIN\\Administrators during join because "
+ "winbindd must be running."));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("Failed to auto-add domain administrators to "
+ "BUILTIN\\Administrators during join: %s\n",
+ nt_errstr(status)));
+ }
+
+ /* Try adding dom users to builtin\users. Only log failures. */
+ status = create_builtin_users(domain_sid);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) {
+ DEBUG(10,("Unable to auto-add domain users to BUILTIN\\users "
+ "during join because winbindd must be running."));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("Failed to auto-add domain administrators to "
+ "BUILTIN\\Administrators during join: %s\n",
+ nt_errstr(status)));
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ WERROR werr;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ return r->out.result;
+ }
+
+ werr = do_JoinConfig(r);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) {
+ return WERR_OK;
+ }
+
+ saf_store(r->in.domain_name, r->in.dc_name);
+
+#ifdef WITH_ADS
+ if (r->out.domain_is_ad) {
+ ADS_STATUS ads_status;
+
+ ads_status = libnet_join_post_processing_ads(mem_ctx, r);
+ if (!ADS_ERR_OK(ads_status)) {
+ return WERR_GENERAL_FAILURE;
+ }
+ }
+#endif /* WITH_ADS */
+
+ libnet_join_add_dom_rids_to_builtins(r->out.domain_sid);
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static int libnet_destroy_JoinCtx(struct libnet_JoinCtx *r)
+{
+ const char *krb5_cc_env = NULL;
+
+ if (r->in.ads) {
+ ads_destroy(&r->in.ads);
+ }
+
+ krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+ if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
+ unsetenv(KRB5_ENV_CCNAME);
+ }
+
+ return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+static int libnet_destroy_UnjoinCtx(struct libnet_UnjoinCtx *r)
+{
+ const char *krb5_cc_env = NULL;
+
+ if (r->in.ads) {
+ ads_destroy(&r->in.ads);
+ }
+
+ krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+ if (krb5_cc_env && StrCaseCmp(krb5_cc_env, "MEMORY:libnetjoin")) {
+ unsetenv(KRB5_ENV_CCNAME);
+ }
+
+ return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx **r)
+{
+ struct libnet_JoinCtx *ctx;
+ const char *krb5_cc_env = NULL;
+
+ ctx = talloc_zero(mem_ctx, struct libnet_JoinCtx);
+ if (!ctx) {
+ return WERR_NOMEM;
+ }
+
+ talloc_set_destructor(ctx, libnet_destroy_JoinCtx);
+
+ ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
+ W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
+
+ krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+ if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
+ krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
+ W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
+ setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
+ }
+
+ ctx->in.secure_channel_type = SEC_CHAN_WKSTA;
+
+ *r = ctx;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx **r)
+{
+ struct libnet_UnjoinCtx *ctx;
+ const char *krb5_cc_env = NULL;
+
+ ctx = talloc_zero(mem_ctx, struct libnet_UnjoinCtx);
+ if (!ctx) {
+ return WERR_NOMEM;
+ }
+
+ talloc_set_destructor(ctx, libnet_destroy_UnjoinCtx);
+
+ ctx->in.machine_name = talloc_strdup(mem_ctx, global_myname());
+ W_ERROR_HAVE_NO_MEMORY(ctx->in.machine_name);
+
+ krb5_cc_env = getenv(KRB5_ENV_CCNAME);
+ if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) {
+ krb5_cc_env = talloc_strdup(mem_ctx, "MEMORY:libnetjoin");
+ W_ERROR_HAVE_NO_MEMORY(krb5_cc_env);
+ setenv(KRB5_ENV_CCNAME, krb5_cc_env, 1);
+ }
+
+ *r = ctx;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ /* check if configuration is already set correctly */
+
+ switch (r->out.domain_is_ad) {
+ case false:
+ if ((strequal(lp_workgroup(),
+ r->out.netbios_domain_name)) &&
+ (lp_security() == SEC_DOMAIN)) {
+ /* nothing to be done */
+ return WERR_OK;
+ }
+ break;
+ case true:
+ if ((strequal(lp_workgroup(),
+ r->out.netbios_domain_name)) &&
+ (strequal(lp_realm(),
+ r->out.dns_domain_name)) &&
+ ((lp_security() == SEC_ADS) ||
+ (lp_security() == SEC_DOMAIN))) {
+ /* nothing to be done */
+ return WERR_OK;
+ }
+ break;
+ }
+
+ /* check if we are supposed to manipulate configuration */
+
+ if (!r->in.modify_config) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "Invalid configuration and configuration modification "
+ "was not requested");
+ return WERR_CAN_NOT_COMPLETE;
+ }
+
+ /* check if we are able to manipulate configuration */
+
+ if (!lp_config_backend_is_registry()) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "Configuration manipulation requested but not "
+ "supported by backend");
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ NTSTATUS status;
+ WERROR werr;
+ struct cli_state *cli = NULL;
+#ifdef WITH_ADS
+ ADS_STATUS ads_status;
+#endif /* WITH_ADS */
+
+ if (!r->in.dc_name) {
+ struct netr_DsRGetDCNameInfo *info;
+ const char *dc;
+ status = dsgetdcname(mem_ctx,
+ r->in.msg_ctx,
+ r->in.domain_name,
+ NULL,
+ NULL,
+ DS_DIRECTORY_SERVICE_REQUIRED |
+ DS_WRITABLE_REQUIRED |
+ DS_RETURN_DNS_NAME,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to find DC for domain %s",
+ r->in.domain_name,
+ get_friendly_nt_error_msg(status));
+ return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ dc = strip_hostname(info->dc_unc);
+ r->in.dc_name = talloc_strdup(mem_ctx, dc);
+ W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
+ }
+
+ status = libnet_join_lookup_dc_rpc(mem_ctx, r, &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to lookup DC info for domain '%s' over rpc: %s",
+ r->in.domain_name, get_friendly_nt_error_msg(status));
+ return ntstatus_to_werror(status);
+ }
+
+ werr = libnet_join_check_config(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+#ifdef WITH_ADS
+ if (r->out.domain_is_ad && r->in.account_ou) {
+
+ ads_status = libnet_join_connect_ads(mem_ctx, r);
+ if (!ADS_ERR_OK(ads_status)) {
+ return WERR_DEFAULT_JOIN_REQUIRED;
+ }
+
+ ads_status = libnet_join_precreate_machine_acct(mem_ctx, r);
+ if (!ADS_ERR_OK(ads_status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to precreate account in ou %s: %s",
+ r->in.account_ou,
+ ads_errstr(ads_status));
+ return WERR_DEFAULT_JOIN_REQUIRED;
+ }
+
+ r->in.join_flags &= ~WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
+ }
+#endif /* WITH_ADS */
+
+ status = libnet_join_joindomain_rpc(mem_ctx, r, cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_join_set_error_string(mem_ctx, r,
+ "failed to join domain '%s' over rpc: %s",
+ r->in.domain_name, get_friendly_nt_error_msg(status));
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ return WERR_SETUP_ALREADY_JOINED;
+ }
+ werr = ntstatus_to_werror(status);
+ goto done;
+ }
+
+ if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) {
+ werr = WERR_SETUP_NOT_JOINED;
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+ done:
+ if (cli) {
+ cli_shutdown(cli);
+ }
+
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_join_rollback(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ WERROR werr;
+ struct libnet_UnjoinCtx *u = NULL;
+
+ werr = libnet_init_UnjoinCtx(mem_ctx, &u);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ u->in.debug = r->in.debug;
+ u->in.dc_name = r->in.dc_name;
+ u->in.domain_name = r->in.domain_name;
+ u->in.admin_account = r->in.admin_account;
+ u->in.admin_password = r->in.admin_password;
+ u->in.modify_config = r->in.modify_config;
+ u->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+
+ werr = libnet_Unjoin(mem_ctx, u);
+ TALLOC_FREE(u);
+
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_Join(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r)
+{
+ WERROR werr;
+
+ if (r->in.debug) {
+ LIBNET_JOIN_IN_DUMP_CTX(mem_ctx, r);
+ }
+
+ werr = libnet_join_pre_processing(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+ werr = libnet_DomainJoin(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ }
+
+ werr = libnet_join_post_processing(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+ werr = libnet_join_post_verify(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ libnet_join_rollback(mem_ctx, r);
+ }
+ }
+
+ done:
+ r->out.result = werr;
+
+ if (r->in.debug) {
+ LIBNET_JOIN_OUT_DUMP_CTX(mem_ctx, r);
+ }
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ NTSTATUS status;
+
+ if (!r->in.domain_sid) {
+ struct dom_sid sid;
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "Unable to fetch domain sid: are we joined?");
+ return WERR_SETUP_NOT_JOINED;
+ }
+ r->in.domain_sid = sid_dup_talloc(mem_ctx, &sid);
+ W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid);
+ }
+
+ if (!r->in.dc_name) {
+ struct netr_DsRGetDCNameInfo *info;
+ const char *dc;
+ status = dsgetdcname(mem_ctx,
+ r->in.msg_ctx,
+ r->in.domain_name,
+ NULL,
+ NULL,
+ DS_DIRECTORY_SERVICE_REQUIRED |
+ DS_WRITABLE_REQUIRED |
+ DS_RETURN_DNS_NAME,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "failed to find DC for domain %s",
+ r->in.domain_name,
+ get_friendly_nt_error_msg(status));
+ return WERR_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ dc = strip_hostname(info->dc_unc);
+ r->in.dc_name = talloc_strdup(mem_ctx, dc);
+ W_ERROR_HAVE_NO_MEMORY(r->in.dc_name);
+ }
+
+ status = libnet_join_unjoindomain_rpc(mem_ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "failed to disable machine account via rpc: %s",
+ get_friendly_nt_error_msg(status));
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return WERR_SETUP_NOT_JOINED;
+ }
+ return ntstatus_to_werror(status);
+ }
+
+ r->out.disabled_machine_account = true;
+
+#ifdef WITH_ADS
+ if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE) {
+ ADS_STATUS ads_status;
+ libnet_unjoin_connect_ads(mem_ctx, r);
+ ads_status = libnet_unjoin_remove_machine_acct(mem_ctx, r);
+ if (!ADS_ERR_OK(ads_status)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "failed to remove machine account from AD: %s",
+ ads_errstr(ads_status));
+ } else {
+ r->out.deleted_machine_account = true;
+ /* dirty hack */
+ r->out.dns_domain_name = talloc_strdup(mem_ctx,
+ r->in.ads->server.realm);
+ W_ERROR_HAVE_NO_MEMORY(r->out.dns_domain_name);
+ }
+ }
+#endif /* WITH_ADS */
+
+ libnet_join_unjoindomain_remove_secrets(mem_ctx, r);
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_unjoin_pre_processing(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ if (!r->in.domain_name) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "No domain name defined");
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name,
+ &r->in.domain_name,
+ &r->in.dc_name)) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "Failed to parse domain name");
+ return WERR_INVALID_PARAM;
+ }
+
+ if (IS_DC) {
+ return WERR_SETUP_DOMAIN_CONTROLLER;
+ }
+
+ if (!secrets_init()) {
+ libnet_unjoin_set_error_string(mem_ctx, r,
+ "Unable to open secrets database");
+ return WERR_CAN_NOT_COMPLETE;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR libnet_unjoin_post_processing(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ saf_delete(r->out.netbios_domain_name);
+ saf_delete(r->out.dns_domain_name);
+
+ return libnet_unjoin_config(r);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r)
+{
+ WERROR werr;
+
+ if (r->in.debug) {
+ LIBNET_UNJOIN_IN_DUMP_CTX(mem_ctx, r);
+ }
+
+ werr = libnet_unjoin_pre_processing(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (r->in.unjoin_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) {
+ werr = libnet_DomainUnjoin(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ libnet_unjoin_config(r);
+ goto done;
+ }
+ }
+
+ werr = libnet_unjoin_post_processing(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ done:
+ r->out.result = werr;
+
+ if (r->in.debug) {
+ LIBNET_UNJOIN_OUT_DUMP_CTX(mem_ctx, r);
+ }
+
+ return werr;
+}
diff --git a/source3/libnet/libnet_keytab.c b/source3/libnet/libnet_keytab.c
new file mode 100644
index 0000000000..46c17b219c
--- /dev/null
+++ b/source3/libnet/libnet_keytab.c
@@ -0,0 +1,404 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Guenther Deschner 2008.
+ Copyright (C) Michael Adam 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+
+#ifdef HAVE_KRB5
+
+/****************************************************************
+****************************************************************/
+
+static int keytab_close(struct libnet_keytab_context *ctx)
+{
+ if (!ctx) {
+ return 0;
+ }
+
+ if (ctx->keytab && ctx->context) {
+ krb5_kt_close(ctx->context, ctx->keytab);
+ }
+
+ if (ctx->context) {
+ krb5_free_context(ctx->context);
+ }
+
+ if (ctx->ads) {
+ ads_destroy(&ctx->ads);
+ }
+
+ TALLOC_FREE(ctx);
+
+ return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
+ const char *keytab_name,
+ struct libnet_keytab_context **ctx)
+{
+ krb5_error_code ret = 0;
+ krb5_context context = NULL;
+ krb5_keytab keytab = NULL;
+ const char *keytab_string = NULL;
+
+ struct libnet_keytab_context *r;
+
+ r = TALLOC_ZERO_P(mem_ctx, struct libnet_keytab_context);
+ if (!r) {
+ return ENOMEM;
+ }
+
+ talloc_set_destructor(r, keytab_close);
+
+ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("keytab_init: could not krb5_init_context: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab);
+ if (ret) {
+ DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
+ error_message(ret)));
+ krb5_free_context(context);
+ return ret;
+ }
+
+ ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string);
+ if (ret) {
+ krb5_kt_close(context, keytab);
+ krb5_free_context(context);
+ return ret;
+ }
+
+ r->context = context;
+ r->keytab = keytab;
+ r->keytab_name = keytab_string;
+ r->clean_old_entries = false;
+
+ *ctx = r;
+
+ return 0;
+}
+
+/****************************************************************
+****************************************************************/
+
+/**
+ * Remove all entries that have the given principal, kvno and enctype.
+ */
+static krb5_error_code libnet_keytab_remove_entries(krb5_context context,
+ krb5_keytab keytab,
+ const char *principal,
+ int kvno,
+ const krb5_enctype enctype,
+ bool ignore_kvno)
+{
+ krb5_error_code ret;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry kt_entry;
+
+ ZERO_STRUCT(kt_entry);
+ ZERO_STRUCT(cursor);
+
+ ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (ret) {
+ return 0;
+ }
+
+ while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0)
+ {
+ krb5_keyblock *keyp;
+ char *princ_s = NULL;
+
+ if (kt_entry.vno != kvno && !ignore_kvno) {
+ goto cont;
+ }
+
+ keyp = KRB5_KT_KEY(&kt_entry);
+
+ if (KRB5_KEY_TYPE(keyp) != enctype) {
+ goto cont;
+ }
+
+ ret = smb_krb5_unparse_name(context, kt_entry.principal,
+ &princ_s);
+ if (ret) {
+ DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n",
+ error_message(ret)));
+ goto cont;
+ }
+
+ if (strcmp(principal, princ_s) != 0) {
+ goto cont;
+ }
+
+ /* match found - remove */
+
+ DEBUG(10, ("found entry for principal %s, kvno %d, "
+ "enctype %d - trying to remove it\n",
+ princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp)));
+
+ ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+ ZERO_STRUCT(cursor);
+ if (ret) {
+ DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
+ error_message(ret)));
+ goto cont;
+ }
+
+ ret = krb5_kt_remove_entry(context, keytab,
+ &kt_entry);
+ if (ret) {
+ DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n",
+ error_message(ret)));
+ goto cont;
+ }
+ DEBUG(10, ("removed entry for principal %s, kvno %d, "
+ "enctype %d\n", princ_s, kt_entry.vno,
+ KRB5_KEY_TYPE(keyp)));
+
+ ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (ret) {
+ DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n",
+ error_message(ret)));
+ goto cont;
+ }
+
+cont:
+ smb_krb5_kt_free_entry(context, &kt_entry);
+ SAFE_FREE(princ_s);
+ }
+
+ ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (ret) {
+ DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
+ error_message(ret)));
+ }
+
+ return ret;
+}
+
+static krb5_error_code libnet_keytab_add_entry(krb5_context context,
+ krb5_keytab keytab,
+ krb5_kvno kvno,
+ const char *princ_s,
+ krb5_enctype enctype,
+ krb5_data password)
+{
+ krb5_keyblock *keyp;
+ krb5_keytab_entry kt_entry;
+ krb5_error_code ret;
+
+ /* remove duplicates first ... */
+ ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
+ enctype, false);
+ if (ret) {
+ DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
+ error_message(ret)));
+ }
+
+ ZERO_STRUCT(kt_entry);
+
+ kt_entry.vno = kvno;
+
+ ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
+ if (ret) {
+ DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
+ princ_s, error_message(ret)));
+ return ret;
+ }
+
+ keyp = KRB5_KT_KEY(&kt_entry);
+
+ if (create_kerberos_key_from_string(context, kt_entry.principal,
+ &password, keyp, enctype, true))
+ {
+ ret = KRB5KRB_ERR_GENERIC;
+ goto done;
+ }
+
+ ret = krb5_kt_add_entry(context, keytab, &kt_entry);
+ if (ret) {
+ DEBUG(1, ("adding entry to keytab failed (%s)\n",
+ error_message(ret)));
+ }
+
+done:
+ krb5_free_keyblock_contents(context, keyp);
+ krb5_free_principal(context, kt_entry.principal);
+ ZERO_STRUCT(kt_entry);
+ smb_krb5_kt_free_entry(context, &kt_entry);
+
+ return ret;
+}
+
+krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
+{
+ krb5_error_code ret = 0;
+ uint32_t i;
+
+
+ if (ctx->clean_old_entries) {
+ DEBUG(0, ("cleaning old entries...\n"));
+ for (i=0; i < ctx->count; i++) {
+ struct libnet_keytab_entry *entry = &ctx->entries[i];
+
+ ret = libnet_keytab_remove_entries(ctx->context,
+ ctx->keytab,
+ entry->principal,
+ 0,
+ entry->enctype,
+ true);
+ if (ret) {
+ DEBUG(1,("libnet_keytab_add: Failed to remove "
+ "old entries for %s (enctype %u): %s\n",
+ entry->principal, entry->enctype,
+ error_message(ret)));
+ return ret;
+ }
+ }
+ }
+
+ for (i=0; i<ctx->count; i++) {
+
+ struct libnet_keytab_entry *entry = &ctx->entries[i];
+ krb5_data password;
+
+ ZERO_STRUCT(password);
+ password.data = (char *)entry->password.data;
+ password.length = entry->password.length;
+
+ ret = libnet_keytab_add_entry(ctx->context,
+ ctx->keytab,
+ entry->kvno,
+ entry->principal,
+ entry->enctype,
+ password);
+ if (ret) {
+ DEBUG(1,("libnet_keytab_add: "
+ "Failed to add entry to keytab file\n"));
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
+ const char *principal,
+ int kvno,
+ const krb5_enctype enctype,
+ TALLOC_CTX *mem_ctx)
+{
+ krb5_error_code ret = 0;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry kt_entry;
+ struct libnet_keytab_entry *entry = NULL;
+
+ ZERO_STRUCT(kt_entry);
+ ZERO_STRUCT(cursor);
+
+ ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
+ if (ret) {
+ DEBUG(10, ("krb5_kt_start_seq_get failed: %s",
+ error_message(ret)));
+ return NULL;
+ }
+
+ while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
+ {
+ krb5_keyblock *keyp;
+ char *princ_s = NULL;
+
+ if (kt_entry.vno != kvno) {
+ goto cont;
+ }
+
+ keyp = KRB5_KT_KEY(&kt_entry);
+
+ if (KRB5_KEY_TYPE(keyp) != enctype) {
+ goto cont;
+ }
+
+ ret = smb_krb5_unparse_name(ctx->context, kt_entry.principal,
+ &princ_s);
+ if (ret) {
+ goto cont;
+ }
+
+ if (strcmp(principal, princ_s) != 0) {
+ goto cont;
+ }
+
+ entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
+ if (!entry) {
+ DEBUG(3, ("talloc failed\n"));
+ goto fail;
+ }
+
+ entry->name = talloc_strdup(entry, princ_s);
+ if (!entry->name) {
+ DEBUG(3, ("talloc_strdup_failed\n"));
+ goto fail;
+ }
+
+ entry->principal = talloc_strdup(entry, princ_s);
+ if (!entry->principal) {
+ DEBUG(3, ("talloc_strdup_failed\n"));
+ goto fail;
+ }
+
+ entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
+ KRB5_KEY_LENGTH(keyp));
+ if (!entry->password.data) {
+ DEBUG(3, ("data_blob_talloc failed\n"));
+ goto fail;
+ }
+
+ DEBUG(10, ("found entry\n"));
+
+ smb_krb5_kt_free_entry(ctx->context, &kt_entry);
+ SAFE_FREE(princ_s);
+ break;
+
+fail:
+ smb_krb5_kt_free_entry(ctx->context, &kt_entry);
+ SAFE_FREE(princ_s);
+ TALLOC_FREE(entry);
+ break;
+
+cont:
+ smb_krb5_kt_free_entry(ctx->context, &kt_entry);
+ SAFE_FREE(princ_s);
+ continue;
+ }
+
+ krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
+ return entry;
+}
+
+#endif /* HAVE_KRB5 */
diff --git a/source3/libnet/libnet_keytab.h b/source3/libnet/libnet_keytab.h
new file mode 100644
index 0000000000..4d311a48e0
--- /dev/null
+++ b/source3/libnet/libnet_keytab.h
@@ -0,0 +1,42 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libnet Support
+ * Copyright (C) Guenther Deschner 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_KRB5
+
+struct libnet_keytab_entry {
+ const char *name;
+ const char *principal;
+ DATA_BLOB password;
+ uint32_t kvno;
+ krb5_enctype enctype;
+};
+
+struct libnet_keytab_context {
+ krb5_context context;
+ krb5_keytab keytab;
+ const char *keytab_name;
+ ADS_STRUCT *ads;
+ const char *dns_domain_name;
+ uint8_t zero_buf[16];
+ uint32_t count;
+ struct libnet_keytab_entry *entries;
+ bool clean_old_entries;
+};
+
+#endif /* HAVE_KRB5 */
diff --git a/source3/libnet/libnet_proto.h b/source3/libnet/libnet_proto.h
new file mode 100644
index 0000000000..43046a44c0
--- /dev/null
+++ b/source3/libnet/libnet_proto.h
@@ -0,0 +1,78 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * collected prototypes header
+ *
+ * frozen from "make proto" in May 2008
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBNET_PROTO_H_
+#define _LIBNET_PROTO_H_
+
+
+/* The following definitions come from libnet/libnet_join.c */
+
+NTSTATUS libnet_join_ok(const char *netbios_domain_name,
+ const char *machine_name,
+ const char *dc_name);
+WERROR libnet_init_JoinCtx(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx **r);
+WERROR libnet_init_UnjoinCtx(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx **r);
+WERROR libnet_Join(TALLOC_CTX *mem_ctx,
+ struct libnet_JoinCtx *r);
+WERROR libnet_Unjoin(TALLOC_CTX *mem_ctx,
+ struct libnet_UnjoinCtx *r);
+
+/* The following definitions come from librpc/gen_ndr/ndr_libnet_join.c */
+
+_PUBLIC_ void ndr_print_libnet_JoinCtx(struct ndr_print *ndr, const char *name, int flags, const struct libnet_JoinCtx *r);
+_PUBLIC_ void ndr_print_libnet_UnjoinCtx(struct ndr_print *ndr, const char *name, int flags, const struct libnet_UnjoinCtx *r);
+
+/* The following definitions come from libnet/libnet_keytab.c */
+
+#ifdef HAVE_KRB5
+krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
+ const char *keytab_name,
+ struct libnet_keytab_context **ctx);
+krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx);
+
+struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
+ const char *principal, int kvno,
+ const const krb5_enctype enctype,
+ TALLOC_CTX *mem_ctx);
+#endif
+
+/* The following definitions come from libnet/libnet_samsync.c */
+
+NTSTATUS libnet_samsync_init_context(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ struct samsync_context **ctx_p);
+NTSTATUS libnet_samsync(enum netr_SamDatabaseID database_id,
+ struct samsync_context *ctx);
+NTSTATUS pull_netr_AcctLockStr(TALLOC_CTX *mem_ctx,
+ struct lsa_BinaryString *r,
+ struct netr_AcctLockStr **str_p);
+
+/* The following definitions come from libnet/libnet_dssync.c */
+
+NTSTATUS libnet_dssync_init_context(TALLOC_CTX *mem_ctx,
+ struct dssync_context **ctx_p);
+NTSTATUS libnet_dssync(TALLOC_CTX *mem_ctx,
+ struct dssync_context *ctx);
+
+#endif /* _LIBNET_PROTO_H_ */
diff --git a/source3/libnet/libnet_samsync.c b/source3/libnet/libnet_samsync.c
new file mode 100644
index 0000000000..daf27ffb51
--- /dev/null
+++ b/source3/libnet/libnet_samsync.c
@@ -0,0 +1,411 @@
+/*
+ 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) Guenther Deschner <gd@samba.org> 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.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,
+ DATA_BLOB *session_key,
+ bool rid_crypt,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM *delta)
+{
+
+ 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;
+
+ if (rid_crypt) {
+ if (user->lm_password_present) {
+ sam_pwd_hash(rid, user->lmpassword.hash, lm_hash.hash, 0);
+ user->lmpassword = lm_hash;
+ }
+
+ if (user->nt_password_present) {
+ sam_pwd_hash(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;
+ enum ndr_err_code ndr_err;
+ data.data = user->user_private_info.SensitiveData;
+ data.length = user->user_private_info.DataLength;
+ SamOEMhashBlob(data.data, data.length, session_key);
+ user->user_private_info.SensitiveData = data.data;
+ user->user_private_info.DataLength = data.length;
+
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, &keys,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dump_data(10, data.data, data.length);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (keys.keys.keys2.lmpassword.length == 16) {
+ if (rid_crypt) {
+ sam_pwd_hash(rid,
+ keys.keys.keys2.lmpassword.pwd.hash,
+ lm_hash.hash, 0);
+ user->lmpassword = lm_hash;
+ } else {
+ user->lmpassword = keys.keys.keys2.lmpassword.pwd;
+ }
+ user->lm_password_present = true;
+ }
+ if (keys.keys.keys2.ntpassword.length == 16) {
+ if (rid_crypt) {
+ sam_pwd_hash(rid,
+ keys.keys.keys2.ntpassword.pwd.hash,
+ nt_hash.hash, 0);
+ user->ntpassword = nt_hash;
+ } else {
+ user->ntpassword = keys.keys.keys2.ntpassword.pwd;
+ }
+ user->nt_password_present = true;
+ }
+ /* TODO: rid decrypt history fields */
+ }
+ 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,
+ DATA_BLOB *session_key,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
+
+ SamOEMhashBlob(secret->current_cipher.cipher_data,
+ secret->current_cipher.maxlen,
+ session_key);
+
+ SamOEMhashBlob(secret->old_cipher.cipher_data,
+ secret->old_cipher.maxlen,
+ session_key);
+
+ 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 samsync_fix_delta(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ bool rid_crypt,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM *delta)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ switch (delta->delta_type) {
+ case NETR_DELTA_USER:
+
+ status = fix_user(mem_ctx,
+ session_key,
+ rid_crypt,
+ database_id,
+ delta);
+ break;
+ case NETR_DELTA_SECRET:
+
+ status = fix_secret(mem_ctx,
+ session_key,
+ database_id,
+ delta);
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+/**
+ * Fix up the delta, dealing with encryption issues so that the final
+ * callback need only do the printing or application logic
+ */
+
+static NTSTATUS samsync_fix_delta_array(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ bool rid_crypt,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r)
+{
+ NTSTATUS status;
+ int i;
+
+ for (i = 0; i < r->num_deltas; i++) {
+
+ status = samsync_fix_delta(mem_ctx,
+ session_key,
+ rid_crypt,
+ database_id,
+ &r->delta_enum[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * libnet_samsync_init_context
+ */
+
+NTSTATUS libnet_samsync_init_context(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ struct samsync_context **ctx_p)
+{
+ struct samsync_context *ctx;
+
+ *ctx_p = NULL;
+
+ ctx = TALLOC_ZERO_P(mem_ctx, struct samsync_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ if (domain_sid) {
+ ctx->domain_sid = sid_dup_talloc(mem_ctx, domain_sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx->domain_sid);
+
+ ctx->domain_sid_str = sid_string_talloc(mem_ctx, ctx->domain_sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx->domain_sid_str);
+ }
+
+ *ctx_p = ctx;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * samsync_database_str
+ */
+
+static const char *samsync_database_str(enum netr_SamDatabaseID database_id)
+{
+
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ return "DOMAIN";
+ case SAM_DATABASE_BUILTIN:
+ return "BUILTIN";
+ case SAM_DATABASE_PRIVS:
+ return "PRIVS";
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * samsync_debug_str
+ */
+
+static const char *samsync_debug_str(TALLOC_CTX *mem_ctx,
+ enum net_samsync_mode mode,
+ enum netr_SamDatabaseID database_id)
+{
+ const char *action = NULL;
+
+ switch (mode) {
+ case NET_SAMSYNC_MODE_DUMP:
+ action = "Dumping (to stdout)";
+ break;
+ case NET_SAMSYNC_MODE_FETCH_PASSDB:
+ action = "Fetching (to passdb)";
+ break;
+ case NET_SAMSYNC_MODE_FETCH_LDIF:
+ action = "Fetching (to ldif)";
+ break;
+ case NET_SAMSYNC_MODE_FETCH_KEYTAB:
+ action = "Fetching (to keytab)";
+ break;
+ default:
+ action = "Unknown";
+ break;
+ }
+
+ return talloc_asprintf(mem_ctx, "%s %s database",
+ action, samsync_database_str(database_id));
+}
+
+/**
+ * libnet_samsync
+ */
+
+NTSTATUS libnet_samsync(enum netr_SamDatabaseID database_id,
+ struct samsync_context *ctx)
+{
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+ const char *logon_server = ctx->cli->desthost;
+ const char *computername = global_myname();
+ struct netr_Authenticator credential;
+ struct netr_Authenticator return_authenticator;
+ uint16_t restart_state = 0;
+ uint32_t sync_context = 0;
+ const char *debug_str;
+ DATA_BLOB session_key;
+
+ ZERO_STRUCT(return_authenticator);
+
+ if (!(mem_ctx = talloc_init("libnet_samsync"))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ debug_str = samsync_debug_str(mem_ctx, ctx->mode, database_id);
+ if (debug_str) {
+ d_fprintf(stderr, "%s\n", debug_str);
+ }
+
+ do {
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ NTSTATUS callback_status;
+
+ netlogon_creds_client_step(ctx->cli->dc, &credential);
+
+ result = rpccli_netr_DatabaseSync2(ctx->cli, mem_ctx,
+ logon_server,
+ computername,
+ &credential,
+ &return_authenticator,
+ database_id,
+ restart_state,
+ &sync_context,
+ &delta_enum_array,
+ 0xffff);
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
+ return result;
+ }
+
+ /* Check returned credentials. */
+ if (!netlogon_creds_client_check(ctx->cli->dc,
+ &return_authenticator.cred)) {
+ DEBUG(0,("credentials chain check failed\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_STATUS_IS_ERR(result)) {
+ break;
+ }
+
+ session_key = data_blob_const(ctx->cli->dc->sess_key, 16);
+
+ samsync_fix_delta_array(mem_ctx,
+ &session_key,
+ false,
+ database_id,
+ delta_enum_array);
+
+ /* Process results */
+ callback_status = ctx->delta_fn(mem_ctx, database_id,
+ delta_enum_array,
+ NT_STATUS_IS_OK(result), ctx);
+ if (!NT_STATUS_IS_OK(callback_status)) {
+ result = callback_status;
+ goto out;
+ }
+
+ TALLOC_FREE(delta_enum_array);
+
+ /* Increment sync_context */
+ sync_context += 1;
+
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ out:
+ if (NT_STATUS_IS_ERR(result) && !ctx->error_message) {
+
+ ctx->error_message = talloc_asprintf(ctx,
+ "Failed to fetch %s database: %s",
+ samsync_database_str(database_id),
+ nt_errstr(result));
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
+
+ ctx->error_message =
+ talloc_asprintf_append(ctx->error_message,
+ "\nPerhaps %s is a Windows native mode domain?",
+ ctx->domain_name);
+ }
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/**
+ * pull_netr_AcctLockStr
+ */
+
+NTSTATUS pull_netr_AcctLockStr(TALLOC_CTX *mem_ctx,
+ struct lsa_BinaryString *r,
+ struct netr_AcctLockStr **str_p)
+{
+ struct netr_AcctLockStr *str;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+
+ if (!mem_ctx || !r || !str_p) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *str_p = NULL;
+
+ str = TALLOC_ZERO_P(mem_ctx, struct netr_AcctLockStr);
+ if (!str) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ blob = data_blob_const(r->array, r->length);
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, str,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_AcctLockStr);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ *str_p = str;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source3/libnet/libnet_samsync.h b/source3/libnet/libnet_samsync.h
new file mode 100644
index 0000000000..1f10d2c1c0
--- /dev/null
+++ b/source3/libnet/libnet_samsync.h
@@ -0,0 +1,73 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libnet Support
+ * Copyright (C) Guenther Deschner 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+enum net_samsync_mode {
+ NET_SAMSYNC_MODE_FETCH_PASSDB = 0,
+ NET_SAMSYNC_MODE_FETCH_LDIF = 1,
+ NET_SAMSYNC_MODE_FETCH_KEYTAB = 2,
+ NET_SAMSYNC_MODE_DUMP = 3
+};
+
+struct samsync_context;
+
+typedef NTSTATUS (*samsync_delta_fn_t)(TALLOC_CTX *,
+ enum netr_SamDatabaseID,
+ struct netr_DELTA_ENUM_ARRAY *,
+ bool,
+ struct samsync_context *);
+
+struct samsync_context {
+ enum net_samsync_mode mode;
+ const struct dom_sid *domain_sid;
+ const char *domain_sid_str;
+ const char *domain_name;
+ const char *output_filename;
+
+ const char *username;
+ const char *password;
+
+ char *result_message;
+ char *error_message;
+
+ struct rpc_pipe_client *cli;
+ samsync_delta_fn_t delta_fn;
+ void *private_data;
+};
+
+NTSTATUS fetch_sam_entries_ldif(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx);
+NTSTATUS fetch_sam_entries(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx);
+NTSTATUS display_sam_entries(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx);
+NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx);
diff --git a/source3/libnet/libnet_samsync_display.c b/source3/libnet/libnet_samsync_display.c
new file mode 100644
index 0000000000..6f7ae4e7aa
--- /dev/null
+++ b/source3/libnet/libnet_samsync_display.c
@@ -0,0 +1,303 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
+ Modified by Volker Lendecke 2002
+ Copyright (C) Jeremy Allison 2005.
+ Copyright (C) Guenther Deschner 2008.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+
+static void display_group_mem_info(uint32_t rid,
+ struct netr_DELTA_GROUP_MEMBER *r)
+{
+ int i;
+ d_printf("Group mem %u: ", rid);
+ for (i=0; i< r->num_rids; i++) {
+ d_printf("%u ", r->rids[i]);
+ }
+ d_printf("\n");
+}
+
+static void display_alias_info(uint32_t rid,
+ struct netr_DELTA_ALIAS *r)
+{
+ d_printf("Alias '%s' ", r->alias_name.string);
+ d_printf("desc='%s' rid=%u\n", r->description.string, r->rid);
+}
+
+static void display_alias_mem(uint32_t rid,
+ struct netr_DELTA_ALIAS_MEMBER *r)
+{
+ int i;
+ d_printf("Alias rid %u: ", rid);
+ for (i=0; i< r->sids.num_sids; i++) {
+ d_printf("%s ", sid_string_tos(r->sids.sids[i].sid));
+ }
+ d_printf("\n");
+}
+
+static void display_account_info(uint32_t rid,
+ struct netr_DELTA_USER *r)
+{
+ fstring hex_nt_passwd, hex_lm_passwd;
+ uchar lm_passwd[16], nt_passwd[16];
+ static uchar zero_buf[16];
+
+ /* Decode hashes from password hash (if they are not NULL) */
+
+ if (memcmp(r->lmpassword.hash, zero_buf, 16) != 0) {
+ sam_pwd_hash(r->rid, r->lmpassword.hash, lm_passwd, 0);
+ pdb_sethexpwd(hex_lm_passwd, lm_passwd, r->acct_flags);
+ } else {
+ pdb_sethexpwd(hex_lm_passwd, NULL, 0);
+ }
+
+ if (memcmp(r->ntpassword.hash, zero_buf, 16) != 0) {
+ sam_pwd_hash(r->rid, r->ntpassword.hash, nt_passwd, 0);
+ pdb_sethexpwd(hex_nt_passwd, nt_passwd, r->acct_flags);
+ } else {
+ pdb_sethexpwd(hex_nt_passwd, NULL, 0);
+ }
+
+ printf("%s:%d:%s:%s:%s:LCT-0\n",
+ r->account_name.string,
+ r->rid, hex_lm_passwd, hex_nt_passwd,
+ pdb_encode_acct_ctrl(r->acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+}
+
+static void display_domain_info(struct netr_DELTA_DOMAIN *r)
+{
+ time_t u_logout;
+ struct netr_AcctLockStr *lockstr = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_tos();
+
+ status = pull_netr_AcctLockStr(mem_ctx, &r->account_lockout,
+ &lockstr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("failed to pull account lockout string: %s\n",
+ nt_errstr(status));
+ }
+
+ u_logout = uint64s_nt_time_to_unix_abs((const uint64 *)&r->force_logoff_time);
+
+ d_printf("Domain name: %s\n", r->domain_name.string);
+
+ d_printf("Minimal Password Length: %d\n", r->min_password_length);
+ d_printf("Password History Length: %d\n", r->password_history_length);
+
+ d_printf("Force Logoff: %d\n", (int)u_logout);
+
+ d_printf("Max Password Age: %s\n", display_time(r->max_password_age));
+ d_printf("Min Password Age: %s\n", display_time(r->min_password_age));
+
+ if (lockstr) {
+ d_printf("Lockout Time: %s\n", display_time((NTTIME)lockstr->lockout_duration));
+ d_printf("Lockout Reset Time: %s\n", display_time((NTTIME)lockstr->reset_count));
+ d_printf("Bad Attempt Lockout: %d\n", lockstr->bad_attempt_lockout);
+ }
+
+ d_printf("User must logon to change password: %d\n", r->logon_to_chgpass);
+}
+
+static void display_group_info(uint32_t rid, struct netr_DELTA_GROUP *r)
+{
+ d_printf("Group '%s' ", r->group_name.string);
+ d_printf("desc='%s', rid=%u\n", r->description.string, rid);
+}
+
+static void display_delete_group(uint32_t rid)
+{
+ d_printf("Delete Group '%d' ", rid);
+}
+
+static void display_rename_group(uint32_t rid, struct netr_DELTA_RENAME *r)
+{
+ d_printf("Rename Group '%d' ", rid);
+ d_printf("Rename Group: %s -> %s\n",
+ r->OldName.string, r->NewName.string);
+}
+
+static void display_delete_user(uint32_t rid)
+{
+ d_printf("Delete User '%d' ", rid);
+}
+
+static void display_rename_user(uint32_t rid, struct netr_DELTA_RENAME *r)
+{
+ d_printf("Rename User '%d' ", rid);
+ d_printf("Rename User: %s -> %s\n",
+ r->OldName.string, r->NewName.string);
+}
+
+static void display_delete_alias(uint32_t rid)
+{
+ d_printf("Delete Alias '%d' ", rid);
+}
+
+static void display_rename_alias(uint32_t rid, struct netr_DELTA_RENAME *r)
+{
+ d_printf("Rename Alias '%d' ", rid);
+ d_printf("Rename Alias: %s -> %s\n",
+ r->OldName.string, r->NewName.string);
+}
+
+static NTSTATUS display_sam_entry(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ union netr_DELTA_UNION u = r->delta_union;
+ union netr_DELTA_ID_UNION id = r->delta_id_union;
+
+ switch (r->delta_type) {
+ case NETR_DELTA_DOMAIN:
+ display_domain_info(u.domain);
+ break;
+ case NETR_DELTA_GROUP:
+ display_group_info(id.rid, u.group);
+ break;
+ case NETR_DELTA_DELETE_GROUP:
+ display_delete_group(id.rid);
+ break;
+ case NETR_DELTA_RENAME_GROUP:
+ display_rename_group(id.rid, u.rename_group);
+ break;
+ case NETR_DELTA_USER:
+ display_account_info(id.rid, u.user);
+ break;
+ case NETR_DELTA_DELETE_USER:
+ display_delete_user(id.rid);
+ break;
+ case NETR_DELTA_RENAME_USER:
+ display_rename_user(id.rid, u.rename_user);
+ break;
+ case NETR_DELTA_GROUP_MEMBER:
+ display_group_mem_info(id.rid, u.group_member);
+ break;
+ case NETR_DELTA_ALIAS:
+ display_alias_info(id.rid, u.alias);
+ break;
+ case NETR_DELTA_DELETE_ALIAS:
+ display_delete_alias(id.rid);
+ break;
+ case NETR_DELTA_RENAME_ALIAS:
+ display_rename_alias(id.rid, u.rename_alias);
+ break;
+ case NETR_DELTA_ALIAS_MEMBER:
+ display_alias_mem(id.rid, u.alias_member);
+ break;
+ case NETR_DELTA_POLICY:
+ printf("Policy\n");
+ break;
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ printf("Trusted Domain: %s\n",
+ u.trusted_domain->domain_name.string);
+ break;
+ case NETR_DELTA_DELETE_TRUST:
+ printf("Delete Trust: %d\n",
+ u.delete_trust.unknown);
+ break;
+ case NETR_DELTA_ACCOUNT:
+ printf("Account\n");
+ break;
+ case NETR_DELTA_DELETE_ACCOUNT:
+ printf("Delete Account: %d\n",
+ u.delete_account.unknown);
+ break;
+ case NETR_DELTA_SECRET:
+ printf("Secret\n");
+ break;
+ case NETR_DELTA_DELETE_SECRET:
+ printf("Delete Secret: %d\n",
+ u.delete_secret.unknown);
+ break;
+ case NETR_DELTA_DELETE_GROUP2:
+ printf("Delete Group2: %s\n",
+ u.delete_group->account_name);
+ break;
+ case NETR_DELTA_DELETE_USER2:
+ printf("Delete User2: %s\n",
+ u.delete_user->account_name);
+ break;
+ case NETR_DELTA_MODIFY_COUNT:
+ printf("sam sequence update: 0x%016llx\n",
+ (unsigned long long) *u.modified_count);
+ break;
+#if 0
+ /* The following types are recognised but not handled */
+ case NETR_DELTA_POLICY:
+ d_printf("NETR_DELTA_POLICY not handled\n");
+ break;
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ d_printf("NETR_DELTA_TRUSTED_DOMAIN not handled\n");
+ break;
+ case NETR_DELTA_ACCOUNT:
+ d_printf("NETR_DELTA_ACCOUNT not handled\n");
+ break;
+ case NETR_DELTA_SECRET:
+ d_printf("NETR_DELTA_SECRET not handled\n");
+ break;
+ case NETR_DELTA_MODIFY_COUNT:
+ d_printf("NETR_DELTA_MODIFY_COUNT not handled\n");
+ break;
+ case NETR_DELTA_DELETE_TRUST:
+ d_printf("NETR_DELTA_DELETE_TRUST not handled\n");
+ break;
+ case NETR_DELTA_DELETE_ACCOUNT:
+ d_printf("NETR_DELTA_DELETE_ACCOUNT not handled\n");
+ break;
+ case NETR_DELTA_DELETE_SECRET:
+ d_printf("NETR_DELTA_DELETE_SECRET not handled\n");
+ break;
+ case NETR_DELTA_DELETE_GROUP2:
+ d_printf("NETR_DELTA_DELETE_GROUP2 not handled\n");
+ break;
+ case NETR_DELTA_DELETE_USER2:
+ d_printf("NETR_DELTA_DELETE_USER2 not handled\n");
+ break;
+#endif
+ default:
+ printf("unknown delta type 0x%02x\n",
+ r->delta_type);
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS display_sam_entries(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ int i;
+
+ for (i = 0; i < r->num_deltas; i++) {
+ display_sam_entry(mem_ctx, database_id, &r->delta_enum[i],
+ last_query, ctx);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/libnet/libnet_samsync_keytab.c b/source3/libnet/libnet_samsync_keytab.c
new file mode 100644
index 0000000000..f284f08ad9
--- /dev/null
+++ b/source3/libnet/libnet_samsync_keytab.c
@@ -0,0 +1,193 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Guenther Deschner 2008.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+
+#if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS keytab_ad_connect(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *username,
+ const char *password,
+ struct libnet_keytab_context *ctx)
+{
+ NTSTATUS status;
+ ADS_STATUS ad_status;
+ ADS_STRUCT *ads;
+ struct netr_DsRGetDCNameInfo *info = NULL;
+ const char *dc;
+
+ status = dsgetdcname(mem_ctx, NULL, domain_name, NULL, NULL, 0, &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dc = strip_hostname(info->dc_unc);
+
+ ads = ads_init(NULL, domain_name, dc);
+ NT_STATUS_HAVE_NO_MEMORY(ads);
+
+ if (getenv(KRB5_ENV_CCNAME) == NULL) {
+ setenv(KRB5_ENV_CCNAME, "MEMORY:libnet_samsync_keytab", 1);
+ }
+
+ ads->auth.user_name = SMB_STRDUP(username);
+ ads->auth.password = SMB_STRDUP(password);
+
+ ad_status = ads_connect_user_creds(ads);
+ if (!ADS_ERR_OK(ad_status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ctx->ads = ads;
+
+ ctx->dns_domain_name = talloc_strdup_upper(mem_ctx, ads->config.realm);
+ NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_sam_entry_keytab(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ uint32_t rid,
+ struct netr_DELTA_USER *r,
+ bool last_query,
+ struct libnet_keytab_context *ctx)
+{
+ uchar nt_passwd[16];
+ struct libnet_keytab_entry entry;
+
+ if (memcmp(r->ntpassword.hash, ctx->zero_buf, 16) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sam_pwd_hash(rid, r->ntpassword.hash, nt_passwd, 0);
+
+ entry.name = talloc_strdup(mem_ctx, r->account_name.string);
+ entry.principal = talloc_asprintf(mem_ctx, "%s@%s",
+ r->account_name.string,
+ ctx->dns_domain_name);
+ entry.password = data_blob_talloc(mem_ctx, nt_passwd, 16);
+ entry.kvno = ads_get_kvno(ctx->ads, entry.name);
+
+ NT_STATUS_HAVE_NO_MEMORY(entry.name);
+ NT_STATUS_HAVE_NO_MEMORY(entry.principal);
+ NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
+
+
+ ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
+ &ctx->entries, &ctx->count);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ krb5_error_code ret = 0;
+ static struct libnet_keytab_context *keytab_ctx = NULL;
+ int i;
+
+ if (!keytab_ctx) {
+ ret = libnet_keytab_init(mem_ctx, ctx->output_filename,
+ &keytab_ctx);
+ if (ret) {
+ status = krb5_to_nt_status(ret);
+ goto out;
+ }
+ }
+
+ status = keytab_ad_connect(mem_ctx,
+ ctx->domain_name,
+ ctx->username,
+ ctx->password,
+ keytab_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ for (i = 0; i < r->num_deltas; i++) {
+
+ if (r->delta_enum[i].delta_type != NETR_DELTA_USER) {
+ continue;
+ }
+
+ status = fetch_sam_entry_keytab(mem_ctx, database_id,
+ r->delta_enum[i].delta_id_union.rid,
+ r->delta_enum[i].delta_union.user,
+ last_query,
+ keytab_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ if (last_query) {
+
+ ret = libnet_keytab_add(keytab_ctx);
+ if (ret) {
+ status = krb5_to_nt_status(ret);
+ ctx->error_message = talloc_asprintf(mem_ctx,
+ "Failed to add entries to keytab %s: %s",
+ keytab_ctx->keytab_name, error_message(ret));
+ goto out;
+ }
+
+ ctx->result_message = talloc_asprintf(mem_ctx,
+ "Vampired %d accounts to keytab %s",
+ keytab_ctx->count,
+ keytab_ctx->keytab_name);
+
+ TALLOC_FREE(keytab_ctx);
+ }
+
+ return NT_STATUS_OK;
+ out:
+ TALLOC_FREE(keytab_ctx);
+
+ return status;
+}
+
+#else
+
+NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+#endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
diff --git a/source3/libnet/libnet_samsync_ldif.c b/source3/libnet/libnet_samsync_ldif.c
new file mode 100644
index 0000000000..cbae22aad3
--- /dev/null
+++ b/source3/libnet/libnet_samsync_ldif.c
@@ -0,0 +1,1229 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
+ Modified by Volker Lendecke 2002
+ Copyright (C) Jeremy Allison 2005.
+ Copyright (C) Guenther Deschner 2008.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet_samsync.h"
+
+#ifdef HAVE_LDAP
+
+/* uid's and gid's for writing deltas to ldif */
+static uint32 ldif_gid = 999;
+static uint32 ldif_uid = 999;
+
+/* Structure for mapping accounts to groups */
+/* Array element is the group rid */
+typedef struct _groupmap {
+ uint32_t rid;
+ uint32_t gidNumber;
+ const char *sambaSID;
+ const char *group_dn;
+} GROUPMAP;
+
+typedef struct _accountmap {
+ uint32_t rid;
+ const char *cn;
+} ACCOUNTMAP;
+
+struct samsync_ldif_context {
+ GROUPMAP *groupmap;
+ ACCOUNTMAP *accountmap;
+ bool initialized;
+ const char *add_template;
+ const char *mod_template;
+ char *add_name;
+ char *mod_name;
+ FILE *add_file;
+ FILE *mod_file;
+ FILE *ldif_file;
+ const char *suffix;
+ int num_alloced;
+};
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS populate_ldap_for_ldif(const char *sid,
+ const char *suffix,
+ const char *builtin_sid,
+ FILE *add_fd)
+{
+ const char *user_suffix, *group_suffix, *machine_suffix, *idmap_suffix;
+ char *user_attr=NULL, *group_attr=NULL;
+ char *suffix_attr;
+ int len;
+
+ /* Get the suffix attribute */
+ suffix_attr = sstring_sub(suffix, '=', ',');
+ if (suffix_attr == NULL) {
+ len = strlen(suffix);
+ suffix_attr = (char*)SMB_MALLOC(len+1);
+ memcpy(suffix_attr, suffix, len);
+ suffix_attr[len] = '\0';
+ }
+
+ /* Write the base */
+ fprintf(add_fd, "# %s\n", suffix);
+ fprintf(add_fd, "dn: %s\n", suffix);
+ fprintf(add_fd, "objectClass: dcObject\n");
+ fprintf(add_fd, "objectClass: organization\n");
+ fprintf(add_fd, "o: %s\n", suffix_attr);
+ fprintf(add_fd, "dc: %s\n", suffix_attr);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ user_suffix = lp_ldap_user_suffix();
+ if (user_suffix == NULL) {
+ SAFE_FREE(suffix_attr);
+ return NT_STATUS_NO_MEMORY;
+ }
+ /* If it exists and is distinct from other containers,
+ Write the Users entity */
+ if (*user_suffix && strcmp(user_suffix, suffix)) {
+ user_attr = sstring_sub(lp_ldap_user_suffix(), '=', ',');
+ fprintf(add_fd, "# %s\n", user_suffix);
+ fprintf(add_fd, "dn: %s\n", user_suffix);
+ fprintf(add_fd, "objectClass: organizationalUnit\n");
+ fprintf(add_fd, "ou: %s\n", user_attr);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+ }
+
+
+ group_suffix = lp_ldap_group_suffix();
+ if (group_suffix == NULL) {
+ SAFE_FREE(suffix_attr);
+ SAFE_FREE(user_attr);
+ return NT_STATUS_NO_MEMORY;
+ }
+ /* If it exists and is distinct from other containers,
+ Write the Groups entity */
+ if (*group_suffix && strcmp(group_suffix, suffix)) {
+ group_attr = sstring_sub(lp_ldap_group_suffix(), '=', ',');
+ fprintf(add_fd, "# %s\n", group_suffix);
+ fprintf(add_fd, "dn: %s\n", group_suffix);
+ fprintf(add_fd, "objectClass: organizationalUnit\n");
+ fprintf(add_fd, "ou: %s\n", group_attr);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+ }
+
+ /* If it exists and is distinct from other containers,
+ Write the Computers entity */
+ machine_suffix = lp_ldap_machine_suffix();
+ if (machine_suffix == NULL) {
+ SAFE_FREE(suffix_attr);
+ SAFE_FREE(user_attr);
+ SAFE_FREE(group_attr);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (*machine_suffix && strcmp(machine_suffix, user_suffix) &&
+ strcmp(machine_suffix, suffix)) {
+ char *machine_ou = NULL;
+ fprintf(add_fd, "# %s\n", machine_suffix);
+ fprintf(add_fd, "dn: %s\n", machine_suffix);
+ fprintf(add_fd, "objectClass: organizationalUnit\n");
+ /* this isn't totally correct as it assumes that
+ there _must_ be an ou. just fixing memleak now. jmcd */
+ machine_ou = sstring_sub(lp_ldap_machine_suffix(), '=', ',');
+ fprintf(add_fd, "ou: %s\n", machine_ou);
+ SAFE_FREE(machine_ou);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+ }
+
+ /* If it exists and is distinct from other containers,
+ Write the IdMap entity */
+ idmap_suffix = lp_ldap_idmap_suffix();
+ if (idmap_suffix == NULL) {
+ SAFE_FREE(suffix_attr);
+ SAFE_FREE(user_attr);
+ SAFE_FREE(group_attr);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (*idmap_suffix &&
+ strcmp(idmap_suffix, user_suffix) &&
+ strcmp(idmap_suffix, suffix)) {
+ char *s;
+ fprintf(add_fd, "# %s\n", idmap_suffix);
+ fprintf(add_fd, "dn: %s\n", idmap_suffix);
+ fprintf(add_fd, "ObjectClass: organizationalUnit\n");
+ s = sstring_sub(lp_ldap_idmap_suffix(), '=', ',');
+ fprintf(add_fd, "ou: %s\n", s);
+ SAFE_FREE(s);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+ }
+
+ /* Write the domain entity */
+ fprintf(add_fd, "# %s, %s\n", lp_workgroup(), suffix);
+ fprintf(add_fd, "dn: sambaDomainName=%s,%s\n", lp_workgroup(),
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_DOMINFO);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_IDPOOL);
+ fprintf(add_fd, "sambaDomainName: %s\n", lp_workgroup());
+ fprintf(add_fd, "sambaSID: %s\n", sid);
+ fprintf(add_fd, "uidNumber: %d\n", ++ldif_uid);
+ fprintf(add_fd, "gidNumber: %d\n", ++ldif_gid);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Domain Admins entity */
+ fprintf(add_fd, "# Domain Admins, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Domain Admins,ou=%s,%s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "cn: Domain Admins\n");
+ fprintf(add_fd, "memberUid: Administrator\n");
+ fprintf(add_fd, "description: Netbios Domain Administrators\n");
+ fprintf(add_fd, "gidNumber: 512\n");
+ fprintf(add_fd, "sambaSID: %s-512\n", sid);
+ fprintf(add_fd, "sambaGroupType: 2\n");
+ fprintf(add_fd, "displayName: Domain Admins\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Domain Users entity */
+ fprintf(add_fd, "# Domain Users, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Domain Users,ou=%s,%s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "cn: Domain Users\n");
+ fprintf(add_fd, "description: Netbios Domain Users\n");
+ fprintf(add_fd, "gidNumber: 513\n");
+ fprintf(add_fd, "sambaSID: %s-513\n", sid);
+ fprintf(add_fd, "sambaGroupType: 2\n");
+ fprintf(add_fd, "displayName: Domain Users\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Domain Guests entity */
+ fprintf(add_fd, "# Domain Guests, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Domain Guests,ou=%s,%s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "cn: Domain Guests\n");
+ fprintf(add_fd, "description: Netbios Domain Guests\n");
+ fprintf(add_fd, "gidNumber: 514\n");
+ fprintf(add_fd, "sambaSID: %s-514\n", sid);
+ fprintf(add_fd, "sambaGroupType: 2\n");
+ fprintf(add_fd, "displayName: Domain Guests\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Domain Computers entity */
+ fprintf(add_fd, "# Domain Computers, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Domain Computers,ou=%s,%s\n",
+ group_attr, suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "gidNumber: 515\n");
+ fprintf(add_fd, "cn: Domain Computers\n");
+ fprintf(add_fd, "description: Netbios Domain Computers accounts\n");
+ fprintf(add_fd, "sambaSID: %s-515\n", sid);
+ fprintf(add_fd, "sambaGroupType: 2\n");
+ fprintf(add_fd, "displayName: Domain Computers\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Admininistrators Groups entity */
+ fprintf(add_fd, "# Administrators, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Administrators,ou=%s,%s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "gidNumber: 544\n");
+ fprintf(add_fd, "cn: Administrators\n");
+ fprintf(add_fd, "description: Netbios Domain Members can fully administer the computer/sambaDomainName\n");
+ fprintf(add_fd, "sambaSID: %s-544\n", builtin_sid);
+ fprintf(add_fd, "sambaGroupType: 5\n");
+ fprintf(add_fd, "displayName: Administrators\n");
+ fprintf(add_fd, "\n");
+
+ /* Write the Print Operator entity */
+ fprintf(add_fd, "# Print Operators, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Print Operators,ou=%s,%s\n",
+ group_attr, suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "gidNumber: 550\n");
+ fprintf(add_fd, "cn: Print Operators\n");
+ fprintf(add_fd, "description: Netbios Domain Print Operators\n");
+ fprintf(add_fd, "sambaSID: %s-550\n", builtin_sid);
+ fprintf(add_fd, "sambaGroupType: 5\n");
+ fprintf(add_fd, "displayName: Print Operators\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Backup Operators entity */
+ fprintf(add_fd, "# Backup Operators, %s, %s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "dn: cn=Backup Operators,ou=%s,%s\n",
+ group_attr, suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "gidNumber: 551\n");
+ fprintf(add_fd, "cn: Backup Operators\n");
+ fprintf(add_fd, "description: Netbios Domain Members can bypass file security to back up files\n");
+ fprintf(add_fd, "sambaSID: %s-551\n", builtin_sid);
+ fprintf(add_fd, "sambaGroupType: 5\n");
+ fprintf(add_fd, "displayName: Backup Operators\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Write the Replicators entity */
+ fprintf(add_fd, "# Replicators, %s, %s\n", group_attr, suffix);
+ fprintf(add_fd, "dn: cn=Replicators,ou=%s,%s\n", group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "gidNumber: 552\n");
+ fprintf(add_fd, "cn: Replicators\n");
+ fprintf(add_fd, "description: Netbios Domain Supports file replication in a sambaDomainName\n");
+ fprintf(add_fd, "sambaSID: %s-552\n", builtin_sid);
+ fprintf(add_fd, "sambaGroupType: 5\n");
+ fprintf(add_fd, "displayName: Replicators\n");
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Deallocate memory, and return */
+ SAFE_FREE(suffix_attr);
+ SAFE_FREE(user_attr);
+ SAFE_FREE(group_attr);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS map_populate_groups(TALLOC_CTX *mem_ctx,
+ GROUPMAP *groupmap,
+ ACCOUNTMAP *accountmap,
+ const char *sid,
+ const char *suffix,
+ const char *builtin_sid)
+{
+ char *group_attr = sstring_sub(lp_ldap_group_suffix(), '=', ',');
+
+ /* Map the groups created by populate_ldap_for_ldif */
+ groupmap[0].rid = 512;
+ groupmap[0].gidNumber = 512;
+ groupmap[0].sambaSID = talloc_asprintf(mem_ctx, "%s-512", sid);
+ groupmap[0].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Domain Admins,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[0].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[0].group_dn);
+
+ accountmap[0].rid = 512;
+ accountmap[0].cn = talloc_strdup(mem_ctx, "Domain Admins");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[0].cn);
+
+ groupmap[1].rid = 513;
+ groupmap[1].gidNumber = 513;
+ groupmap[1].sambaSID = talloc_asprintf(mem_ctx, "%s-513", sid);
+ groupmap[1].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Domain Users,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[1].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[1].group_dn);
+
+ accountmap[1].rid = 513;
+ accountmap[1].cn = talloc_strdup(mem_ctx, "Domain Users");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[1].cn);
+
+ groupmap[2].rid = 514;
+ groupmap[2].gidNumber = 514;
+ groupmap[2].sambaSID = talloc_asprintf(mem_ctx, "%s-514", sid);
+ groupmap[2].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Domain Guests,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[2].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[2].group_dn);
+
+ accountmap[2].rid = 514;
+ accountmap[2].cn = talloc_strdup(mem_ctx, "Domain Guests");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[2].cn);
+
+ groupmap[3].rid = 515;
+ groupmap[3].gidNumber = 515;
+ groupmap[3].sambaSID = talloc_asprintf(mem_ctx, "%s-515", sid);
+ groupmap[3].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Domain Computers,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[3].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[3].group_dn);
+
+ accountmap[3].rid = 515;
+ accountmap[3].cn = talloc_strdup(mem_ctx, "Domain Computers");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[3].cn);
+
+ groupmap[4].rid = 544;
+ groupmap[4].gidNumber = 544;
+ groupmap[4].sambaSID = talloc_asprintf(mem_ctx, "%s-544", builtin_sid);
+ groupmap[4].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Administrators,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[4].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[4].group_dn);
+
+ accountmap[4].rid = 515;
+ accountmap[4].cn = talloc_strdup(mem_ctx, "Administrators");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[4].cn);
+
+ groupmap[5].rid = 550;
+ groupmap[5].gidNumber = 550;
+ groupmap[5].sambaSID = talloc_asprintf(mem_ctx, "%s-550", builtin_sid);
+ groupmap[5].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Print Operators,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[5].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[5].group_dn);
+
+ accountmap[5].rid = 550;
+ accountmap[5].cn = talloc_strdup(mem_ctx, "Print Operators");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[5].cn);
+
+ groupmap[6].rid = 551;
+ groupmap[6].gidNumber = 551;
+ groupmap[6].sambaSID = talloc_asprintf(mem_ctx, "%s-551", builtin_sid);
+ groupmap[6].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Backup Operators,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[6].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[6].group_dn);
+
+ accountmap[6].rid = 551;
+ accountmap[6].cn = talloc_strdup(mem_ctx, "Backup Operators");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[6].cn);
+
+ groupmap[7].rid = 552;
+ groupmap[7].gidNumber = 552;
+ groupmap[7].sambaSID = talloc_asprintf(mem_ctx, "%s-552", builtin_sid);
+ groupmap[7].group_dn = talloc_asprintf(mem_ctx,
+ "cn=Replicators,ou=%s,%s", group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[7].sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap[7].group_dn);
+
+ accountmap[7].rid = 551;
+ accountmap[7].cn = talloc_strdup(mem_ctx, "Replicators");
+ NT_STATUS_HAVE_NO_MEMORY(accountmap[7].cn);
+
+ SAFE_FREE(group_attr);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * This is a crap routine, but I think it's the quickest way to solve the
+ * UTF8->base64 problem.
+ */
+
+static int fprintf_attr(FILE *add_fd, const char *attr_name,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *value, *p, *base64;
+ DATA_BLOB base64_blob;
+ bool do_base64 = false;
+ int res;
+
+ va_start(ap, fmt);
+ value = talloc_vasprintf(NULL, fmt, ap);
+ va_end(ap);
+
+ SMB_ASSERT(value != NULL);
+
+ for (p=value; *p; p++) {
+ if (*p & 0x80) {
+ do_base64 = true;
+ break;
+ }
+ }
+
+ if (!do_base64) {
+ bool only_whitespace = true;
+ for (p=value; *p; p++) {
+ /*
+ * I know that this not multibyte safe, but we break
+ * on the first non-whitespace character anyway.
+ */
+ if (!isspace(*p)) {
+ only_whitespace = false;
+ break;
+ }
+ }
+ if (only_whitespace) {
+ do_base64 = true;
+ }
+ }
+
+ if (!do_base64) {
+ res = fprintf(add_fd, "%s: %s\n", attr_name, value);
+ TALLOC_FREE(value);
+ return res;
+ }
+
+ base64_blob.data = (unsigned char *)value;
+ base64_blob.length = strlen(value);
+
+ base64 = base64_encode_data_blob(value, base64_blob);
+ SMB_ASSERT(base64 != NULL);
+
+ res = fprintf(add_fd, "%s:: %s\n", attr_name, base64);
+ TALLOC_FREE(value);
+ return res;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_group_info_to_ldif(TALLOC_CTX *mem_ctx,
+ struct netr_DELTA_GROUP *r,
+ GROUPMAP *groupmap,
+ FILE *add_fd,
+ const char *sid,
+ const char *suffix)
+{
+ const char *groupname = r->group_name.string;
+ uint32 grouptype = 0, g_rid = 0;
+ char *group_attr = sstring_sub(lp_ldap_group_suffix(), '=', ',');
+
+ /* Set up the group type (always 2 for group info) */
+ grouptype = 2;
+
+ /* These groups are entered by populate_ldap_for_ldif */
+ if (strcmp(groupname, "Domain Admins") == 0 ||
+ strcmp(groupname, "Domain Users") == 0 ||
+ strcmp(groupname, "Domain Guests") == 0 ||
+ strcmp(groupname, "Domain Computers") == 0 ||
+ strcmp(groupname, "Administrators") == 0 ||
+ strcmp(groupname, "Print Operators") == 0 ||
+ strcmp(groupname, "Backup Operators") == 0 ||
+ strcmp(groupname, "Replicators") == 0) {
+ SAFE_FREE(group_attr);
+ return NT_STATUS_OK;
+ } else {
+ /* Increment the gid for the new group */
+ ldif_gid++;
+ }
+
+ /* Map the group rid, gid, and dn */
+ g_rid = r->rid;
+ groupmap->rid = g_rid;
+ groupmap->gidNumber = ldif_gid;
+ groupmap->sambaSID = talloc_asprintf(mem_ctx, "%s-%d", sid, g_rid);
+ groupmap->group_dn = talloc_asprintf(mem_ctx,
+ "cn=%s,ou=%s,%s", groupname, group_attr, suffix);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap->sambaSID);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap->group_dn);
+
+ /* Write the data to the temporary add ldif file */
+ fprintf(add_fd, "# %s, %s, %s\n", groupname, group_attr,
+ suffix);
+ fprintf_attr(add_fd, "dn", "cn=%s,ou=%s,%s", groupname, group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf_attr(add_fd, "cn", "%s", groupname);
+ fprintf(add_fd, "gidNumber: %d\n", ldif_gid);
+ fprintf(add_fd, "sambaSID: %s\n", groupmap->sambaSID);
+ fprintf(add_fd, "sambaGroupType: %d\n", grouptype);
+ fprintf_attr(add_fd, "displayName", "%s", groupname);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ SAFE_FREE(group_attr);
+ /* Return */
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_account_info_to_ldif(TALLOC_CTX *mem_ctx,
+ struct netr_DELTA_USER *r,
+ GROUPMAP *groupmap,
+ ACCOUNTMAP *accountmap,
+ FILE *add_fd,
+ const char *sid,
+ const char *suffix,
+ int alloced)
+{
+ fstring username, logonscript, homedrive, homepath = "", homedir = "";
+ fstring hex_nt_passwd, hex_lm_passwd;
+ fstring description, profilepath, fullname, sambaSID;
+ uchar lm_passwd[16], nt_passwd[16];
+ char *flags, *user_rdn;
+ const char *ou;
+ const char* nopasswd = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
+ static uchar zero_buf[16];
+ uint32 rid = 0, group_rid = 0, gidNumber = 0;
+ time_t unix_time;
+ int i;
+
+ /* Get the username */
+ fstrcpy(username, r->account_name.string);
+
+ /* Get the rid */
+ rid = r->rid;
+
+ /* Map the rid and username for group member info later */
+ accountmap->rid = rid;
+ accountmap->cn = talloc_strdup(mem_ctx, username);
+ NT_STATUS_HAVE_NO_MEMORY(accountmap->cn);
+
+ /* Get the home directory */
+ if (r->acct_flags & ACB_NORMAL) {
+ fstrcpy(homedir, r->home_directory.string);
+ if (!*homedir) {
+ snprintf(homedir, sizeof(homedir), "/home/%s", username);
+ } else {
+ snprintf(homedir, sizeof(homedir), "/nobodyshomedir");
+ }
+ ou = lp_ldap_user_suffix();
+ } else {
+ ou = lp_ldap_machine_suffix();
+ snprintf(homedir, sizeof(homedir), "/machinehomedir");
+ }
+
+ /* Get the logon script */
+ fstrcpy(logonscript, r->logon_script.string);
+
+ /* Get the home drive */
+ fstrcpy(homedrive, r->home_drive.string);
+
+ /* Get the home path */
+ fstrcpy(homepath, r->home_directory.string);
+
+ /* Get the description */
+ fstrcpy(description, r->description.string);
+
+ /* Get the display name */
+ fstrcpy(fullname, r->full_name.string);
+
+ /* Get the profile path */
+ fstrcpy(profilepath, r->profile_path.string);
+
+ /* Get lm and nt password data */
+ if (memcmp(r->lmpassword.hash, zero_buf, 16) != 0) {
+ sam_pwd_hash(r->rid, r->lmpassword.hash, lm_passwd, 0);
+ pdb_sethexpwd(hex_lm_passwd, lm_passwd, r->acct_flags);
+ } else {
+ pdb_sethexpwd(hex_lm_passwd, NULL, 0);
+ }
+ if (memcmp(r->ntpassword.hash, zero_buf, 16) != 0) {
+ sam_pwd_hash(r->rid, r->ntpassword.hash, nt_passwd, 0);
+ pdb_sethexpwd(hex_nt_passwd, nt_passwd, r->acct_flags);
+ } else {
+ pdb_sethexpwd(hex_nt_passwd, NULL, 0);
+ }
+ unix_time = nt_time_to_unix(r->last_password_change);
+
+ /* Increment the uid for the new user */
+ ldif_uid++;
+
+ /* Set up group id and sambaSID for the user */
+ group_rid = r->primary_gid;
+ for (i=0; i<alloced; i++) {
+ if (groupmap[i].rid == group_rid) break;
+ }
+ if (i == alloced){
+ DEBUG(1, ("Could not find rid %d in groupmap array\n",
+ group_rid));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ gidNumber = groupmap[i].gidNumber;
+ snprintf(sambaSID, sizeof(sambaSID), groupmap[i].sambaSID);
+
+ /* Set up sambaAcctFlags */
+ flags = pdb_encode_acct_ctrl(r->acct_flags,
+ NEW_PW_FORMAT_SPACE_PADDED_LEN);
+
+ /* Add the user to the temporary add ldif file */
+ /* this isn't quite right...we can't assume there's just OU=. jmcd */
+ user_rdn = sstring_sub(ou, '=', ',');
+ fprintf(add_fd, "# %s, %s, %s\n", username, user_rdn, suffix);
+ fprintf_attr(add_fd, "dn", "uid=%s,ou=%s,%s", username, user_rdn,
+ suffix);
+ SAFE_FREE(user_rdn);
+ fprintf(add_fd, "ObjectClass: top\n");
+ fprintf(add_fd, "objectClass: inetOrgPerson\n");
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXACCOUNT);
+ fprintf(add_fd, "objectClass: shadowAccount\n");
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_SAMBASAMACCOUNT);
+ fprintf_attr(add_fd, "cn", "%s", username);
+ fprintf_attr(add_fd, "sn", "%s", username);
+ fprintf_attr(add_fd, "uid", "%s", username);
+ fprintf(add_fd, "uidNumber: %d\n", ldif_uid);
+ fprintf(add_fd, "gidNumber: %d\n", gidNumber);
+ fprintf_attr(add_fd, "homeDirectory", "%s", homedir);
+ if (*homepath)
+ fprintf_attr(add_fd, "sambaHomePath", "%s", homepath);
+ if (*homedrive)
+ fprintf_attr(add_fd, "sambaHomeDrive", "%s", homedrive);
+ if (*logonscript)
+ fprintf_attr(add_fd, "sambaLogonScript", "%s", logonscript);
+ fprintf(add_fd, "loginShell: %s\n",
+ ((r->acct_flags & ACB_NORMAL) ?
+ "/bin/bash" : "/bin/false"));
+ fprintf(add_fd, "gecos: System User\n");
+ if (*description)
+ fprintf_attr(add_fd, "description", "%s", description);
+ fprintf(add_fd, "sambaSID: %s-%d\n", sid, rid);
+ fprintf(add_fd, "sambaPrimaryGroupSID: %s\n", sambaSID);
+ if(*fullname)
+ fprintf_attr(add_fd, "displayName", "%s", fullname);
+ if(*profilepath)
+ fprintf_attr(add_fd, "sambaProfilePath", "%s", profilepath);
+ if (strcmp(nopasswd, hex_lm_passwd) != 0)
+ fprintf(add_fd, "sambaLMPassword: %s\n", hex_lm_passwd);
+ if (strcmp(nopasswd, hex_nt_passwd) != 0)
+ fprintf(add_fd, "sambaNTPassword: %s\n", hex_nt_passwd);
+ fprintf(add_fd, "sambaPwdLastSet: %d\n", (int)unix_time);
+ fprintf(add_fd, "sambaAcctFlags: %s\n", flags);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ /* Return */
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_alias_info_to_ldif(TALLOC_CTX *mem_ctx,
+ struct netr_DELTA_ALIAS *r,
+ GROUPMAP *groupmap,
+ FILE *add_fd,
+ const char *sid,
+ const char *suffix,
+ enum netr_SamDatabaseID database_id)
+{
+ fstring aliasname, description;
+ uint32 grouptype = 0, g_rid = 0;
+ char *group_attr = sstring_sub(lp_ldap_group_suffix(), '=', ',');
+
+ /* Get the alias name */
+ fstrcpy(aliasname, r->alias_name.string);
+
+ /* Get the alias description */
+ fstrcpy(description, r->description.string);
+
+ /* Set up the group type */
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ grouptype = 4;
+ break;
+ case SAM_DATABASE_BUILTIN:
+ grouptype = 5;
+ break;
+ default:
+ grouptype = 4;
+ break;
+ }
+
+ /*
+ These groups are entered by populate_ldap_for_ldif
+ Note that populate creates a group called Relicators,
+ but NT returns a group called Replicator
+ */
+ if (strcmp(aliasname, "Domain Admins") == 0 ||
+ strcmp(aliasname, "Domain Users") == 0 ||
+ strcmp(aliasname, "Domain Guests") == 0 ||
+ strcmp(aliasname, "Domain Computers") == 0 ||
+ strcmp(aliasname, "Administrators") == 0 ||
+ strcmp(aliasname, "Print Operators") == 0 ||
+ strcmp(aliasname, "Backup Operators") == 0 ||
+ strcmp(aliasname, "Replicator") == 0) {
+ SAFE_FREE(group_attr);
+ return NT_STATUS_OK;
+ } else {
+ /* Increment the gid for the new group */
+ ldif_gid++;
+ }
+
+ /* Map the group rid and gid */
+ g_rid = r->rid;
+ groupmap->gidNumber = ldif_gid;
+ groupmap->sambaSID = talloc_asprintf(mem_ctx, "%s-%d", sid, g_rid);
+ NT_STATUS_HAVE_NO_MEMORY(groupmap->sambaSID);
+
+ /* Write the data to the temporary add ldif file */
+ fprintf(add_fd, "# %s, %s, %s\n", aliasname, group_attr,
+ suffix);
+ fprintf_attr(add_fd, "dn", "cn=%s,ou=%s,%s", aliasname, group_attr,
+ suffix);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_POSIXGROUP);
+ fprintf(add_fd, "objectClass: %s\n", LDAP_OBJ_GROUPMAP);
+ fprintf(add_fd, "cn: %s\n", aliasname);
+ fprintf(add_fd, "gidNumber: %d\n", ldif_gid);
+ fprintf(add_fd, "sambaSID: %s\n", groupmap->sambaSID);
+ fprintf(add_fd, "sambaGroupType: %d\n", grouptype);
+ fprintf_attr(add_fd, "displayName", "%s", aliasname);
+ if (description[0])
+ fprintf_attr(add_fd, "description", "%s", description);
+ fprintf(add_fd, "\n");
+ fflush(add_fd);
+
+ SAFE_FREE(group_attr);
+ /* Return */
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_groupmem_info_to_ldif(struct netr_DELTA_GROUP_MEMBER *r,
+ uint32_t id_rid,
+ GROUPMAP *groupmap,
+ ACCOUNTMAP *accountmap,
+ FILE *mod_fd, int alloced)
+{
+ fstring group_dn;
+ uint32 group_rid = 0, rid = 0;
+ int i, j, k;
+
+ /* Get the dn for the group */
+ if (r->num_rids > 0) {
+ group_rid = id_rid;
+ for (j=0; j<alloced; j++) {
+ if (groupmap[j].rid == group_rid) break;
+ }
+ if (j == alloced){
+ DEBUG(1, ("Could not find rid %d in groupmap array\n",
+ group_rid));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ snprintf(group_dn, sizeof(group_dn), "%s", groupmap[j].group_dn);
+ fprintf(mod_fd, "dn: %s\n", group_dn);
+
+ /* Get the cn for each member */
+ for (i=0; i < r->num_rids; i++) {
+ rid = r->rids[i];
+ for (k=0; k<alloced; k++) {
+ if (accountmap[k].rid == rid) break;
+ }
+ if (k == alloced){
+ DEBUG(1, ("Could not find rid %d in "
+ "accountmap array\n", rid));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ fprintf(mod_fd, "memberUid: %s\n", accountmap[k].cn);
+ }
+ fprintf(mod_fd, "\n");
+ }
+ fflush(mod_fd);
+
+ /* Return */
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS ldif_init_context(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ const char *ldif_filename,
+ const char *domain_sid_str,
+ struct samsync_ldif_context **ctx)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct samsync_ldif_context *r;
+ const char *add_template = "/tmp/add.ldif.XXXXXX";
+ const char *mod_template = "/tmp/mod.ldif.XXXXXX";
+ const char *builtin_sid = "S-1-5-32";
+
+ /* Get other smb.conf data */
+ if (!(lp_workgroup()) || !*(lp_workgroup())) {
+ DEBUG(0,("workgroup missing from smb.conf--exiting\n"));
+ exit(1);
+ }
+
+ /* Get the ldap suffix */
+ if (!(lp_ldap_suffix()) || !*(lp_ldap_suffix())) {
+ DEBUG(0,("ldap suffix missing from smb.conf--exiting\n"));
+ exit(1);
+ }
+
+ if (*ctx && (*ctx)->initialized) {
+ return NT_STATUS_OK;
+ }
+
+ r = TALLOC_ZERO_P(mem_ctx, struct samsync_ldif_context);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ /* Get the ldap suffix */
+ r->suffix = lp_ldap_suffix();
+
+ /* Ensure we have an output file */
+ if (ldif_filename) {
+ r->ldif_file = fopen(ldif_filename, "a");
+ } else {
+ r->ldif_file = stdout;
+ }
+
+ if (!r->ldif_file) {
+ fprintf(stderr, "Could not open %s\n", ldif_filename);
+ DEBUG(1, ("Could not open %s\n", ldif_filename));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ r->add_template = talloc_strdup(mem_ctx, add_template);
+ r->mod_template = talloc_strdup(mem_ctx, mod_template);
+ if (!r->add_template || !r->mod_template) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ r->add_name = talloc_strdup(mem_ctx, add_template);
+ r->mod_name = talloc_strdup(mem_ctx, mod_template);
+ if (!r->add_name || !r->mod_name) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Open the add and mod ldif files */
+ if (!(r->add_file = fdopen(smb_mkstemp(r->add_name),"w"))) {
+ DEBUG(1, ("Could not open %s\n", r->add_name));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ if (!(r->mod_file = fdopen(smb_mkstemp(r->mod_name),"w"))) {
+ DEBUG(1, ("Could not open %s\n", r->mod_name));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Allocate initial memory for groupmap and accountmap arrays */
+ r->groupmap = TALLOC_ZERO_ARRAY(mem_ctx, GROUPMAP, 8);
+ r->accountmap = TALLOC_ZERO_ARRAY(mem_ctx, ACCOUNTMAP, 8);
+ if (r->groupmap == NULL || r->accountmap == NULL) {
+ DEBUG(1,("GROUPMAP talloc failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Remember how many we malloced */
+ r->num_alloced = 8;
+
+ /* Initial database population */
+ if (database_id == SAM_DATABASE_DOMAIN) {
+
+ status = populate_ldap_for_ldif(domain_sid_str,
+ r->suffix,
+ builtin_sid,
+ r->add_file);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = map_populate_groups(mem_ctx,
+ r->groupmap,
+ r->accountmap,
+ domain_sid_str,
+ r->suffix,
+ builtin_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+ r->initialized = true;
+
+ *ctx = r;
+
+ return NT_STATUS_OK;
+ done:
+ TALLOC_FREE(r);
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static void ldif_free_context(struct samsync_ldif_context *r)
+{
+ if (!r) {
+ return;
+ }
+
+ /* Close and delete the ldif files */
+ if (r->add_file) {
+ fclose(r->add_file);
+ }
+
+ if ((r->add_name != NULL) &&
+ strcmp(r->add_name, r->add_template) && (unlink(r->add_name))) {
+ DEBUG(1,("unlink(%s) failed, error was (%s)\n",
+ r->add_name, strerror(errno)));
+ }
+
+ if (r->mod_file) {
+ fclose(r->mod_file);
+ }
+
+ if ((r->mod_name != NULL) &&
+ strcmp(r->mod_name, r->mod_template) && (unlink(r->mod_name))) {
+ DEBUG(1,("unlink(%s) failed, error was (%s)\n",
+ r->mod_name, strerror(errno)));
+ }
+
+ if (r->ldif_file && (r->ldif_file != stdout)) {
+ fclose(r->ldif_file);
+ }
+
+ TALLOC_FREE(r);
+}
+
+/****************************************************************
+****************************************************************/
+
+static void ldif_write_output(enum netr_SamDatabaseID database_id,
+ struct samsync_ldif_context *l)
+{
+ /* Write ldif data to the user's file */
+ if (database_id == SAM_DATABASE_DOMAIN) {
+ fprintf(l->ldif_file,
+ "# SAM_DATABASE_DOMAIN: ADD ENTITIES\n");
+ fprintf(l->ldif_file,
+ "# =================================\n\n");
+ fflush(l->ldif_file);
+ } else if (database_id == SAM_DATABASE_BUILTIN) {
+ fprintf(l->ldif_file,
+ "# SAM_DATABASE_BUILTIN: ADD ENTITIES\n");
+ fprintf(l->ldif_file,
+ "# ==================================\n\n");
+ fflush(l->ldif_file);
+ }
+ fseek(l->add_file, 0, SEEK_SET);
+ transfer_file(fileno(l->add_file), fileno(l->ldif_file), (size_t) -1);
+
+ if (database_id == SAM_DATABASE_DOMAIN) {
+ fprintf(l->ldif_file,
+ "# SAM_DATABASE_DOMAIN: MODIFY ENTITIES\n");
+ fprintf(l->ldif_file,
+ "# ====================================\n\n");
+ fflush(l->ldif_file);
+ } else if (database_id == SAM_DATABASE_BUILTIN) {
+ fprintf(l->ldif_file,
+ "# SAM_DATABASE_BUILTIN: MODIFY ENTITIES\n");
+ fprintf(l->ldif_file,
+ "# =====================================\n\n");
+ fflush(l->ldif_file);
+ }
+ fseek(l->mod_file, 0, SEEK_SET);
+ transfer_file(fileno(l->mod_file), fileno(l->ldif_file), (size_t) -1);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fetch_sam_entry_ldif(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM *r,
+ struct samsync_context *ctx,
+ uint32_t *a_index,
+ uint32_t *g_index)
+{
+ union netr_DELTA_UNION u = r->delta_union;
+ union netr_DELTA_ID_UNION id = r->delta_id_union;
+ struct samsync_ldif_context *l =
+ talloc_get_type_abort(ctx->private_data, struct samsync_ldif_context);
+
+ switch (r->delta_type) {
+ case NETR_DELTA_DOMAIN:
+ break;
+
+ case NETR_DELTA_GROUP:
+ fetch_group_info_to_ldif(mem_ctx,
+ u.group,
+ &l->groupmap[*g_index],
+ l->add_file,
+ ctx->domain_sid_str,
+ l->suffix);
+ (*g_index)++;
+ break;
+
+ case NETR_DELTA_USER:
+ fetch_account_info_to_ldif(mem_ctx,
+ u.user,
+ l->groupmap,
+ &l->accountmap[*a_index],
+ l->add_file,
+ ctx->domain_sid_str,
+ l->suffix,
+ l->num_alloced);
+ (*a_index)++;
+ break;
+
+ case NETR_DELTA_ALIAS:
+ fetch_alias_info_to_ldif(mem_ctx,
+ u.alias,
+ &l->groupmap[*g_index],
+ l->add_file,
+ ctx->domain_sid_str,
+ l->suffix,
+ database_id);
+ (*g_index)++;
+ break;
+
+ case NETR_DELTA_GROUP_MEMBER:
+ fetch_groupmem_info_to_ldif(u.group_member,
+ id.rid,
+ l->groupmap,
+ l->accountmap,
+ l->mod_file,
+ l->num_alloced);
+ break;
+
+ case NETR_DELTA_ALIAS_MEMBER:
+ case NETR_DELTA_POLICY:
+ case NETR_DELTA_ACCOUNT:
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ case NETR_DELTA_SECRET:
+ case NETR_DELTA_RENAME_GROUP:
+ case NETR_DELTA_RENAME_USER:
+ case NETR_DELTA_RENAME_ALIAS:
+ case NETR_DELTA_DELETE_GROUP:
+ case NETR_DELTA_DELETE_USER:
+ case NETR_DELTA_MODIFY_COUNT:
+ default:
+ break;
+ } /* end of switch */
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS ldif_realloc_maps(TALLOC_CTX *mem_ctx,
+ struct samsync_ldif_context *l,
+ uint32_t num_entries)
+{
+ /* Re-allocate memory for groupmap and accountmap arrays */
+ l->groupmap = TALLOC_REALLOC_ARRAY(mem_ctx,
+ l->groupmap,
+ GROUPMAP,
+ num_entries + l->num_alloced);
+
+ l->accountmap = TALLOC_REALLOC_ARRAY(mem_ctx,
+ l->accountmap,
+ ACCOUNTMAP,
+ num_entries + l->num_alloced);
+
+ if (l->groupmap == NULL || l->accountmap == NULL) {
+ DEBUG(1,("GROUPMAP talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Initialize the new records */
+ memset(&(l->groupmap[l->num_alloced]), 0,
+ sizeof(GROUPMAP) * num_entries);
+ memset(&(l->accountmap[l->num_alloced]), 0,
+ sizeof(ACCOUNTMAP) * num_entries);
+
+ /* Remember how many we alloced this time */
+ l->num_alloced += num_entries;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS fetch_sam_entries_ldif(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ NTSTATUS status;
+ int i;
+ uint32_t g_index = 0, a_index = 0;
+ struct samsync_ldif_context *ldif_ctx =
+ (struct samsync_ldif_context *)ctx->private_data;
+
+ status = ldif_init_context(mem_ctx,
+ database_id,
+ ctx->output_filename,
+ ctx->domain_sid_str,
+ &ldif_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ ctx->private_data = ldif_ctx;
+
+ status = ldif_realloc_maps(mem_ctx, ldif_ctx, r->num_deltas);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ for (i = 0; i < r->num_deltas; i++) {
+ status = fetch_sam_entry_ldif(mem_ctx, database_id,
+ &r->delta_enum[i], ctx,
+ &a_index, &g_index);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ }
+
+ /* This was the last query */
+ if (last_query) {
+ ldif_write_output(database_id, ldif_ctx);
+ if (ldif_ctx->ldif_file != stdout) {
+ ctx->result_message = talloc_asprintf(mem_ctx,
+ "Vampired %d accounts and %d groups to %s",
+ a_index, g_index, ctx->output_filename);
+ }
+ ldif_free_context(ldif_ctx);
+ ctx->private_data = NULL;
+ }
+
+ return NT_STATUS_OK;
+
+ failed:
+ ldif_free_context(ldif_ctx);
+ ctx->private_data = NULL;
+
+ return status;
+}
+
+#else /* HAVE_LDAP */
+
+NTSTATUS fetch_sam_entries_ldif(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+#endif
diff --git a/source3/libnet/libnet_samsync_passdb.c b/source3/libnet/libnet_samsync_passdb.c
new file mode 100644
index 0000000000..7d07bcb791
--- /dev/null
+++ b/source3/libnet/libnet_samsync_passdb.c
@@ -0,0 +1,789 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
+ Modified by Volker Lendecke 2002
+ Copyright (C) Jeremy Allison 2005.
+ Copyright (C) Guenther Deschner 2008.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+
+/* Convert a struct samu_DELTA to a struct samu. */
+#define STRING_CHANGED (old_string && !new_string) ||\
+ (!old_string && new_string) ||\
+ (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+#define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\
+ (!(s1) && (s2)) ||\
+ ((s1) && (s2) && (strcmp((s1), (s2)) != 0))
+
+static NTSTATUS sam_account_from_delta(struct samu *account,
+ struct netr_DELTA_USER *r)
+{
+ const char *old_string, *new_string;
+ time_t unix_time, stored_time;
+ uchar lm_passwd[16], nt_passwd[16];
+ static uchar zero_buf[16];
+
+ /* Username, fullname, home dir, dir drive, logon script, acct
+ desc, workstations, profile. */
+
+ if (r->account_name.string) {
+ old_string = pdb_get_nt_username(account);
+ new_string = r->account_name.string;
+
+ if (STRING_CHANGED) {
+ pdb_set_nt_username(account, new_string, PDB_CHANGED);
+ }
+
+ /* Unix username is the same - for sanity */
+ old_string = pdb_get_username( account );
+ if (STRING_CHANGED) {
+ pdb_set_username(account, new_string, PDB_CHANGED);
+ }
+ }
+
+ if (r->full_name.string) {
+ old_string = pdb_get_fullname(account);
+ new_string = r->full_name.string;
+
+ if (STRING_CHANGED)
+ pdb_set_fullname(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->home_directory.string) {
+ old_string = pdb_get_homedir(account);
+ new_string = r->home_directory.string;
+
+ if (STRING_CHANGED)
+ pdb_set_homedir(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->home_drive.string) {
+ old_string = pdb_get_dir_drive(account);
+ new_string = r->home_drive.string;
+
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->logon_script.string) {
+ old_string = pdb_get_logon_script(account);
+ new_string = r->logon_script.string;
+
+ if (STRING_CHANGED)
+ pdb_set_logon_script(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->description.string) {
+ old_string = pdb_get_acct_desc(account);
+ new_string = r->description.string;
+
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->workstations.string) {
+ old_string = pdb_get_workstations(account);
+ new_string = r->workstations.string;
+
+ if (STRING_CHANGED)
+ pdb_set_workstations(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->profile_path.string) {
+ old_string = pdb_get_profile_path(account);
+ new_string = r->profile_path.string;
+
+ if (STRING_CHANGED)
+ pdb_set_profile_path(account, new_string, PDB_CHANGED);
+ }
+
+ if (r->parameters.string) {
+ DATA_BLOB mung;
+ char *newstr;
+ old_string = pdb_get_munged_dial(account);
+ mung.length = r->parameters.length;
+ mung.data = (uint8 *) r->parameters.string;
+ newstr = (mung.length == 0) ? NULL :
+ base64_encode_data_blob(talloc_tos(), mung);
+
+ if (STRING_CHANGED_NC(old_string, newstr))
+ pdb_set_munged_dial(account, newstr, PDB_CHANGED);
+ TALLOC_FREE(newstr);
+ }
+
+ /* User and group sid */
+ if (pdb_get_user_rid(account) != r->rid)
+ pdb_set_user_sid_from_rid(account, r->rid, PDB_CHANGED);
+ if (pdb_get_group_rid(account) != r->primary_gid)
+ pdb_set_group_sid_from_rid(account, r->primary_gid, PDB_CHANGED);
+
+ /* Logon and password information */
+ if (!nt_time_is_zero(&r->last_logon)) {
+ unix_time = nt_time_to_unix(r->last_logon);
+ stored_time = pdb_get_logon_time(account);
+ if (stored_time != unix_time)
+ pdb_set_logon_time(account, unix_time, PDB_CHANGED);
+ }
+
+ if (!nt_time_is_zero(&r->last_logoff)) {
+ unix_time = nt_time_to_unix(r->last_logoff);
+ stored_time = pdb_get_logoff_time(account);
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(account, unix_time,PDB_CHANGED);
+ }
+
+ /* Logon Divs */
+ if (pdb_get_logon_divs(account) != r->logon_hours.units_per_week)
+ pdb_set_logon_divs(account, r->logon_hours.units_per_week, PDB_CHANGED);
+
+#if 0
+ /* no idea what to do with this one - gd */
+ /* Max Logon Hours */
+ if (delta->unknown1 != pdb_get_unknown_6(account)) {
+ pdb_set_unknown_6(account, delta->unknown1, PDB_CHANGED);
+ }
+#endif
+ /* Logon Hours Len */
+ if (r->logon_hours.units_per_week/8 != pdb_get_hours_len(account)) {
+ pdb_set_hours_len(account, r->logon_hours.units_per_week/8, PDB_CHANGED);
+ }
+
+ /* Logon Hours */
+ if (r->logon_hours.bits) {
+ char oldstr[44], newstr[44];
+ pdb_sethexhours(oldstr, pdb_get_hours(account));
+ pdb_sethexhours(newstr, r->logon_hours.bits);
+ if (!strequal(oldstr, newstr))
+ pdb_set_hours(account, r->logon_hours.bits, PDB_CHANGED);
+ }
+
+ if (pdb_get_bad_password_count(account) != r->bad_password_count)
+ pdb_set_bad_password_count(account, r->bad_password_count, PDB_CHANGED);
+
+ if (pdb_get_logon_count(account) != r->logon_count)
+ pdb_set_logon_count(account, r->logon_count, PDB_CHANGED);
+
+ if (!nt_time_is_zero(&r->last_password_change)) {
+ unix_time = nt_time_to_unix(r->last_password_change);
+ stored_time = pdb_get_pass_last_set_time(account);
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED);
+ } else {
+ /* no last set time, make it now */
+ pdb_set_pass_last_set_time(account, time(NULL), PDB_CHANGED);
+ }
+
+ if (!nt_time_is_zero(&r->acct_expiry)) {
+ unix_time = nt_time_to_unix(r->acct_expiry);
+ stored_time = pdb_get_kickoff_time(account);
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(account, unix_time, PDB_CHANGED);
+ }
+
+ /* Decode hashes from password hash
+ Note that win2000 may send us all zeros for the hashes if it doesn't
+ think this channel is secure enough - don't set the passwords at all
+ in that case
+ */
+ if (memcmp(r->ntpassword.hash, zero_buf, 16) != 0) {
+ sam_pwd_hash(r->rid, r->ntpassword.hash, lm_passwd, 0);
+ pdb_set_lanman_passwd(account, lm_passwd, PDB_CHANGED);
+ }
+
+ if (memcmp(r->lmpassword.hash, zero_buf, 16) != 0) {
+ sam_pwd_hash(r->rid, r->lmpassword.hash, nt_passwd, 0);
+ pdb_set_nt_passwd(account, nt_passwd, PDB_CHANGED);
+ }
+
+ /* TODO: account expiry time */
+
+ pdb_set_acct_ctrl(account, r->acct_flags, PDB_CHANGED);
+
+ pdb_set_domain(account, lp_workgroup(), PDB_CHANGED);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_account_info(uint32_t rid,
+ struct netr_DELTA_USER *r)
+{
+
+ NTSTATUS nt_ret = NT_STATUS_UNSUCCESSFUL;
+ fstring account;
+ char *add_script = NULL;
+ struct samu *sam_account=NULL;
+ GROUP_MAP map;
+ struct group *grp;
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+ struct passwd *passwd;
+ fstring sid_string;
+
+ fstrcpy(account, r->account_name.string);
+ d_printf("Creating account: %s\n", account);
+
+ if ( !(sam_account = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(passwd = Get_Pwnam_alloc(sam_account, account))) {
+ /* Create appropriate user */
+ if (r->acct_flags & ACB_NORMAL) {
+ add_script = talloc_strdup(sam_account,
+ lp_adduser_script());
+ } else if ( (r->acct_flags & ACB_WSTRUST) ||
+ (r->acct_flags & ACB_SVRTRUST) ||
+ (r->acct_flags & ACB_DOMTRUST) ) {
+ add_script = talloc_strdup(sam_account,
+ lp_addmachine_script());
+ } else {
+ DEBUG(1, ("Unknown user type: %s\n",
+ pdb_encode_acct_ctrl(r->acct_flags, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
+ nt_ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ if (!add_script) {
+ nt_ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ if (*add_script) {
+ int add_ret;
+ add_script = talloc_all_string_sub(sam_account,
+ add_script,
+ "%u",
+ account);
+ if (!add_script) {
+ nt_ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ add_ret = smbrun(add_script,NULL);
+ DEBUG(add_ret ? 0 : 1,("fetch_account: Running the command `%s' "
+ "gave %d\n", add_script, add_ret));
+ if (add_ret == 0) {
+ smb_nscd_flush_user_cache();
+ }
+ }
+
+ /* try and find the possible unix account again */
+ if ( !(passwd = Get_Pwnam_alloc(sam_account, account)) ) {
+ d_fprintf(stderr, "Could not create posix account info for '%s'\n", account);
+ nt_ret = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, r->rid);
+
+ DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n",
+ sid_to_fstring(sid_string, &user_sid), account));
+ if (!pdb_getsampwsid(sam_account, &user_sid)) {
+ sam_account_from_delta(sam_account, r);
+ DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n",
+ sid_to_fstring(sid_string, &user_sid),
+ pdb_get_username(sam_account)));
+ if (!NT_STATUS_IS_OK(pdb_add_sam_account(sam_account))) {
+ DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n",
+ account));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ sam_account_from_delta(sam_account, r);
+ DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n",
+ sid_to_fstring(sid_string, &user_sid),
+ pdb_get_username(sam_account)));
+ if (!NT_STATUS_IS_OK(pdb_update_sam_account(sam_account))) {
+ DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n",
+ account));
+ TALLOC_FREE(sam_account);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (pdb_get_group_sid(sam_account) == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ group_sid = *pdb_get_group_sid(sam_account);
+
+ if (!pdb_getgrsid(&map, group_sid)) {
+ DEBUG(0, ("Primary group of %s has no mapping!\n",
+ pdb_get_username(sam_account)));
+ } else {
+ if (map.gid != passwd->pw_gid) {
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n",
+ (unsigned long)map.gid, pdb_get_username(sam_account), sid_string_tos(&group_sid)));
+ } else {
+ smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account));
+ }
+ }
+ }
+
+ if ( !passwd ) {
+ DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n",
+ pdb_get_username(sam_account)));
+ }
+
+ done:
+ TALLOC_FREE(sam_account);
+ return nt_ret;
+}
+
+static NTSTATUS fetch_group_info(uint32_t rid,
+ struct netr_DELTA_GROUP *r)
+{
+ fstring name;
+ fstring comment;
+ struct group *grp = NULL;
+ DOM_SID group_sid;
+ fstring sid_string;
+ GROUP_MAP map;
+ bool insert = true;
+
+ fstrcpy(name, r->group_name.string);
+ fstrcpy(comment, r->description.string);
+
+ /* add the group to the mapping table */
+ sid_copy(&group_sid, get_global_sam_sid());
+ sid_append_rid(&group_sid, rid);
+ sid_to_fstring(sid_string, &group_sid);
+
+ if (pdb_getgrsid(&map, group_sid)) {
+ if ( map.gid != -1 )
+ grp = getgrgid(map.gid);
+ insert = false;
+ }
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ /* No group found from mapping, find it from its name. */
+ if ((grp = getgrnam(name)) == NULL) {
+
+ /* No appropriate group found, create one */
+
+ d_printf("Creating unix group: '%s'\n", name);
+
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((grp = getgrnam(name)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ map.gid = grp->gr_gid;
+ map.sid = group_sid;
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.nt_name, name);
+ if (r->description.string) {
+ fstrcpy(map.comment, comment);
+ } else {
+ fstrcpy(map.comment, "");
+ }
+
+ if (insert)
+ pdb_add_group_mapping_entry(&map);
+ else
+ pdb_update_group_mapping_entry(&map);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_group_mem_info(uint32_t rid,
+ struct netr_DELTA_GROUP_MEMBER *r)
+{
+ int i;
+ TALLOC_CTX *t = NULL;
+ char **nt_members = NULL;
+ char **unix_members;
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ struct group *grp;
+
+ if (r->num_rids == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sid_copy(&group_sid, get_global_sam_sid());
+ sid_append_rid(&group_sid, rid);
+
+ if (!get_domain_group_from_sid(group_sid, &map)) {
+ DEBUG(0, ("Could not find global group %d\n", rid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %lu\n", (unsigned long)map.gid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ d_printf("Group members of %s: ", grp->gr_name);
+
+ if (!(t = talloc_init("fetch_group_mem_info"))) {
+ DEBUG(0, ("could not talloc_init\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r->num_rids) {
+ if ((nt_members = TALLOC_ZERO_ARRAY(t, char *, r->num_rids)) == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ talloc_free(t);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ nt_members = NULL;
+ }
+
+ for (i=0; i < r->num_rids; i++) {
+ struct samu *member = NULL;
+ DOM_SID member_sid;
+
+ if ( !(member = samu_new(t)) ) {
+ talloc_destroy(t);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sid_copy(&member_sid, get_global_sam_sid());
+ sid_append_rid(&member_sid, r->rids[i]);
+
+ if (!pdb_getsampwsid(member, &member_sid)) {
+ DEBUG(1, ("Found bogus group member: %d (member_sid=%s group=%s)\n",
+ r->rids[i], sid_string_tos(&member_sid), grp->gr_name));
+ TALLOC_FREE(member);
+ continue;
+ }
+
+ if (pdb_get_group_rid(member) == rid) {
+ d_printf("%s(primary),", pdb_get_username(member));
+ TALLOC_FREE(member);
+ continue;
+ }
+
+ d_printf("%s,", pdb_get_username(member));
+ nt_members[i] = talloc_strdup(t, pdb_get_username(member));
+ TALLOC_FREE(member);
+ }
+
+ d_printf("\n");
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ bool is_nt_member = false;
+ for (i=0; i < r->num_rids; i++) {
+ if (nt_members[i] == NULL) {
+ /* This was a primary group */
+ continue;
+ }
+
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_nt_member = true;
+ break;
+ }
+ }
+ if (!is_nt_member) {
+ /* We look at a unix group member that is not
+ an nt group member. So, remove it. NT is
+ boss here. */
+ smb_delete_user_group(grp->gr_name, *unix_members);
+ }
+ unix_members += 1;
+ }
+
+ for (i=0; i < r->num_rids; i++) {
+ bool is_unix_member = false;
+
+ if (nt_members[i] == NULL) {
+ /* This was the primary group */
+ continue;
+ }
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_unix_member = true;
+ break;
+ }
+ unix_members += 1;
+ }
+
+ if (!is_unix_member) {
+ /* We look at a nt group member that is not a
+ unix group member currently. So, add the nt
+ group member. */
+ smb_add_user_group(grp->gr_name, nt_members[i]);
+ }
+ }
+
+ talloc_destroy(t);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_alias_info(uint32_t rid,
+ struct netr_DELTA_ALIAS *r,
+ const DOM_SID *dom_sid)
+{
+ fstring name;
+ fstring comment;
+ struct group *grp = NULL;
+ DOM_SID alias_sid;
+ fstring sid_string;
+ GROUP_MAP map;
+ bool insert = true;
+
+ fstrcpy(name, r->alias_name.string);
+ fstrcpy(comment, r->description.string);
+
+ /* Find out whether the group is already mapped */
+ sid_copy(&alias_sid, dom_sid);
+ sid_append_rid(&alias_sid, rid);
+ sid_to_fstring(sid_string, &alias_sid);
+
+ if (pdb_getgrsid(&map, alias_sid)) {
+ grp = getgrgid(map.gid);
+ insert = false;
+ }
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ /* No group found from mapping, find it from its name. */
+ if ((grp = getgrnam(name)) == NULL) {
+ /* No appropriate group found, create one */
+ d_printf("Creating unix group: '%s'\n", name);
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+ if ((grp = getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ map.gid = grp->gr_gid;
+ map.sid = alias_sid;
+
+ if (sid_equal(dom_sid, &global_sid_Builtin))
+ map.sid_name_use = SID_NAME_WKN_GRP;
+ else
+ map.sid_name_use = SID_NAME_ALIAS;
+
+ fstrcpy(map.nt_name, name);
+ fstrcpy(map.comment, comment);
+
+ if (insert)
+ pdb_add_group_mapping_entry(&map);
+ else
+ pdb_update_group_mapping_entry(&map);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_alias_mem(uint32_t rid,
+ struct netr_DELTA_ALIAS_MEMBER *r,
+ const DOM_SID *dom_sid)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_domain_info(uint32_t rid,
+ struct netr_DELTA_DOMAIN *r)
+{
+ time_t u_max_age, u_min_age, u_logout;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ const char *domname;
+ struct netr_AcctLockStr *lockstr = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_tos();
+
+ status = pull_netr_AcctLockStr(mem_ctx, &r->account_lockout,
+ &lockstr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("failed to pull account lockout string: %s\n",
+ nt_errstr(status));
+ }
+
+ u_max_age = uint64s_nt_time_to_unix_abs((uint64 *)&r->max_password_age);
+ u_min_age = uint64s_nt_time_to_unix_abs((uint64 *)&r->min_password_age);
+ u_logout = uint64s_nt_time_to_unix_abs((uint64 *)&r->force_logoff_time);
+
+ domname = r->domain_name.string;
+ if (!domname) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* we don't handle BUILTIN account policies */
+ if (!strequal(domname, get_global_sam_name())) {
+ printf("skipping SAM_DOMAIN_INFO delta for '%s' (is not my domain)\n", domname);
+ return NT_STATUS_OK;
+ }
+
+
+ if (!pdb_set_account_policy(AP_PASSWORD_HISTORY,
+ r->password_history_length))
+ return nt_status;
+
+ if (!pdb_set_account_policy(AP_MIN_PASSWORD_LEN,
+ r->min_password_length))
+ return nt_status;
+
+ if (!pdb_set_account_policy(AP_MAX_PASSWORD_AGE, (uint32)u_max_age))
+ return nt_status;
+
+ if (!pdb_set_account_policy(AP_MIN_PASSWORD_AGE, (uint32)u_min_age))
+ return nt_status;
+
+ if (!pdb_set_account_policy(AP_TIME_TO_LOGOUT, (uint32)u_logout))
+ return nt_status;
+
+ if (lockstr) {
+ time_t u_lockoutreset, u_lockouttime;
+
+ u_lockoutreset = uint64s_nt_time_to_unix_abs(&lockstr->reset_count);
+ u_lockouttime = uint64s_nt_time_to_unix_abs((uint64_t *)&lockstr->lockout_duration);
+
+ if (!pdb_set_account_policy(AP_BAD_ATTEMPT_LOCKOUT,
+ lockstr->bad_attempt_lockout))
+ return nt_status;
+
+ if (!pdb_set_account_policy(AP_RESET_COUNT_TIME, (uint32_t)u_lockoutreset/60))
+ return nt_status;
+
+ if (u_lockouttime != -1)
+ u_lockouttime /= 60;
+
+ if (!pdb_set_account_policy(AP_LOCK_ACCOUNT_DURATION, (uint32_t)u_lockouttime))
+ return nt_status;
+ }
+
+ if (!pdb_set_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS,
+ r->logon_to_chgpass))
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_sam_entry(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM *r,
+ struct samsync_context *ctx)
+{
+ switch(r->delta_type) {
+ case NETR_DELTA_USER:
+ fetch_account_info(r->delta_id_union.rid,
+ r->delta_union.user);
+ break;
+ case NETR_DELTA_GROUP:
+ fetch_group_info(r->delta_id_union.rid,
+ r->delta_union.group);
+ break;
+ case NETR_DELTA_GROUP_MEMBER:
+ fetch_group_mem_info(r->delta_id_union.rid,
+ r->delta_union.group_member);
+ break;
+ case NETR_DELTA_ALIAS:
+ fetch_alias_info(r->delta_id_union.rid,
+ r->delta_union.alias,
+ ctx->domain_sid);
+ break;
+ case NETR_DELTA_ALIAS_MEMBER:
+ fetch_alias_mem(r->delta_id_union.rid,
+ r->delta_union.alias_member,
+ ctx->domain_sid);
+ break;
+ case NETR_DELTA_DOMAIN:
+ fetch_domain_info(r->delta_id_union.rid,
+ r->delta_union.domain);
+ break;
+ /* The following types are recognised but not handled */
+ case NETR_DELTA_RENAME_GROUP:
+ d_printf("NETR_DELTA_RENAME_GROUP not handled\n");
+ break;
+ case NETR_DELTA_RENAME_USER:
+ d_printf("NETR_DELTA_RENAME_USER not handled\n");
+ break;
+ case NETR_DELTA_RENAME_ALIAS:
+ d_printf("NETR_DELTA_RENAME_ALIAS not handled\n");
+ break;
+ case NETR_DELTA_POLICY:
+ d_printf("NETR_DELTA_POLICY not handled\n");
+ break;
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ d_printf("NETR_DELTA_TRUSTED_DOMAIN not handled\n");
+ break;
+ case NETR_DELTA_ACCOUNT:
+ d_printf("NETR_DELTA_ACCOUNT not handled\n");
+ break;
+ case NETR_DELTA_SECRET:
+ d_printf("NETR_DELTA_SECRET not handled\n");
+ break;
+ case NETR_DELTA_DELETE_GROUP:
+ d_printf("NETR_DELTA_DELETE_GROUP not handled\n");
+ break;
+ case NETR_DELTA_DELETE_USER:
+ d_printf("NETR_DELTA_DELETE_USER not handled\n");
+ break;
+ case NETR_DELTA_MODIFY_COUNT:
+ d_printf("NETR_DELTA_MODIFY_COUNT not handled\n");
+ break;
+ case NETR_DELTA_DELETE_ALIAS:
+ d_printf("NETR_DELTA_DELETE_ALIAS not handled\n");
+ break;
+ case NETR_DELTA_DELETE_TRUST:
+ d_printf("NETR_DELTA_DELETE_TRUST not handled\n");
+ break;
+ case NETR_DELTA_DELETE_ACCOUNT:
+ d_printf("NETR_DELTA_DELETE_ACCOUNT not handled\n");
+ break;
+ case NETR_DELTA_DELETE_SECRET:
+ d_printf("NETR_DELTA_DELETE_SECRET not handled\n");
+ break;
+ case NETR_DELTA_DELETE_GROUP2:
+ d_printf("NETR_DELTA_DELETE_GROUP2 not handled\n");
+ break;
+ case NETR_DELTA_DELETE_USER2:
+ d_printf("NETR_DELTA_DELETE_USER2 not handled\n");
+ break;
+ default:
+ d_printf("Unknown delta record type %d\n", r->delta_type);
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS fetch_sam_entries(TALLOC_CTX *mem_ctx,
+ enum netr_SamDatabaseID database_id,
+ struct netr_DELTA_ENUM_ARRAY *r,
+ bool last_query,
+ struct samsync_context *ctx)
+{
+ int i;
+
+ for (i = 0; i < r->num_deltas; i++) {
+ fetch_sam_entry(mem_ctx, database_id, &r->delta_enum[i], ctx);
+ }
+
+ return NT_STATUS_OK;
+}