summaryrefslogtreecommitdiffstats
path: root/source/sam
diff options
context:
space:
mode:
Diffstat (limited to 'source/sam')
-rw-r--r--source/sam/account.c305
-rw-r--r--source/sam/group.c193
-rw-r--r--source/sam/gums.c173
-rw-r--r--source/sam/gums_api.c1426
-rw-r--r--source/sam/gums_helper.c383
-rw-r--r--source/sam/gums_tdbsam2.c1220
-rw-r--r--source/sam/idmap.c317
-rw-r--r--source/sam/idmap_ldap.c790
-rw-r--r--source/sam/idmap_tdb.c676
-rw-r--r--source/sam/idmap_util.c295
-rw-r--r--source/sam/interface.c1338
11 files changed, 7116 insertions, 0 deletions
diff --git a/source/sam/account.c b/source/sam/account.c
new file mode 100644
index 00000000000..b8336146cda
--- /dev/null
+++ b/source/sam/account.c
@@ -0,0 +1,305 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_ACCOUNT_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_account(SAM_ACCOUNT_HANDLE *account)
+{
+ ZERO_STRUCT(account->private); /* Don't touch the talloc context */
+
+ /* Don't change these timestamp settings without a good reason.
+ They are important for NT member server compatibility. */
+
+ /* FIXME: We should actually call get_nt_time_max() or sthng
+ * here */
+ unix_to_nt_time(&(account->private.logoff_time),get_time_t_max());
+ unix_to_nt_time(&(account->private.kickoff_time),get_time_t_max());
+ unix_to_nt_time(&(account->private.pass_must_change_time),get_time_t_max());
+ account->private.unknown_1 = 0x00ffffff; /* don't know */
+ account->private.logon_divs = 168; /* hours per week */
+ account->private.hours_len = 21; /* 21 times 8 bits = 168 */
+ memset(account->private.hours, 0xff, account->private.hours_len); /* available at all hours */
+ account->private.unknown_2 = 0x00000000; /* don't know */
+ account->private.unknown_3 = 0x000004ec; /* don't know */
+}
+
+static void destroy_sam_talloc(SAM_ACCOUNT_HANDLE **account)
+{
+ if (*account) {
+ data_blob_clear_free(&((*account)->private.lm_pw));
+ data_blob_clear_free(&((*account)->private.nt_pw));
+ if((*account)->private.plaintext_pw!=NULL)
+ memset((*account)->private.plaintext_pw,'\0',strlen((*account)->private.plaintext_pw));
+
+ talloc_destroy((*account)->mem_ctx);
+ *account = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_ACCOUNT_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_account_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT_HANDLE **account)
+{
+ SMB_ASSERT(*account != NULL);
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_account_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *account=(SAM_ACCOUNT_HANDLE *)talloc(mem_ctx, sizeof(SAM_ACCOUNT_HANDLE));
+
+ if (*account==NULL) {
+ DEBUG(0,("sam_init_account_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*account)->mem_ctx = mem_ctx;
+
+ (*account)->free_fn = NULL;
+
+ sam_fill_default_account(*account);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS sam_init_account(SAM_ACCOUNT_HANDLE **account)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("sam internal SAM_ACCOUNT_HANDLE allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_account: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_init_account_talloc(mem_ctx, account))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*account)->free_fn = destroy_sam_talloc;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Free the contents of the SAM_ACCOUNT_HANDLE, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from
+ * memory.
+ *
+ * @param account SAM_ACCOUNT_HANDLE to free members of.
+ **/
+
+static void sam_free_account_contents(SAM_ACCOUNT_HANDLE *account)
+{
+
+ /* Kill off sensitive data. Free()ed by the
+ talloc mechinism */
+
+ data_blob_clear_free(&(account->private.lm_pw));
+ data_blob_clear_free(&(account->private.nt_pw));
+ if (account->private.plaintext_pw)
+ memset(account->private.plaintext_pw,'\0',strlen(account->private.plaintext_pw));
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT_HANDLE and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS sam_reset_sam(SAM_ACCOUNT_HANDLE *account)
+{
+ SMB_ASSERT(account != NULL);
+
+ sam_free_account_contents(account);
+
+ sam_fill_default_account(account);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_account(SAM_ACCOUNT_HANDLE **account)
+{
+ SMB_ASSERT(*account != NULL);
+
+ sam_free_account_contents(*account);
+
+ if ((*account)->free_fn) {
+ (*account)->free_fn(account);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+ static fstring acct_str;
+ size_t i = 0;
+
+ acct_str[i++] = '[';
+
+ if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+ if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+ if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+ if (acct_ctrl & ACB_TEMPDUP ) acct_str[i++] = 'T';
+ if (acct_ctrl & ACB_NORMAL ) acct_str[i++] = 'U';
+ if (acct_ctrl & ACB_MNS ) acct_str[i++] = 'M';
+ if (acct_ctrl & ACB_WSTRUST ) acct_str[i++] = 'W';
+ if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+ if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+ if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X';
+ if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+ for ( ; i < length - 2 ; i++ )
+ acct_str[i] = ' ';
+
+ i = length - 2;
+ acct_str[i++] = ']';
+ acct_str[i++] = '\0';
+
+ return acct_str;
+}
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_acct_ctrl(const char *p)
+{
+ uint16 acct_ctrl = 0;
+ BOOL finished = False;
+
+ /*
+ * Check if the account type bits have been encoded after the
+ * NT password (in the form [NDHTUWSLXI]).
+ */
+
+ if (*p != '[')
+ return 0;
+
+ for (p++; *p && !finished; p++) {
+ switch (*p) {
+ case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+ case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+ case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+ case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
+ case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
+ case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
+ case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
+ case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
+ case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
+ case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
+ case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void sam_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+ if (pwd != NULL) {
+ int i;
+ for (i = 0; i < 16; i++)
+ slprintf(&p[i*2], 3, "%02X", pwd[i]);
+ } else {
+ if (acct_ctrl & ACB_PWNOTREQ)
+ safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+ else
+ safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+ }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL sam_gethexpwd(const char *p, unsigned char *pwd)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p)
+ return (False);
+
+ for (i = 0; i < 32; i += 2) {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr(hexchars, hinybble);
+ p2 = strchr(hexchars, lonybble);
+
+ if (!p1 || !p2)
+ return (False);
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return (True);
+}
diff --git a/source/sam/group.c b/source/sam/group.c
new file mode 100644
index 00000000000..101e3dd7ce1
--- /dev/null
+++ b/source/sam/group.c
@@ -0,0 +1,193 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_GROUP_HANDLE /SAM_GROUP_ENUM helpers
+
+ Copyright (C) Stefan (metze) Metzmacher 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_GROUP_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_group(SAM_GROUP_HANDLE *group)
+{
+ ZERO_STRUCT(group->private); /* Don't touch the talloc context */
+
+}
+
+static void destroy_sam_group_handle_talloc(SAM_GROUP_HANDLE **group)
+{
+ if (*group) {
+
+ talloc_destroy((*group)->mem_ctx);
+ *group = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_GROUP_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_group_talloc(TALLOC_CTX *mem_ctx, SAM_GROUP_HANDLE **group)
+{
+ SMB_ASSERT(*group != NULL);
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_group_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *group=(SAM_GROUP_HANDLE *)talloc(mem_ctx, sizeof(SAM_GROUP_HANDLE));
+
+ if (*group==NULL) {
+ DEBUG(0,("sam_init_group_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*group)->mem_ctx = mem_ctx;
+
+ (*group)->free_fn = NULL;
+
+ sam_fill_default_group(*group);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct SAM_GROUP_HANDLE.
+ ************************************************************/
+
+NTSTATUS sam_init_group(SAM_GROUP_HANDLE **group)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("sam internal SAM_GROUP_HANDLE allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_group: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_init_group_talloc(mem_ctx, group))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*group)->free_fn = destroy_sam_group_handle_talloc;
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Reset the SAM_GROUP_HANDLE.
+ ***********************************************************/
+
+NTSTATUS sam_reset_group(SAM_GROUP_HANDLE *group)
+{
+ SMB_ASSERT(group != NULL);
+
+ sam_fill_default_group(group);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_GROUP_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_group(SAM_ACCOUNT_HANDLE **group)
+{
+ SMB_ASSERT(*group != NULL);
+
+ if ((*group)->free_fn) {
+ (*group)->free_fn(group);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************
+ Encode the group control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 group_ctrl, size_t length)
+{
+ static fstring group_str;
+ size_t i = 0;
+
+ group_str[i++] = '[';
+
+ if (group_ctrl & GCB_LOCAL_GROUP ) group_str[i++] = 'L';
+ if (group_ctrl & GCB_GLOBAL_GROUP ) group_str[i++] = 'G';
+
+ for ( ; i < length - 2 ; i++ )
+ group_str[i] = ' ';
+
+ i = length - 2;
+ group_str[i++] = ']';
+ group_str[i++] = '\0';
+
+ return group_str;
+}
+
+/**********************************************************
+ Decode the group control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_group_ctrl(const char *p)
+{
+ uint16 group_ctrl = 0;
+ BOOL finished = False;
+
+ /*
+ * Check if the account type bits have been encoded after the
+ * NT password (in the form [NDHTUWSLXI]).
+ */
+
+ if (*p != '[')
+ return 0;
+
+ for (p++; *p && !finished; p++) {
+ switch (*p) {
+ case 'L': { group_ctrl |= GCB_LOCAL_GROUP; break; /* 'L'ocal Aliases Group. */ }
+ case 'G': { group_ctrl |= GCB_GLOBAL_GROUP; break; /* 'G'lobal Domain Group. */ }
+
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return group_ctrl;
+}
+
diff --git a/source/sam/gums.c b/source/sam/gums.c
new file mode 100644
index 00000000000..b7191535845
--- /dev/null
+++ b/source/sam/gums.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ Grops and Users Management System initializations.
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+#define GMV_MAJOR 0
+#define GMV_MINOR 1
+
+static GUMS_FUNCTIONS *gums_backend = NULL;
+
+static struct gums_init_function_entry *backends = NULL;
+
+static void lazy_initialize_gums(void)
+{
+ static BOOL initialized = False;
+
+ if (initialized)
+ return;
+
+ static_init_gums;
+ initialized = True;
+}
+
+static struct gums_init_function_entry *gums_find_backend_entry(const char *name);
+
+NTSTATUS gums_register_module(int version, const char *name, gums_init_function init_fn)
+{
+ struct gums_init_function_entry *entry = backends;
+
+ if (version != GUMS_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register gums backend!\n"
+ "You tried to register a gums module with"
+ "GUMS_INTERFACE_VERSION %d, while this version"
+ "of samba uses version %d\n", version,
+ GUMS_INTERFACE_VERSION));
+
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init_fn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register gums backend %s\n", name));
+
+ /* Check for duplicates */
+ if (gums_find_backend_entry(name)) {
+ DEBUG(0,("There already is a gums backend registered"
+ "with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct gums_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init_fn = init_fn;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added gums backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct gums_init_function_entry *gums_find_backend_entry(const char *name)
+{
+ struct gums_init_function_entry *entry = backends;
+
+ while (entry) {
+ if (strcmp(entry->name, name) == 0)
+ return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS gums_setup_backend(const char *backend)
+{
+
+ TALLOC_CTX *mem_ctx;
+ char *module_name = smb_xstrdup(backend);
+ char *p, *module_data = NULL;
+ struct gums_init_function_entry *entry;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ lazy_initialize_gums();
+
+ p = strchr(module_name, ':');
+ if (p) {
+ *p = 0;
+ module_data = p+1;
+ trim_string(module_data, " ", " ");
+ }
+
+ trim_string(module_name, " ", " ");
+
+ DEBUG(5,("Attempting to find a gums backend to match %s (%s)\n", backend, module_name));
+
+ entry = gums_find_backend_entry(module_name);
+
+ /* Try to find a module that contains this module */
+ if (!entry) {
+ DEBUG(2,("No builtin backend found, trying to load plugin\n"));
+ if(NT_STATUS_IS_OK(smb_probe_module("gums", module_name)) && !(entry = gums_find_backend_entry(module_name))) {
+ DEBUG(0,("Plugin is available, but doesn't register gums backend %s\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* No such backend found */
+ if(!entry) {
+ DEBUG(0,("No builtin nor plugin backend for %s found\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Found gums backend %s\n", module_name));
+
+ /* free current functions structure if any */
+ if (gums_backend) {
+ gums_backend->free_private_data(gums_backend->private_data);
+ talloc_destroy(gums_backend->mem_ctx);
+ gums_backend = NULL;
+ }
+
+ /* allocate a new GUMS_FUNCTIONS structure and memory context */
+ mem_ctx = talloc_init("gums_backend (%s)", module_name);
+ if (!mem_ctx)
+ return NT_STATUS_NO_MEMORY;
+ gums_backend = talloc(mem_ctx, sizeof(GUMS_FUNCTIONS));
+ if (!gums_backend)
+ return NT_STATUS_NO_MEMORY;
+ gums_backend->mem_ctx = mem_ctx;
+
+ /* init the requested backend module */
+ if (NT_STATUS_IS_OK(ret = entry->init_fn(gums_backend, module_data))) {
+ DEBUG(5,("gums backend %s has a valid init\n", backend));
+ } else {
+ DEBUG(0,("gums backend %s did not correctly init (error was %s)\n", backend, nt_errstr(ret)));
+ }
+ SAFE_FREE(module_name);
+ return ret;
+}
+
+NTSTATUS get_gums_fns(GUMS_FUNCTIONS **fns)
+{
+ if (gums_backend != NULL) {
+ *fns = gums_backend;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2, ("get_gums_fns: unable to get gums functions! backend uninitialized?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/sam/gums_api.c b/source/sam/gums_api.c
new file mode 100644
index 00000000000..5aafa7695f6
--- /dev/null
+++ b/source/sam/gums_api.c
@@ -0,0 +1,1426 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS structures
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Functions to get/set info from a GUMS object */
+
+NTSTATUS gums_create_object(GUMS_OBJECT **obj, uint32 type)
+{
+ TALLOC_CTX *mem_ctx;
+ GUMS_OBJECT *go;
+ NTSTATUS ret;
+
+ mem_ctx = talloc_init("gums_create_object");
+ if (!mem_ctx) {
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ *obj = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ go = talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+ if (!go) {
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ talloc_destroy(mem_ctx);
+ *obj = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ go->mem_ctx = mem_ctx;
+ go->type = type;
+ go->version = GUMS_OBJECT_VERSION;
+
+ switch(type) {
+ case GUMS_OBJ_DOMAIN:
+ go->domain = (GUMS_DOMAIN *)talloc_zero(mem_ctx, sizeof(GUMS_DOMAIN));
+ if (!(go->domain)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+
+ break;
+
+/*
+ case GUMS_OBJ_WORKSTATION_TRUST:
+ case GUMS_OBJ_SERVER_TRUST:
+ case GUMS_OBJ_DOMAIN_TRUST:
+*/
+ case GUMS_OBJ_NORMAL_USER:
+ go->user = (GUMS_USER *)talloc_zero(mem_ctx, sizeof(GUMS_USER));
+ if (!(go->user)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+ gums_set_user_acct_ctrl(go, ACB_NORMAL);
+ gums_set_user_hours(go, 0, NULL);
+
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ go->group = (GUMS_GROUP *)talloc_zero(mem_ctx, sizeof(GUMS_GROUP));
+ if (!(go->group)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+
+ break;
+
+ default:
+ /* TODO: throw error */
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto error;
+ }
+
+ *obj = go;
+ return NT_STATUS_OK;
+
+error:
+ talloc_destroy(go->mem_ctx);
+ *obj = NULL;
+ return ret;
+}
+
+NTSTATUS gums_create_privilege(GUMS_PRIVILEGE **priv)
+{
+ TALLOC_CTX *mem_ctx;
+ GUMS_PRIVILEGE *pri;
+
+ mem_ctx = talloc_init("gums_create_privilege");
+ if (!mem_ctx) {
+ DEBUG(0, ("gums_create_privilege: Out of memory!\n"));
+ *priv = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pri = talloc_zero(mem_ctx, sizeof(GUMS_PRIVILEGE));
+ if (!pri) {
+ DEBUG(0, ("gums_create_privilege: Out of memory!\n"));
+ talloc_destroy(mem_ctx);
+ *priv = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pri->mem_ctx = mem_ctx;
+ pri->version = GUMS_PRIVILEGE_VERSION;
+
+ *priv = pri;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_destroy_object(GUMS_OBJECT **obj)
+{
+ if (!obj || !(*obj))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if ((*obj)->mem_ctx)
+ talloc_destroy((*obj)->mem_ctx);
+ *obj = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_destroy_privilege(GUMS_PRIVILEGE **priv)
+{
+ if (!priv || !(*priv))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if ((*priv)->mem_ctx)
+ talloc_destroy((*priv)->mem_ctx);
+ *priv = NULL;
+
+ return NT_STATUS_OK;
+}
+
+void gums_reset_object(GUMS_OBJECT *go)
+{
+ go->seq_num = 0;
+ go->sid = NULL;
+ go->name = NULL;
+ go->description = NULL;
+
+ switch(go->type) {
+ case GUMS_OBJ_DOMAIN:
+ memset(go->domain, 0, sizeof(GUMS_DOMAIN));
+ break;
+
+/*
+ case GUMS_OBJ_WORKSTATION_TRUST:
+ case GUMS_OBJ_SERVER_TRUST:
+ case GUMS_OBJ_DOMAIN_TRUST:
+*/
+ case GUMS_OBJ_NORMAL_USER:
+ memset(go->user, 0, sizeof(GUMS_USER));
+ gums_set_user_acct_ctrl(go, ACB_NORMAL);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ memset(go->group, 0, sizeof(GUMS_GROUP));
+ break;
+
+ default:
+ return;
+ }
+}
+
+uint32 gums_get_object_type(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->type;
+}
+
+uint32 gums_get_object_seq_num(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->seq_num;
+}
+
+uint32 gums_get_object_version(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->version;
+}
+
+const SEC_DESC *gums_get_sec_desc(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->sec_desc;
+}
+
+const DOM_SID *gums_get_object_sid(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->sid;
+}
+
+const char *gums_get_object_name(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->name;
+}
+
+const char *gums_get_object_description(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->description;
+}
+
+NTSTATUS gums_set_object_seq_num(GUMS_OBJECT *obj, uint32 seq_num)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->seq_num = seq_num;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_version(GUMS_OBJECT *obj, uint32 version)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->version = version;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_sec_desc(GUMS_OBJECT *obj, const SEC_DESC *sec_desc)
+{
+ if (!obj || !sec_desc)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->sec_desc = dup_sec_desc(obj->mem_ctx, sec_desc);
+ if (!(obj->sec_desc)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_sid(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->sid = sid_dup_talloc(obj->mem_ctx, sid);
+ if (!(obj->sid)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_name(GUMS_OBJECT *obj, const char *name)
+{
+ if (!obj || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->name = (char *)talloc_strdup(obj->mem_ctx, name);
+ if (!(obj->name)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_description(GUMS_OBJECT *obj, const char *description)
+{
+ if (!obj || !description)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->description = (char *)talloc_strdup(obj->mem_ctx, description);
+ if (!(obj->description)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+/*
+NTSTATUS gums_get_object_privileges(PRIVILEGE_SET **priv_set, const GUMS_OBJECT *obj)
+{
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *priv_set = obj->priv_set;
+ return NT_STATUS_OK;
+}
+*/
+
+uint32 gums_get_domain_next_rid(const GUMS_OBJECT *obj)
+{
+ if (obj->type != GUMS_OBJ_DOMAIN)
+ return -1;
+
+ return obj->domain->next_rid;
+}
+
+NTSTATUS gums_set_domain_next_rid(GUMS_OBJECT *obj, uint32 rid)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_DOMAIN)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->domain->next_rid = rid;
+ return NT_STATUS_OK;
+}
+
+/* User specific functions */
+
+const DOM_SID *gums_get_user_pri_group(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->group_sid;
+}
+
+const DATA_BLOB gums_get_user_nt_pwd(const GUMS_OBJECT *obj)
+{
+ fstring p;
+
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return data_blob(NULL, 0);
+
+ pdb_sethexpwd(p, (unsigned char *)(obj->user->nt_pw.data), 0);
+ DEBUG(100, ("Reading NT Password=[%s]\n", p));
+
+ return obj->user->nt_pw;
+}
+
+const DATA_BLOB gums_get_user_lm_pwd(const GUMS_OBJECT *obj)
+{
+ fstring p;
+
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return data_blob(NULL, 0);
+
+ pdb_sethexpwd(p, (unsigned char *)(obj->user->lm_pw.data), 0);
+ DEBUG(100, ("Reading LM Password=[%s]\n", p));
+
+ return obj->user->lm_pw;
+}
+
+const char *gums_get_user_fullname(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->full_name;
+}
+
+const char *gums_get_user_homedir(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->home_dir;
+}
+
+const char *gums_get_user_dir_drive(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->dir_drive;
+}
+
+const char *gums_get_user_profile_path(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->profile_path;
+}
+
+const char *gums_get_user_logon_script(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->logon_script;
+}
+
+const char *gums_get_user_workstations(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->workstations;
+}
+
+const char *gums_get_user_unknown_str(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->unknown_str;
+}
+
+const char *gums_get_user_munged_dial(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->munged_dial;
+}
+
+NTTIME gums_get_user_logon_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->logon_time;
+}
+
+NTTIME gums_get_user_logoff_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->logoff_time;
+}
+
+NTTIME gums_get_user_kickoff_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->kickoff_time;
+}
+
+NTTIME gums_get_user_pass_last_set_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_last_set_time;
+}
+
+NTTIME gums_get_user_pass_can_change_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_can_change_time;
+}
+
+NTTIME gums_get_user_pass_must_change_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_must_change_time;
+}
+
+uint16 gums_get_user_acct_ctrl(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->acct_ctrl;
+}
+
+uint16 gums_get_user_logon_divs(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->logon_divs;
+}
+
+uint32 gums_get_user_hours_len(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->hours_len;
+}
+
+const uint8 *gums_get_user_hours(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->hours;
+}
+
+uint32 gums_get_user_unknown_3(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->unknown_3;
+}
+
+uint16 gums_get_user_bad_password_count(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->bad_password_count;
+}
+
+uint16 gums_get_user_logon_count(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->logon_count;
+}
+
+uint32 gums_get_user_unknown_6(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->unknown_6;
+}
+
+NTSTATUS gums_set_user_pri_group(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->group_sid = sid_dup_talloc(obj->mem_ctx, sid);
+ if (!(obj->user->group_sid)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_nt_pwd(GUMS_OBJECT *obj, const DATA_BLOB nt_pwd)
+{
+ fstring p;
+ unsigned char r[16];
+
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->nt_pw = data_blob_talloc(obj->mem_ctx, nt_pwd.data, nt_pwd.length);
+
+ memcpy(r, nt_pwd.data, 16);
+ pdb_sethexpwd(p, r, 0);
+ DEBUG(100, ("Setting NT Password=[%s]\n", p));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_lm_pwd(GUMS_OBJECT *obj, const DATA_BLOB lm_pwd)
+{
+ fstring p;
+ unsigned char r[16];
+
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->lm_pw = data_blob_talloc(obj->mem_ctx, lm_pwd.data, lm_pwd.length);
+
+ memcpy(r, lm_pwd.data, 16);
+ pdb_sethexpwd(p, r, 0);
+ DEBUG(100, ("Setting LM Password=[%s]\n", p));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_fullname(GUMS_OBJECT *obj, const char *fullname)
+{
+ if (!obj || !fullname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->full_name = (char *)talloc_strdup(obj->mem_ctx, fullname);
+ if (!(obj->user->full_name)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_homedir(GUMS_OBJECT *obj, const char *homedir)
+{
+ if (!obj || !homedir)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->home_dir = (char *)talloc_strdup(obj->mem_ctx, homedir);
+ if (!(obj->user->home_dir)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_dir_drive(GUMS_OBJECT *obj, const char *dir_drive)
+{
+ if (!obj || !dir_drive)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->dir_drive = (char *)talloc_strdup(obj->mem_ctx, dir_drive);
+ if (!(obj->user->dir_drive)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_script(GUMS_OBJECT *obj, const char *logon_script)
+{
+ if (!obj || !logon_script)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_script = (char *)talloc_strdup(obj->mem_ctx, logon_script);
+ if (!(obj->user->logon_script)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_profile_path(GUMS_OBJECT *obj, const char *profile_path)
+{
+ if (!obj || !profile_path)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->profile_path = (char *)talloc_strdup(obj->mem_ctx, profile_path);
+ if (!(obj->user->profile_path)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_workstations(GUMS_OBJECT *obj, const char *workstations)
+{
+ if (!obj || !workstations)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->workstations = (char *)talloc_strdup(obj->mem_ctx, workstations);
+ if (!(obj->user->workstations)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_str(GUMS_OBJECT *obj, const char *unknown_str)
+{
+ if (!obj || !unknown_str)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_str = (char *)talloc_strdup(obj->mem_ctx, unknown_str);
+ if (!(obj->user->unknown_str)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_munged_dial(GUMS_OBJECT *obj, const char *munged_dial)
+{
+ if (!obj || !munged_dial)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->munged_dial = (char *)talloc_strdup(obj->mem_ctx, munged_dial);
+ if (!(obj->user->munged_dial)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_time(GUMS_OBJECT *obj, NTTIME logon_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_time = logon_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logoff_time(GUMS_OBJECT *obj, NTTIME logoff_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logoff_time = logoff_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_kickoff_time(GUMS_OBJECT *obj, NTTIME kickoff_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->kickoff_time = kickoff_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_last_set_time(GUMS_OBJECT *obj, NTTIME pass_last_set_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_last_set_time = pass_last_set_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_can_change_time(GUMS_OBJECT *obj, NTTIME pass_can_change_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_can_change_time = pass_can_change_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_must_change_time(GUMS_OBJECT *obj, NTTIME pass_must_change_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_must_change_time = pass_must_change_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_acct_ctrl(GUMS_OBJECT *obj, uint16 acct_ctrl)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->acct_ctrl = acct_ctrl;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_divs(GUMS_OBJECT *obj, uint16 logon_divs)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_divs = logon_divs;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_hours(GUMS_OBJECT *obj, uint32 hours_len, const uint8 *hours)
+{
+ if (!obj || !hours)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->hours_len = hours_len;
+ if (hours_len == 0)
+ DEBUG(10, ("gums_set_user_hours: Warning, hours_len is zero!\n"));
+
+ obj->user->hours = (uint8 *)talloc(obj->mem_ctx, MAX_HOURS_LEN);
+ if (!(obj->user->hours))
+ return NT_STATUS_NO_MEMORY;
+ if (hours_len)
+ memcpy(obj->user->hours, hours, hours_len);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_3(GUMS_OBJECT *obj, uint32 unknown_3)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_3 = unknown_3;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_bad_password_count(GUMS_OBJECT *obj, uint16 bad_password_count)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->bad_password_count = bad_password_count;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_count(GUMS_OBJECT *obj, uint16 logon_count)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_count = logon_count;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_6(GUMS_OBJECT *obj, uint32 unknown_6)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_6 = unknown_6;
+ return NT_STATUS_OK;
+}
+
+/* Group specific functions */
+
+const DOM_SID *gums_get_group_members(int *count, const GUMS_OBJECT *obj)
+{
+ if (!count || !obj || !(obj->type == GUMS_OBJ_GROUP || obj->type == GUMS_OBJ_ALIAS)) {
+ *count = -1;
+ return NULL;
+ }
+
+ *count = obj->group->count;
+ return obj->group->members;
+}
+
+NTSTATUS gums_set_group_members(GUMS_OBJECT *obj, uint32 count, DOM_SID *members)
+{
+ uint32 n;
+
+ if (!obj || ((count > 0) && !members))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_GROUP &&
+ obj->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->group->count = count;
+
+ if (count) {
+ obj->group->members = (DOM_SID *)talloc(obj->mem_ctx, count * sizeof(DOM_SID));
+ if (!(obj->group->members)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ n = 0;
+ do {
+ sid_copy(&(obj->group->members[n]), &(members[n]));
+ n++;
+ } while (n < count);
+ } else {
+ obj->group->members = 0;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Privilege specific functions */
+
+const LUID_ATTR *gums_get_priv_luid_attr(const GUMS_PRIVILEGE *priv)
+{
+ if (!priv) {
+ return NULL;
+ }
+
+ return priv->privilege;
+}
+
+const DOM_SID *gums_get_priv_members(int *count, const GUMS_PRIVILEGE *priv)
+{
+ if (!count || !priv) {
+ *count = -1;
+ return NULL;
+ }
+
+ *count = priv->count;
+ return priv->members;
+}
+
+NTSTATUS gums_set_priv_luid_attr(GUMS_PRIVILEGE *priv, LUID_ATTR *luid_attr)
+{
+ if (!luid_attr || !priv)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ priv->privilege = (LUID_ATTR *)talloc_memdup(priv->mem_ctx, luid_attr, sizeof(LUID_ATTR));
+ if (!(priv->privilege)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_priv_members(GUMS_PRIVILEGE *priv, uint32 count, DOM_SID *members)
+{
+ uint32 n;
+
+ if (!priv || !members || !members)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ priv->count = count;
+ priv->members = (DOM_SID *)talloc(priv->mem_ctx, count * sizeof(DOM_SID));
+ if (!(priv->members))
+ return NT_STATUS_NO_MEMORY;
+
+ n = 0;
+ do {
+ sid_copy(&(priv->members[n]), &(members[n]));
+ n++;
+ } while (n < count);
+
+ return NT_STATUS_OK;
+}
+
+/* data_store set functions */
+
+NTSTATUS gums_create_commit_set(GUMS_COMMIT_SET **com_set, DOM_SID *sid, uint32 type)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("commit_set");
+ if (mem_ctx == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ *com_set = (GUMS_COMMIT_SET *)talloc_zero(mem_ctx, sizeof(GUMS_COMMIT_SET));
+ if (*com_set == NULL) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*com_set)->mem_ctx = mem_ctx;
+ (*com_set)->type = type;
+ sid_copy(&((*com_set)->sid), sid);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_grow_data_set(GUMS_COMMIT_SET *com_set, int size)
+{
+ GUMS_DATA_SET *data_set;
+
+ com_set->count = com_set->count + size;
+ if (com_set->count == size) { /* data set is empty*/
+ data_set = (GUMS_DATA_SET *)talloc_zero(com_set->mem_ctx, sizeof(GUMS_DATA_SET));
+ } else {
+ data_set = (GUMS_DATA_SET *)talloc_realloc(com_set->mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+ }
+ if (data_set == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ com_set->data = data_set;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_sec_desc(GUMS_COMMIT_SET *com_set, SEC_DESC *sec_desc)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ SEC_DESC *new_sec_desc;
+
+ if (!com_set || !sec_desc)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_SEC_DESC;
+ new_sec_desc = dup_sec_desc(com_set->mem_ctx, sec_desc);
+ if (new_sec_desc == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ (SEC_DESC *)(data_set->data) = new_sec_desc;
+
+ return NT_STATUS_OK;
+}
+
+/*
+NTSTATUS gums_cs_add_privilege(GUMS_PRIV_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ LUID_ATTR *new_priv;
+
+ if (!com_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_ADD_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(com_set->mem_ctx, &new_priv, priv)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_del_privilege(GUMS_PRIV_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ LUID_ATTR *new_priv;
+
+ if (!com_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_DEL_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(com_set->mem_ctx, &new_priv, priv)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_privilege_set(GUMS_PRIV_COMMIT_SET *com_set, PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ PRIVILEGE_SET *new_priv_set;
+
+ if (!com_set || !priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = init_priv_set_with_ctx(com_set->mem_ctx, &new_priv_set)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = dup_priv_set(new_priv_set, priv_set)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv_set;
+
+ return NT_STATUS_OK;
+}
+*/
+
+NTSTATUS gums_cs_set_string(GUMS_COMMIT_SET *com_set, uint32 type, char *str)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ char *new_str;
+
+ if (!com_set || !str || type < GUMS_SET_NAME || type > GUMS_SET_MUNGED_DIAL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = type;
+ new_str = talloc_strdup(com_set->mem_ctx, str);
+ if (new_str == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ (char *)(data_set->data) = new_str;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_name(GUMS_COMMIT_SET *com_set, char *name)
+{
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, name);
+}
+
+NTSTATUS gums_cs_set_description(GUMS_COMMIT_SET *com_set, char *desc)
+{
+ return gums_cs_set_string(com_set, GUMS_SET_DESCRIPTION, desc);
+}
+
+NTSTATUS gums_cs_set_full_name(GUMS_COMMIT_SET *com_set, char *full_name)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, full_name);
+}
+
+NTSTATUS gums_cs_set_home_directory(GUMS_COMMIT_SET *com_set, char *home_dir)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, home_dir);
+}
+
+NTSTATUS gums_cs_set_drive(GUMS_COMMIT_SET *com_set, char *drive)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, drive);
+}
+
+NTSTATUS gums_cs_set_logon_script(GUMS_COMMIT_SET *com_set, char *logon_script)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, logon_script);
+}
+
+NTSTATUS gums_cs_set_profile_path(GUMS_COMMIT_SET *com_set, char *prof_path)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, prof_path);
+}
+
+NTSTATUS gums_cs_set_workstations(GUMS_COMMIT_SET *com_set, char *wks)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, wks);
+}
+
+NTSTATUS gums_cs_set_unknown_string(GUMS_COMMIT_SET *com_set, char *unkn_str)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, unkn_str);
+}
+
+NTSTATUS gums_cs_set_munged_dial(GUMS_COMMIT_SET *com_set, char *munged_dial)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, munged_dial);
+}
+
+NTSTATUS gums_cs_set_nttime(GUMS_COMMIT_SET *com_set, uint32 type, NTTIME *nttime)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ NTTIME *new_time;
+
+ if (!com_set || !nttime || type < GUMS_SET_LOGON_TIME || type > GUMS_SET_PASS_MUST_CHANGE_TIME)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = type;
+ new_time = talloc(com_set->mem_ctx, sizeof(NTTIME));
+ if (new_time == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ new_time->low = nttime->low;
+ new_time->high = nttime->high;
+ (char *)(data_set->data) = new_time;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_logon_time(GUMS_COMMIT_SET *com_set, NTTIME *logon_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, logon_time);
+}
+
+NTSTATUS gums_cs_set_logoff_time(GUMS_COMMIT_SET *com_set, NTTIME *logoff_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGOFF_TIME, logoff_time);
+}
+
+NTSTATUS gums_cs_set_kickoff_time(GUMS_COMMIT_SET *com_set, NTTIME *kickoff_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_KICKOFF_TIME, kickoff_time);
+}
+
+NTSTATUS gums_cs_set_pass_last_set_time(GUMS_COMMIT_SET *com_set, NTTIME *pls_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pls_time);
+}
+
+NTSTATUS gums_cs_set_pass_can_change_time(GUMS_COMMIT_SET *com_set, NTTIME *pcc_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pcc_time);
+}
+
+NTSTATUS gums_cs_set_pass_must_change_time(GUMS_COMMIT_SET *com_set, NTTIME *pmc_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pmc_time);
+}
+
+NTSTATUS gums_cs_add_sids_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_ADD_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_add_users_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_add_sids_to_group(com_set, sids, count);
+}
+
+NTSTATUS gums_cs_add_groups_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_add_sids_to_group(com_set, sids, count);
+}
+
+NTSTATUS gums_cs_del_sids_from_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_DEL_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_ds_set_sids_in_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_commit_data(GUMS_COMMIT_SET *set)
+{
+ NTSTATUS ret;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns))) {
+ DEBUG(0, ("gums_commit_data: unable to get gums functions! backend uninitialized?\n"));
+ return ret;
+ }
+ return fns->set_object_values(&(set->sid), set->count, set->data);
+}
+
+NTSTATUS gums_destroy_commit_set(GUMS_COMMIT_SET **com_set)
+{
+ talloc_destroy((*com_set)->mem_ctx);
+ *com_set = NULL;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/sam/gums_helper.c b/source/sam/gums_helper.c
new file mode 100644
index 00000000000..fcb9366cda8
--- /dev/null
+++ b/source/sam/gums_helper.c
@@ -0,0 +1,383 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS backends helper functions
+ Copyright (C) Simo Sorce 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Builtin;
+extern DOM_SID global_sid_Builtin_Administrators;
+extern DOM_SID global_sid_Builtin_Power_Users;
+extern DOM_SID global_sid_Builtin_Account_Operators;
+extern DOM_SID global_sid_Builtin_Server_Operators;
+extern DOM_SID global_sid_Builtin_Print_Operators;
+extern DOM_SID global_sid_Builtin_Backup_Operators;
+extern DOM_SID global_sid_Builtin_Replicator;
+extern DOM_SID global_sid_Builtin_Users;
+extern DOM_SID global_sid_Builtin_Guests;
+
+
+/* defines */
+
+#define ALLOC_CHECK(str, ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define NTSTATUS_CHECK(err, label, str1, str2) do { if (NT_STATUS_IS_ERR(err)) { DEBUG(0, ("%s: %s\n", str1, str2)); } } while(0)
+
+/****************************************************************************
+ Check if a user is a mapped group.
+
+ This function will check if the group SID is mapped onto a
+ system managed gid or onto a winbind manged sid.
+ In the first case it will be threated like a mapped group
+ and the backend should take the member list with a getgrgid
+ and ignore any user that have been possibly set into the group
+ object.
+
+ In the second case, the group is a fully SAM managed group
+ served back to the system through winbind. In this case the
+ members of a Local group are "unrolled" to cope with the fact
+ that unix cannot contain groups inside groups.
+ The backend MUST never call any getgr* / getpw* function or
+ loops with winbind may happen.
+ ****************************************************************************/
+
+#if 0
+NTSTATUS is_mapped_group(BOOL *mapped, const DOM_SID *sid)
+{
+ NTSTATUS result;
+ gid_t id;
+
+ /* look if mapping exist, do not make idmap alloc an uid if SID is not found */
+ result = idmap_get_gid_from_sid(&id, sid, False);
+ if (NT_STATUS_IS_OK(result)) {
+ *mapped = gid_is_in_winbind_range(id);
+ } else {
+ *mapped = False;
+ }
+
+ return result;
+}
+#endif
+
+#define ALIAS_DEFAULT_SACL_SA_RIGHTS 0x01050013
+#define ALIAS_DEFAULT_DACL_SA_RIGHTS \
+ (READ_CONTROL_ACCESS | \
+ SA_RIGHT_ALIAS_LOOKUP_INFO | \
+ SA_RIGHT_ALIAS_GET_MEMBERS) /* 0x0002000c */
+
+#define ALIAS_DEFAULT_SACL_SEC_ACE_FLAG (SEC_ACE_FLAG_FAILED_ACCESS | SEC_ACE_FLAG_SUCCESSFUL_ACCESS) /* 0xc0 */
+
+
+NTSTATUS create_builtin_alias_default_sec_desc(SEC_DESC **sec_desc, TALLOC_CTX *ctx)
+{
+ DOM_SID *world = &global_sid_World;
+ DOM_SID *admins = &global_sid_Builtin_Administrators;
+ SEC_ACCESS sa;
+ SEC_ACE sacl_ace;
+ SEC_ACE dacl_aces[2];
+ SEC_ACL *sacl = NULL;
+ SEC_ACL *dacl = NULL;
+ size_t psize;
+
+ init_sec_access(&sa, ALIAS_DEFAULT_SACL_SA_RIGHTS);
+ init_sec_ace(&sacl_ace, world, SEC_ACE_TYPE_SYSTEM_AUDIT, sa, ALIAS_DEFAULT_SACL_SEC_ACE_FLAG);
+
+ sacl = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &sacl_ace);
+ if (!sacl) {
+ DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ init_sec_access(&sa, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ init_sec_ace(&(dacl_aces[0]), world, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+ init_sec_access(&sa, SA_RIGHT_ALIAS_ALL_ACCESS);
+ init_sec_ace(&(dacl_aces[1]), admins, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+ dacl = make_sec_acl(ctx, NT4_ACL_REVISION, 2, dacl_aces);
+ if (!sacl) {
+ DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *sec_desc = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, admins, admins, sacl, dacl, &psize);
+ if (!(*sec_desc)) {
+ DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sec_desc_add_ace_to_dacl(SEC_DESC *sec_desc, TALLOC_CTX *ctx, DOM_SID *sid, uint32 mask)
+{
+ NTSTATUS result;
+ SEC_ACE *new_aces;
+ unsigned num_aces;
+ int i;
+
+ num_aces = sec_desc->dacl->num_aces + 1;
+ result = sec_ace_add_sid(ctx, &new_aces, sec_desc->dacl->ace, &num_aces, sid, mask);
+ if (NT_STATUS_IS_OK(result)) {
+ sec_desc->dacl->ace = new_aces;
+ sec_desc->dacl->num_aces = num_aces;
+ sec_desc->dacl->size = SEC_ACL_HEADER_SIZE;
+ for (i = 0; i < num_aces; i++) {
+ sec_desc->dacl->size += sec_desc->dacl->ace[i].size;
+ }
+ }
+ return result;
+}
+
+NTSTATUS gums_make_domain(DOM_SID *sid, const char *name, const char *description)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_DOMAIN)))
+ return ret;
+
+ ret = gums_set_object_sid(go, sid);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set sid!");
+
+ ret = gums_set_object_name(go, name);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set name!");
+
+ if (description) {
+ ret = gums_set_object_description(go, description);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set description!");
+ }
+
+ /* make security descriptor * /
+ ret = create_builtin_alias_default_sec_desc(&((*go).sec_desc), (*go).mem_ctx);
+ NTSTATUS_CHECK(ret, error, "gums_init_backend", "create_builtin_alias_default_sec_desc");
+ */
+
+ ret = fns->set_object(go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+NTSTATUS gums_make_alias(DOM_SID *sid, const char *name, const char *description)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_ALIAS)))
+ return ret;
+
+ ret = gums_set_object_sid(go, sid);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set sid!");
+
+ ret = gums_set_object_name(go, name);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set name!");
+
+ if (description) {
+ ret = gums_set_object_description(go, description);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set description!");
+ }
+
+ /* make security descriptor * /
+ ret = create_builtin_alias_default_sec_desc(&((*go).sec_desc), (*go).mem_ctx);
+ NTSTATUS_CHECK(ret, error, "gums_init_backend", "create_builtin_alias_default_sec_desc");
+ */
+
+ ret = fns->set_object(go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+NTSTATUS gums_init_domain(DOM_SID *sid, const char *name, const char * description)
+{
+ NTSTATUS ret;
+
+ /* Add the weelknown Builtin Domain */
+ if (!NT_STATUS_IS_OK(ret = gums_make_domain(
+ sid,
+ name,
+ description
+ ))) {
+ return ret;
+ }
+
+ /* Add default users and groups */
+ /* Administrator
+ Guest
+ Domain Administrators
+ Domain Users
+ Domain Guests
+ */
+
+ return ret;
+}
+
+NTSTATUS gums_init_builtin_domain(void)
+{
+ NTSTATUS ret;
+
+ generate_wellknown_sids();
+
+ /* Add the weelknown Builtin Domain */
+ if (!NT_STATUS_IS_OK(ret = gums_make_domain(
+ &global_sid_Builtin,
+ "BUILTIN",
+ "Builtin Domain"
+ ))) {
+ return ret;
+ }
+
+ /* Add the well known Builtin Local Groups */
+
+ /* Administrators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Administrators,
+ "Administrators",
+ "Members can fully administer the computer/domain"
+ ))) {
+ return ret;
+ }
+ /* Administrator privilege set */
+ /* From BDC join trace:
+ SeSecurityPrivilege, SeBackupPrivilege, SeRestorePrivilege,
+ SeSystemtimePrivilege, SeShutdownPrivilege,
+ SeRemoteShutdownPrivilege, SeTakeOwnershipPrivilege,
+ SeDebugPrivilege, SeSystemEnvironmentPrivilege,
+ SeSystemProfilePrivilege, SeProfileSingleProcessPrivilege,
+ SeIncreaseBasePriorityPrivilege, SeLocalDriverPrivilege,
+ SeCreatePagefilePrivilege, SeIncreaseQuotaPrivilege
+ */
+
+ /* Power Users */
+ /* Domain Controllers Does NOT have Power Users (?) */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Power_Users,
+ "Power Users",
+ "Power Users"
+ ))) {
+ return ret;
+ }
+
+ /* Power Users privilege set */
+ /* (?) */
+
+ /* Account Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Account_Operators,
+ "Account Operators",
+ "Members can administer domain user and group accounts"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeShutdownPrivilege
+ */
+
+ /* Server Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Server_Operators,
+ "Server Operators",
+ "Members can administer domain servers"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeSystemtimePrivilege,
+ SeShutdownPrivilege, SeRemoteShutdownPrivilege
+ */
+
+ /* Print Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Print_Operators,
+ "Print Operators",
+ "Members can administer domain printers"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeShutdownPrivilege
+ */
+
+ /* Backup Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Backup_Operators,
+ "Backup Operators",
+ "Members can bypass file security to backup files"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege
+ */
+
+ /* Replicator */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Replicator,
+ "Replicator",
+ "Supports file replication in a domain"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege
+ */
+
+ /* Users */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Users,
+ "Users",
+ "Ordinary users"
+ ))) {
+ return ret;
+ }
+
+ /* Users specific ACEs * /
+ sec_desc_add_ace_to_dacl(go->sec_desc, go->mem_ctx, &global_sid_Builtin_Account_Operators, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ sec_desc_add_ace_to_dacl(go->sec_desc, go->mem_ctx, &global_sid_Builtin_Power_Users, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ */
+
+ /* Guests */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Guests,
+ "Guests",
+ "Users granted guest access to the computer/domain"
+ ))) {
+ return ret;
+ }
+
+ return ret;
+}
+
diff --git a/source/sam/gums_tdbsam2.c b/source/sam/gums_tdbsam2.c
new file mode 100644
index 00000000000..7fb9a1a997f
--- /dev/null
+++ b/source/sam/gums_tdbsam2.c
@@ -0,0 +1,1220 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * tdbsam2 - sam backend
+ * Copyright (C) Simo Sorce 2002-2003
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 675
+ * Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "includes.h"
+#include "tdbsam2_parse_info.h"
+
+#if 0
+static int gums_tdbsam2_debug_class = DBGC_ALL;
+#endif
+/*
+#undef DBGC_CLASS
+#define DBGC_CLASS gums_tdbsam2_debug_class
+*/
+
+#define TDBSAM_VERSION 20021215
+#define TDB_FILE_NAME "tdbsam2.tdb"
+#define NAMEPREFIX "NAME_"
+#define SIDPREFIX "SID_"
+#define PRIVILEGEPREFIX "PRIV_"
+
+#define TDB_BASIC_OBJ_STRING "ddd"
+#define TDB_FORMAT_STRING "dddB"
+#define TDB_PRIV_FORMAT_STRING "ddB"
+
+#define TALLOC_CHECK(ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: Out of memory!\n", FUNCTION_MACRO)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define SET_OR_FAIL(func, label) do { if (!NT_STATUS_IS_OK(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+
+
+
+struct tdbsam2_enum_objs {
+ uint32 type;
+ DOM_SID *dom_sid;
+ TDB_CONTEXT *db;
+ TDB_DATA key;
+ struct tdbsam2_enum_objs *next;
+};
+
+struct tdbsam2_private_data {
+
+ const char *storage;
+ struct tdbsam2_enum_objs *teo_handlers;
+};
+
+static struct tdbsam2_private_data *ts2_privs;
+
+static NTSTATUS init_object_from_buffer(GUMS_OBJECT **go, char *buffer, int size)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ int iret;
+ char *obj_data = NULL;
+ int data_size = 0;
+ int version, type, seqnum;
+ int len;
+
+ mem_ctx = talloc_init("init_object_from_buffer");
+ if (!mem_ctx) {
+ DEBUG(0, ("init_object_from_buffer: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = tdb_unpack (buffer, size, TDB_FORMAT_STRING,
+ &version,
+ &type,
+ &seqnum,
+ &data_size, &obj_data);
+
+ if (len == -1 || data_size <= 0)
+ goto done;
+
+ /* version is checked inside this function so that backward
+ compatibility code can be called eventually.
+ This way we can easily handle database format upgrades */
+ if (version != TDBSAM_VERSION) {
+ DEBUG(3,("init_object_from_buffer: Error, db object has wrong tdbsam version!\n"));
+ goto done;
+ }
+
+ /* be sure the string is terminated before trying to parse it */
+ if (obj_data[data_size - 1] != '\0')
+ obj_data[data_size - 1] = '\0';
+
+ *go = (GUMS_OBJECT *)talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+ TALLOC_CHECK(*go, ret, done);
+
+ switch (type) {
+
+ case GUMS_OBJ_DOMAIN:
+ iret = gen_parse(mem_ctx, pinfo_gums_domain, (char *)(*go), obj_data);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ iret = gen_parse(mem_ctx, pinfo_gums_group, (char *)(*go), obj_data);
+ break;
+
+ case GUMS_OBJ_NORMAL_USER:
+ iret = gen_parse(mem_ctx, pinfo_gums_user, (char *)(*go), obj_data);
+ break;
+
+ default:
+ DEBUG(3,("init_object_from_buffer: Error, wrong object type number!\n"));
+ goto done;
+ }
+
+ if (iret != 0) {
+ DEBUG(0, ("init_object_from_buffer: Fatal Error! Unable to parse object!\n"));
+ DEBUG(0, ("init_object_from_buffer: DB Corrupt ?"));
+ goto done;
+ }
+
+ (*go)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+done:
+ SAFE_FREE(obj_data);
+ return ret;
+}
+
+static NTSTATUS init_privilege_from_buffer(GUMS_PRIVILEGE **priv, char *buffer, int size)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ int iret;
+ char *obj_data = NULL;
+ int data_size = 0;
+ int version, seqnum;
+ int len;
+
+ mem_ctx = talloc_init("init_privilege_from_buffer");
+ if (!mem_ctx) {
+ DEBUG(0, ("init_privilege_from_buffer: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = tdb_unpack (buffer, size, TDB_PRIV_FORMAT_STRING,
+ &version,
+ &seqnum,
+ &data_size, &obj_data);
+
+ if (len == -1 || data_size <= 0)
+ goto done;
+
+ /* version is checked inside this function so that backward
+ compatibility code can be called eventually.
+ This way we can easily handle database format upgrades */
+ if (version != TDBSAM_VERSION) {
+ DEBUG(3,("init_privilege_from_buffer: Error, db object has wrong tdbsam version!\n"));
+ goto done;
+ }
+
+ /* be sure the string is terminated before trying to parse it */
+ if (obj_data[data_size - 1] != '\0')
+ obj_data[data_size - 1] = '\0';
+
+ *priv = (GUMS_PRIVILEGE *)talloc_zero(mem_ctx, sizeof(GUMS_PRIVILEGE));
+ TALLOC_CHECK(*priv, ret, done);
+
+ iret = gen_parse(mem_ctx, pinfo_gums_privilege, (char *)(*priv), obj_data);
+
+ if (iret != 0) {
+ DEBUG(0, ("init_privilege_from_buffer: Fatal Error! Unable to parse object!\n"));
+ DEBUG(0, ("init_privilege_from_buffer: DB Corrupt ?"));
+ goto done;
+ }
+
+ (*priv)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+done:
+ SAFE_FREE(obj_data);
+ return ret;
+}
+
+static NTSTATUS init_buffer_from_object(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, GUMS_OBJECT *object)
+{
+
+ NTSTATUS ret;
+ char *genbuf = NULL;
+ size_t buflen;
+
+ if (!buffer)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ switch (gums_get_object_type(object)) {
+
+ case GUMS_OBJ_DOMAIN:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_domain, (char *)object, 0);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_group, (char *)object, 0);
+ break;
+
+ case GUMS_OBJ_NORMAL_USER:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_user, (char *)object, 0);
+ break;
+
+ default:
+ DEBUG(3,("init_buffer_from_object: Error, wrong object type number!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (genbuf == NULL) {
+ DEBUG(0, ("init_buffer_from_object: Fatal Error! Unable to dump object!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buflen = tdb_pack(NULL, 0, TDB_FORMAT_STRING,
+ TDBSAM_VERSION,
+ object->type,
+ object->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ *buffer = talloc(mem_ctx, buflen);
+ TALLOC_CHECK(*buffer, ret, done);
+
+ *len = tdb_pack(*buffer, buflen, TDB_FORMAT_STRING,
+ TDBSAM_VERSION,
+ object->type,
+ object->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ if (*len != buflen) {
+ DEBUG(0, ("init_buffer_from_object: something odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n",
+ buflen, *len));
+ *buffer = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+static NTSTATUS init_buffer_from_privilege(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, GUMS_PRIVILEGE *priv)
+{
+
+ NTSTATUS ret;
+ char *genbuf = NULL;
+ size_t buflen;
+
+ if (!buffer || !len || !mem_ctx || !priv)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ genbuf = gen_dump(mem_ctx, pinfo_gums_privilege, (char *)priv, 0);
+
+ if (genbuf == NULL) {
+ DEBUG(0, ("init_buffer_from_privilege: Fatal Error! Unable to dump object!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buflen = tdb_pack(NULL, 0, TDB_PRIV_FORMAT_STRING,
+ TDBSAM_VERSION,
+ priv->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ *buffer = talloc(mem_ctx, buflen);
+ TALLOC_CHECK(*buffer, ret, done);
+
+ *len = tdb_pack(*buffer, buflen, TDB_PRIV_FORMAT_STRING,
+ TDBSAM_VERSION,
+ priv->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ if (*len != buflen) {
+ DEBUG(0, ("init_buffer_from_privilege: something odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n",
+ buflen, *len));
+ *buffer = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+static NTSTATUS opentdb(TDB_CONTEXT **tdb, BOOL readonly)
+{
+ if (!tdb)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *tdb = tdb_open_log(ts2_privs->storage, 0, TDB_DEFAULT, readonly?(O_RDONLY):(O_RDWR | O_CREAT), 0600);
+ if (!(*tdb))
+ {
+ DEBUG(0, ("opentdb: Unable to open database (%s)!\n", ts2_privs->storage));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_object_by_sid(TDB_CONTEXT *tdb, GUMS_OBJECT **obj, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ TDB_DATA data, key;
+ fstring keystr;
+
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sid_string_static(sid));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_by_sid: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(init_object_from_buffer(obj, data.dptr, data.dsize))) {
+ DEBUG(0, ("get_object_by_sid: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+static NTSTATUS make_full_object_name(TDB_CONTEXT *tdb, fstring objname, GUMS_OBJECT *object)
+{
+ NTSTATUS ret;
+
+ objname[0] = '\0';
+
+ if (gums_get_object_type(object) == GUMS_OBJ_DOMAIN) {
+
+ fstrcpy(objname, gums_get_object_name(object));
+
+ } else {
+ GUMS_OBJECT *domain_object;
+ DOM_SID domain_sid;
+ uint32 *discard_rid;
+
+ sid_copy(&domain_sid, gums_get_object_sid(object));
+ sid_split_rid(&domain_sid, discard_rid);
+
+ if (!NT_STATUS_IS_OK(get_object_by_sid(tdb,
+ &domain_object,
+ &domain_sid))) {
+
+ DEBUG(3, ("Object's domain not found!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ fstrcpy(objname, gums_get_object_name(domain_object));
+ fstrcat(objname, "\\");
+ fstrcat(objname, gums_get_object_name(object));
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/* name should be in DOMAIN\NAME format */
+static NTSTATUS get_object_by_name(TDB_CONTEXT *tdb, GUMS_OBJECT **obj, const char *fullname)
+{
+
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring objname;
+ DOM_SID sid;
+ fstring sidstr;
+ int sidstr_len;
+
+ if (!obj || !fullname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Data is stored in all lower-case */
+ fstrcpy(objname, fullname);
+ strlower_m(objname);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", NAMEPREFIX, objname);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_by_name: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ fstrcpy(sidstr, data.dptr);
+ sidstr_len = data.dsize;
+
+ SAFE_FREE(data.dptr);
+
+ if (sidstr_len <= 0) {
+ DEBUG(5, ("get_object_by_name: Error unpacking database object!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!string_to_sid(&sid, sidstr)) {
+ DEBUG(5, ("get_object_by_name: Error invalid sid string found in database object!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+done:
+ if (NT_STATUS_IS_OK(ret))
+ return get_object_by_sid(tdb, obj, &sid);
+ return ret;
+}
+
+/* Get object's sequence number */
+
+static NTSTATUS get_object_seq_num(TDB_CONTEXT *tdb, GUMS_OBJECT *object, int *seq_num)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring sidstr;
+ int version, type, seqnum;
+
+ if (!object || !seq_num)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ fstrcpy(sidstr, sid_string_static(gums_get_object_sid(object)));
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sidstr);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_seq_num: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (tdb_unpack (data.dptr, data.dsize, TDB_BASIC_OBJ_STRING, &version, &type, &seqnum) == -1)
+ goto done;
+
+ *seq_num = seqnum;
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/* store a gums object
+ * flag: TDB_REPLACE or TDB_MODIFY or TDB_INSERT
+ */
+
+static NTSTATUS store_object(TDB_CONTEXT *tdb, GUMS_OBJECT *object, int flag)
+{
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_DATA data, data2, key, key2;
+ TALLOC_CTX *mem_ctx;
+ fstring keystr;
+ fstring sidstr;
+ fstring namestr;
+ fstring objname;
+ int r;
+
+ /* TODO: on object renaming/replacing this function should
+ * check name->sid record and delete the old one
+ */
+
+ mem_ctx = talloc_init("store_object");
+ if (!mem_ctx) {
+ DEBUG(0, ("store_object: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ make_full_object_name(tdb, objname, object);
+
+ /* Data is stored in all lower-case */
+ strlower_m(objname);
+
+ if (flag == TDB_MODIFY) {
+ if (!NT_STATUS_IS_OK(ret = get_object_seq_num(tdb, object, &(object->seq_num))))
+ goto done;
+ object->seq_num += 1;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = init_buffer_from_object(&(data.dptr), &(data.dsize), mem_ctx, object)))
+ goto done;
+
+ fstrcpy(sidstr, sid_string_static(gums_get_object_sid(object)));
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sidstr);
+ slprintf(namestr, sizeof(namestr) - 1, "%s%s", NAMEPREFIX, objname);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ if ((r = tdb_store(tdb, key, data, flag)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to modify TDBSAM!\n"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(tdb)));
+ DEBUGADD(0, (" occured while storing sid record (%s)\n", keystr));
+ if (r == TDB_ERR_EXISTS)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ else
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ data2.dptr = sidstr;
+ data2.dsize = strlen(sidstr) + 1;
+ key2.dptr = namestr;
+ key2.dsize = strlen(namestr) + 1;
+
+ if ((r = tdb_store(tdb, key2, data2, flag)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to modify TDBSAM!\n"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(tdb)));
+ DEBUGADD(0, (" occured while storing name record (%s)\n", keystr));
+ DEBUGADD(0, (" attempting rollback operation.\n"));
+ if ((tdb_delete(tdb, key)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to rollback! Check database consitency!\n"));
+ }
+ if (r == TDB_ERR_EXISTS)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ else
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+/* TODO: update the general database counter */
+/* TODO: update this entry counter too */
+
+done:
+ talloc_destroy(mem_ctx);
+ return ret;
+}
+
+/* GUMM object functions */
+
+static NTSTATUS tdbsam2_get_domain_sid(DOM_SID *sid, const char* name)
+{
+
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ fstring domname;
+
+ if (!sid || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ /* Data is stored in all lower-case */
+ fstrcpy(domname, name);
+ strlower_m(domname);
+
+ if (!NT_STATUS_IS_OK(ret = get_object_by_name(tdb, &go, domname))) {
+ go = NULL;
+ DEBUG(0, ("tdbsam2_get_domain_sid: Error fetching database!\n"));
+ goto done;
+ }
+
+ if (gums_get_object_type(go) != GUMS_OBJ_DOMAIN) {
+ DEBUG(5, ("tdbsam2_get_domain_sid: Requested object is not a domain!\n"));
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto done;
+ }
+
+ sid_copy(sid, gums_get_object_sid(go));
+
+ ret = NT_STATUS_OK;
+
+done:
+ if (go)
+ gums_destroy_object(&go);
+ tdb_close(tdb);
+ return ret;
+}
+
+static NTSTATUS get_next_sid(TDB_CONTEXT *tdb, DOM_SID *sid)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ DOM_SID dom_sid;
+ TDB_DATA dom_sid_key;
+ fstring dom_sid_str;
+ uint32 new_rid;
+
+ /* Find the domain SID */
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, global_myname()))) {
+ DEBUG(0, ("get_next_sid: cannot found the domain sid!!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Lock the domain record */
+ sid_to_string(dom_sid_str, &dom_sid);
+ dom_sid_key.dptr = dom_sid_str;
+ dom_sid_key.dsize = strlen(dom_sid_key.dptr) + 1;
+
+ if(tdb_chainlock(tdb, dom_sid_key) != 0) {
+ DEBUG(0, ("get_next_sid: unable to lock domain record!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get the domain object */
+ ret = get_object_by_sid(tdb, &go, &dom_sid);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("get_next_sid: unable to get root Domain object!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ new_rid = gums_get_domain_next_rid(go);
+
+ /* Increment the RID Counter */
+ gums_set_domain_next_rid(go, new_rid+1);
+
+ /* Store back Domain object */
+ ret = store_object(tdb, go, TDB_MODIFY);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("get_next_sid: unable to update root Domain object!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ /* Build the Domain SID to return */
+ sid_copy(sid, &dom_sid);
+
+ if (!sid_append_rid(sid, new_rid)) {
+ DEBUG(0, ("get_next_sid: unable to build new SID !?!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* Unlock the Domain object */
+ tdb_chainunlock(tdb, dom_sid_key);
+
+ return ret;
+}
+
+/* TODO */
+ NTSTATUS (*get_sequence_number) (void);
+
+
+extern DOM_SID global_sid_NULL;
+
+static NTSTATUS tdbsam2_new_object(DOM_SID *sid, const char *name, const int obj_type)
+{
+
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ NTTIME null_time;
+ DATA_BLOB pw;
+ const char *defpw = "NOPASSWORDXXXXXX";
+ uint8 defhours[21] = {255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255};
+
+ if (!name) {
+ DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, obj_type))) {
+ go = NULL;
+ goto done;
+ }
+
+ if (obj_type == GUMS_OBJ_DOMAIN) {
+ sid_copy(sid, get_global_sam_sid());
+ } else {
+ if (!NT_STATUS_IS_OK(ret = get_next_sid(tdb, sid)))
+ goto done;
+ }
+
+ gums_set_object_sid(go, sid);
+ gums_set_object_name(go, name);
+ gums_set_object_seq_num(go, 1);
+
+ /*obj.domain->sec_desc*/
+
+ switch (obj_type) {
+ case GUMS_OBJ_NORMAL_USER:
+
+ init_nt_time(&null_time);
+
+ gums_set_user_logon_time(go, null_time);
+ gums_set_user_logoff_time(go, null_time);
+ gums_set_user_kickoff_time(go, null_time);
+ gums_set_user_pass_last_set_time(go, null_time);
+ gums_set_user_pass_can_change_time(go, null_time);
+ gums_set_user_pass_must_change_time(go, null_time);
+
+ pw = data_blob(defpw, NT_HASH_LEN);
+ gums_set_user_nt_pwd(go, pw);
+ gums_set_user_lm_pwd(go, pw);
+ data_blob_free(&pw);
+
+ gums_set_user_logon_divs(go, 168);
+ gums_set_user_hours(go, 21, defhours);
+
+ gums_set_user_bad_password_count(go, 0);
+ gums_set_user_logon_count(go, 0);
+ gums_set_user_unknown_6(go, 0x000004ec);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+
+ break;
+
+ case GUMS_OBJ_DOMAIN:
+
+ gums_set_domain_next_rid(go, 0x3e9);
+
+ break;
+
+ default:
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto done;
+ }
+
+ ret = store_object(tdb, go, TDB_INSERT);
+
+done:
+ if (go)
+ gums_destroy_object(&go);
+ tdb_close(tdb);
+ return ret;
+}
+
+/* TODO: handle privileges objects */
+
+static NTSTATUS tdbsam2_delete_object(const DOM_SID *sid)
+{
+ /* TODO: need to address privilege deletion */
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ TDB_DATA data, key;
+ fstring keystr;
+
+ if (!sid) {
+ DEBUG(0, ("tdbsam2_delete_object: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sid_string_static(sid));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("tdbsam2_delete_object: Error fetching database, SID entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (tdb_delete(tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(init_object_from_buffer(&go, data.dptr, data.dsize))) {
+ DEBUG(0, ("tdbsam2_delete_object: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", NAMEPREFIX, gums_get_object_name(go));
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ if (tdb_delete(tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+/* TODO: update the general database counter */
+
+done:
+ gums_destroy_object(&go);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+static NTSTATUS tdbsam2_get_object_from_sid(GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+
+ if (!object || !sid) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ ret = get_object_by_sid(tdb, object, sid);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+ if (obj_type && gums_get_object_type(*object) != obj_type) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: the object is not of the rerquested type!\n"));
+ goto error;
+ }
+
+ tdb_close(tdb);
+ return NT_STATUS_OK;
+
+error:
+ gums_destroy_object(object);
+ tdb_close(tdb);
+ return ret;
+}
+
+static NTSTATUS tdbsam2_get_object_from_name(GUMS_OBJECT **object, const char *domain, const char *name, const int obj_type)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ fstring objname;
+
+ if (!object || !name) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ if (obj_type == GUMS_OBJ_DOMAIN) {
+ fstrcpy(objname, name);
+ } else {
+ if (!domain) {
+ domain = global_myname();
+ }
+ fstrcpy(objname, domain);
+ fstrcat(objname, "\\");
+ fstrcat(objname, name);
+ }
+
+ *object = NULL;
+ ret = get_object_by_name(tdb, object, name);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+ if (obj_type && gums_get_object_type(*object) != obj_type) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: the object is not of the rerquested type!\n"));
+ goto error;
+ }
+
+ tdb_close(tdb);
+ return NT_STATUS_OK;
+
+error:
+ gums_destroy_object(object);
+ tdb_close(tdb);
+ return ret;
+}
+
+ /* This function is used to get the list of all objects changed since base_time, it is
+ used to support PDC<->BDC synchronization */
+ NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+static NTSTATUS tdbsam2_enumerate_objects_start(void **handle, const DOM_SID *sid, const int obj_type)
+{
+ struct tdbsam2_enum_objs *teo, *t;
+
+ teo = (struct tdbsam2_enum_objs *)malloc(sizeof(struct tdbsam2_enum_objs));
+ if (!teo) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(teo, 0, sizeof(struct tdbsam2_enum_objs));
+
+ teo->type = obj_type;
+ if (sid) {
+ teo->dom_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ if (!teo->dom_sid) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid_copy(teo->dom_sid, sid);
+ }
+
+ if (!NT_STATUS_IS_OK(opentdb(&(teo->db), True)))
+ {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Unable to open database (%s)!\n", ts2_privs->storage));
+ SAFE_FREE(teo);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!ts2_privs->teo_handlers) {
+ ts2_privs->teo_handlers = teo;
+ } else {
+ t = ts2_privs->teo_handlers;
+ while (t->next) {
+ t = t->next;
+ }
+ t->next = teo;
+ }
+
+ *handle = teo;
+
+ teo->key = tdb_firstkey(teo->db);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_enumerate_objects_get_next(GUMS_OBJECT **object, void *handle)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ struct tdbsam2_enum_objs *teo;
+ const char *prefix = SIDPREFIX;
+ const int preflen = strlen(prefix);
+ fstring dom_sid_str;
+ int dom_sid_str_len = 0;
+
+ if (!object || !handle) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ teo = (struct tdbsam2_enum_objs *)handle;
+
+ if (teo->dom_sid) {
+ sid_to_string(dom_sid_str, teo->dom_sid);
+ dom_sid_str_len = strlen(dom_sid_str);
+ }
+
+ while ((teo->key.dptr != NULL)) {
+ int len, version, type, size, seqnum;
+ char *ptr;
+
+ if (strncmp(teo->key.dptr, prefix, preflen)) {
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+
+ if (dom_sid_str_len != 0) {
+ if (strncmp(&(teo->key.dptr[preflen]), dom_sid_str, dom_sid_str_len)) {
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+ }
+
+ data = tdb_fetch(teo->db, teo->key);
+ if (!data.dptr) {
+ DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error fetching database, SID entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(teo->db)));
+ DEBUGADD(5, (" Key: %s\n", teo->key.dptr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ len = tdb_unpack (data.dptr, data.dsize, TDB_FORMAT_STRING,
+ &version,
+ &type,
+ &seqnum,
+ &size, &ptr);
+
+ if (len == -1) {
+ DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error unable to unpack data!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ SAFE_FREE(ptr);
+
+ if (teo->type && type != teo->type) {
+ SAFE_FREE(data.dptr);
+ data.dsize = 0;
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+
+ break;
+ }
+
+ if (teo->key.dptr == NULL) { /* no more objs */
+ ret = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = init_object_from_buffer(object, data.dptr, data.dsize))) {
+ SAFE_FREE(data.dptr);
+ DEBUG(0, ("tdbsam2_enumerate_objects_get_next: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ SAFE_FREE(data.dptr);
+
+ /* prepare next run */
+ teo->key = tdb_nextkey(teo->db, teo->key);
+
+done:
+ return ret;
+}
+
+static NTSTATUS tdbsam2_enumerate_objects_stop(void *handle)
+{
+ struct tdbsam2_enum_objs *teo, *t, *p;
+
+ teo = (struct tdbsam2_enum_objs *)handle;
+
+ if (ts2_privs->teo_handlers == teo) {
+ ts2_privs->teo_handlers = teo->next;
+ } else {
+ t = ts2_privs->teo_handlers;
+ while (t != teo) {
+ p = t;
+ t = t->next;
+ if (t == NULL) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_stop: Error, handle not found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ p = t->next;
+ }
+
+ tdb_close(teo->db);
+ SAFE_FREE(teo->dom_sid);
+ SAFE_FREE(teo);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_set_object(GUMS_OBJECT *go)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+
+ if (!go)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ ret = store_object(tdb, go, TDB_REPLACE);
+
+ tdb_close(tdb);
+ return ret;
+}
+
+#if 0
+ /* set object values function */
+static NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+ /* Group related functions */
+static NTSTATUS (*add_memberss_to_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+static NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+static NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+static NTSTATUS (*lock_sid) (const DOM_SID *sid);
+static NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+ /* privileges related functions */
+
+static NTSTATUS (*get_privilege) (GUMS_OBJECT **object, const char *name);
+static NTSTATUS (*add_members_to_privilege) (const char *name, const DOM_SID **members);
+static NTSTATUS (*delete_members_from_privilege) (const char *name, const DOM_SID **members);
+static NTSTATUS (*enumerate_privilege_members) (const char *name, DOM_SID **members);
+static NTSTATUS (*get_sid_privileges) (const DOM_SID *sid, const char **privs);
+
+ /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+static NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+#endif
+
+static void free_tdbsam2_private_data(void **vp)
+{
+ struct tdbsam2_private_data **tdb_privs = (struct tdbsam2_private_data **)vp;
+ while (ts2_privs->teo_handlers)
+ tdbsam2_enumerate_objects_stop(ts2_privs->teo_handlers);
+ *tdb_privs = NULL;
+ /* No need to free any further, as it is talloc()ed */
+}
+
+static NTSTATUS init_tdbsam2(GUMS_FUNCTIONS *fns, const char *storage)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ DOM_SID dom_sid;
+
+ fns->name = talloc_strdup(fns->mem_ctx, "tdbsam2");
+
+ fns->get_domain_sid = tdbsam2_get_domain_sid;
+ /* fns->get_sequence_number = tdbsam2_get_sequence_number; */
+ fns->new_object = tdbsam2_new_object;
+ fns->delete_object = tdbsam2_delete_object;
+ fns->get_object_from_sid = tdbsam2_get_object_from_sid;
+ fns->get_object_from_name = tdbsam2_get_object_from_name;
+ /* fns->get_updated_objects = tdbsam2_get_updated_objects; */
+ fns->enumerate_objects_start = tdbsam2_enumerate_objects_start;
+ fns->enumerate_objects_get_next = tdbsam2_enumerate_objects_get_next;
+ fns->enumerate_objects_stop = tdbsam2_enumerate_objects_stop;
+ fns->set_object = tdbsam2_set_object;
+ /* fns->set_object_values = tdbsam2_set_object_values;
+ fns->add_members_to_group = tdbsam2_add_members_to_group;
+ fns->delete_members_from_group = tdbsam2_delete_members_from_group;
+ fns->enumerate_group_members = tdbsam2_enumerate_group_members;
+ fns->get_sid_groups = tdbsam2_get_sid_groups;
+ fns->lock_sid = tdbsam2_lock_sid;
+ fns->unlock_sid = tdbsam2_unlock_sid;
+ fns->get_privilege = tdbsam2_get_privilege;
+ fns->add_members_to_privilege = tdbsam2_add_members_to_privilege;
+ fns->delete_members_from_privilege = tdbsam2_delete_members_from_privilege;
+ fns->enumerate_privilege_members = tdbsam2_enumerate_privilege_members;
+ fns->get_sid_privileges = tdbsam2_get_sid_privileges;
+ fns->set_privilege = tdbsam2_set_privilege; */
+
+ ts2_privs = talloc_zero(fns->mem_ctx, sizeof(struct tdbsam2_private_data));
+ if (!ts2_privs) {
+ DEBUG(0, ("talloc() failed for tdbsam2 private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (storage) {
+ ts2_privs->storage = talloc_strdup(fns->mem_ctx, storage);
+ } else {
+ pstring tdbfile;
+ get_private_directory(tdbfile);
+ pstrcat(tdbfile, "/");
+ pstrcat(tdbfile, TDB_FILE_NAME);
+ ts2_privs->storage = talloc_strdup(fns->mem_ctx, tdbfile);
+ }
+
+ /* check tdb exist (or create it) */
+
+ /* Find the domain SID */
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, global_myname()))) {
+ /* db file does not exist or it is not inited */
+ /* make the tdb file */
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+ tdb_close(tdb);
+
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, "BUILTIN"))) {
+ gums_init_builtin_domain();
+ }
+
+ gums_init_domain(get_global_sam_sid(), global_myname(), "The Domain");
+ }
+
+ fns->private_data = &ts2_privs;
+ fns->free_private_data = free_tdbsam2_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_tdbsam2_init(void)
+{
+ /*
+ if ((gums_tdbsam2_debug_class = debug_add_class("gums_tdbsam2")) == -1) {
+ DEBUG(0, ("gums_tdbsam2: unable to register my own debug class! going on ...\n"));
+ gums_tdbsam2_debug_class = DBGC_ALL;
+ }
+ */
+ return gums_register_module(GUMS_INTERFACE_VERSION, "tdbsam2", init_tdbsam2);
+}
diff --git a/source/sam/idmap.c b/source/sam/idmap.c
new file mode 100644
index 00000000000..4d8b768c2fa
--- /dev/null
+++ b/source/sam/idmap.c
@@ -0,0 +1,317 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Jeremy Allison 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_function_entry {
+ const char *name;
+ struct idmap_methods *methods;
+ struct idmap_function_entry *prev,*next;
+};
+
+static struct idmap_function_entry *backends = NULL;
+
+static struct idmap_methods *cache_map;
+static struct idmap_methods *remote_map;
+
+/**********************************************************************
+ Get idmap methods. Don't allow tdb to be a remote method.
+**********************************************************************/
+
+static struct idmap_methods *get_methods(const char *name, BOOL cache_method)
+{
+ struct idmap_function_entry *entry = backends;
+
+ for(entry = backends; entry; entry = entry->next) {
+ if (!cache_method && strequal(entry->name, "tdb"))
+ continue; /* tdb is only cache method. */
+ if (strequal(entry->name, name))
+ return entry->methods;
+ }
+
+ return NULL;
+}
+
+/**********************************************************************
+ Allow a module to register itself as a method.
+**********************************************************************/
+
+NTSTATUS smb_register_idmap(int version, const char *name, struct idmap_methods *methods)
+{
+ struct idmap_function_entry *entry;
+
+ if ((version != SMB_IDMAP_INTERFACE_VERSION)) {
+ DEBUG(0, ("smb_register_idmap: Failed to register idmap module.\n"
+ "The module was compiled against SMB_IDMAP_INTERFACE_VERSION %d,\n"
+ "current SMB_IDMAP_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current version of samba!\n",
+ version, SMB_IDMAP_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !methods) {
+ DEBUG(0,("smb_register_idmap: called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (get_methods(name, False)) {
+ DEBUG(0,("smb_register_idmap: idmap module %s already registered!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct idmap_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->methods = methods;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("smb_register_idmap: Successfully added idmap backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise idmap cache and a remote backend (if configured).
+**********************************************************************/
+
+BOOL idmap_init(const char *remote_backend)
+{
+ if (!backends)
+ static_init_idmap;
+
+ if (!cache_map) {
+ cache_map = get_methods("tdb", True);
+
+ if (!cache_map) {
+ DEBUG(0, ("idmap_init: could not find tdb cache backend!\n"));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(cache_map->init( NULL ))) {
+ DEBUG(0, ("idmap_init: could not initialise tdb cache backend!\n"));
+ return False;
+ }
+ }
+
+ if (!remote_map && remote_backend && *remote_backend != 0) {
+ char *rem_backend = smb_xstrdup(remote_backend);
+ fstring params = "";
+ char *pparams;
+
+ /* get any mode parameters passed in */
+
+ if ( (pparams = strchr( rem_backend, ':' )) != NULL ) {
+ *pparams = '\0';
+ pparams++;
+ fstrcpy( params, pparams );
+ }
+
+ DEBUG(3, ("idmap_init: using '%s' as remote backend\n", rem_backend));
+
+ if((remote_map = get_methods(rem_backend, False)) ||
+ (NT_STATUS_IS_OK(smb_probe_module("idmap", rem_backend)) &&
+ (remote_map = get_methods(rem_backend, False)))) {
+ remote_map->init(params);
+ } else {
+ DEBUG(0, ("idmap_init: could not load remote backend '%s'\n", rem_backend));
+ SAFE_FREE(rem_backend);
+ return False;
+ }
+ SAFE_FREE(rem_backend);
+ }
+
+ return True;
+}
+
+/**************************************************************************
+ This is a rare operation, designed to allow an explicit mapping to be
+ set up for a sid to a POSIX id.
+**************************************************************************/
+
+NTSTATUS idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ struct idmap_methods *map = remote_map;
+ DOM_SID tmp_sid;
+
+ DEBUG(10, ("idmap_set_mapping: Set %s to %s %lu\n",
+ sid_string_static(sid),
+ ((id_type & ID_TYPEMASK) == ID_USERID) ? "UID" : "GID",
+ ((id_type & ID_TYPEMASK) == ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid));
+
+ if ( (NT_STATUS_IS_OK(cache_map->
+ get_sid_from_id(&tmp_sid, id,
+ id_type | ID_QUERY_ONLY))) &&
+ sid_equal(sid, &tmp_sid) ) {
+ /* Nothing to do, we already have that mapping */
+ DEBUG(10, ("idmap_set_mapping: Mapping already there\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (map == NULL) {
+ /* Ok, we don't have a authoritative remote
+ mapping. So update our local cache only. */
+ map = cache_map;
+ }
+
+ return map->set_mapping(sid, id, id_type);
+}
+
+/**************************************************************************
+ Get ID from SID. This can create a mapping for a SID to a POSIX id.
+**************************************************************************/
+
+NTSTATUS idmap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ int loc_type;
+
+ loc_type = *id_type;
+
+ if (remote_map) {
+ /* We have a central remote idmap so only look in
+ cache, don't allocate */
+ loc_type |= ID_QUERY_ONLY;
+ }
+
+ ret = cache_map->get_id_from_sid(id, &loc_type, sid);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ *id_type = loc_type & ID_TYPEMASK;
+ return NT_STATUS_OK;
+ }
+
+ if (remote_map == NULL) {
+ return ret;
+ }
+
+ /* Ok, the mapping was not in the cache, give the remote map a
+ second try. */
+
+ ret = remote_map->get_id_from_sid(id, id_type, sid);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* The remote backend gave us a valid mapping, cache it. */
+ ret = cache_map->set_mapping(sid, *id, *id_type);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Get SID from ID. This must have been created before.
+**************************************************************************/
+
+NTSTATUS idmap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ NTSTATUS ret;
+ int loc_type;
+
+ loc_type = id_type;
+ if (remote_map) {
+ loc_type = id_type | ID_QUERY_ONLY;
+ }
+
+ ret = cache_map->get_sid_from_id(sid, id, loc_type);
+
+ if (NT_STATUS_IS_OK(ret))
+ return ret;
+
+ if (remote_map == NULL)
+ return ret;
+
+ /* We have a second chance, ask our authoritative backend */
+
+ ret = remote_map->get_sid_from_id(sid, id, id_type);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* The remote backend gave us a valid mapping, cache it. */
+ ret = cache_map->set_mapping(sid, id, id_type);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Alloocate a new UNIX uid/gid
+**************************************************************************/
+
+NTSTATUS idmap_allocate_id(unid_t *id, int id_type)
+{
+ /* we have to allocate from the authoritative backend */
+
+ if ( remote_map )
+ return remote_map->allocate_id( id, id_type );
+
+ return cache_map->allocate_id( id, id_type );
+}
+
+/**************************************************************************
+ Alloocate a new RID
+**************************************************************************/
+
+NTSTATUS idmap_allocate_rid(uint32 *rid, int type)
+{
+ /* we have to allocate from the authoritative backend */
+
+ if ( remote_map )
+ return remote_map->allocate_rid( rid, type );
+
+ return cache_map->allocate_rid( rid, type );
+}
+
+/**************************************************************************
+ Shutdown maps.
+**************************************************************************/
+
+NTSTATUS idmap_close(void)
+{
+ NTSTATUS ret;
+
+ ret = cache_map->close();
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("idmap_close: failed to close local tdb cache!\n"));
+ }
+ cache_map = NULL;
+
+ if (remote_map) {
+ ret = remote_map->close();
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("idmap_close: failed to close remote idmap repository!\n"));
+ }
+ remote_map = NULL;
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Dump backend status.
+**************************************************************************/
+
+void idmap_status(void)
+{
+ cache_map->status();
+ if (remote_map)
+ remote_map->status();
+}
diff --git a/source/sam/idmap_ldap.c b/source/sam/idmap_ldap.c
new file mode 100644
index 00000000000..2124fb68793
--- /dev/null
+++ b/source/sam/idmap_ldap.c
@@ -0,0 +1,790 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap LDAP backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "smbldap.h"
+
+struct ldap_idmap_state {
+ struct smbldap_state *smbldap_state;
+ TALLOC_CTX *mem_ctx;
+};
+
+static struct ldap_idmap_state ldap_state;
+
+/* number tries while allocating new id */
+#define LDAP_MAX_ALLOC_ID 128
+
+
+/***********************************************************************
+ This function cannot be called to modify a mapping, only set a new one
+***********************************************************************/
+
+static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ pstring dn;
+ pstring id_str;
+ fstring type;
+ LDAPMod **mods = NULL;
+ int rc = -1;
+ int ldap_op;
+ fstring sid_string;
+ LDAPMessage *entry = NULL;
+
+ sid_to_string( sid_string, sid );
+
+ ldap_op = LDAP_MOD_ADD;
+ pstr_sprintf(dn, "%s=%s,%s", get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID),
+ sid_string, lp_ldap_idmap_suffix());
+
+ if ( id_type & ID_USERID )
+ fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER ) );
+ else
+ fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER ) );
+
+ pstr_sprintf(id_str, "%lu", ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid));
+
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDMAP_ENTRY );
+
+ smbldap_make_mod( ldap_state.smbldap_state->ldap_struct,
+ entry, &mods, type, id_str );
+
+ smbldap_make_mod( ldap_state.smbldap_state->ldap_struct,
+ entry, &mods,
+ get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID),
+ sid_string );
+
+ /* There may well be nothing at all to do */
+
+ if (mods) {
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SID_ENTRY );
+ rc = smbldap_add(ldap_state.smbldap_state, dn, mods);
+ ldap_mods_free( mods, True );
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state.smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldap_set_mapping_internals: Failed to %s mapping from %s to %lu [%s]\n",
+ (ldap_op == LDAP_MOD_ADD) ? "add" : "replace",
+ sid_string, (unsigned long)((id_type & ID_USERID) ? id.uid : id.gid), type));
+ DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n",
+ ld_error ? ld_error : "(NULL)", ldap_err2string (rc)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to %lu [%s]\n",
+ sid_string, ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid), type));
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Even if the sambaDomain attribute in LDAP tells us that this RID is
+ safe to use, always check before use.
+*********************************************************************/
+
+static BOOL sid_in_use(struct ldap_idmap_state *state,
+ const DOM_SID *sid, int *error)
+{
+ fstring filter;
+ fstring sid_string;
+ LDAPMessage *result = NULL;
+ int count;
+ int rc;
+ char *sid_attr[] = {LDAP_ATTRIBUTE_SID, NULL};
+
+ slprintf(filter, sizeof(filter)-1, "(%s=%s)", LDAP_ATTRIBUTE_SID, sid_to_string(sid_string, sid));
+
+ rc = smbldap_search_suffix(state->smbldap_state,
+ filter, sid_attr, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to check if sid %s is alredy in use: %s\n",
+ sid_string, ld_error));
+ SAFE_FREE(ld_error);
+
+ *error = rc;
+ return True;
+ }
+
+ if ((count = ldap_count_entries(state->smbldap_state->ldap_struct, result)) > 0) {
+ DEBUG(3, ("Sid %s already in use - trying next RID\n",
+ sid_string));
+ ldap_msgfree(result);
+ return True;
+ }
+
+ ldap_msgfree(result);
+
+ /* good, sid is not in use */
+ return False;
+}
+
+/**********************************************************************
+ Set the new nextRid attribute, and return one we can use.
+
+ This also checks that this RID is actually free - in case the admin
+ manually stole it :-).
+*********************************************************************/
+
+static NTSTATUS ldap_next_rid(struct ldap_idmap_state *state, uint32 *rid,
+ int rid_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc;
+ LDAPMessage *domain_result = NULL;
+ LDAPMessage *entry = NULL;
+ char *dn;
+ LDAPMod **mods = NULL;
+ fstring old_rid_string;
+ fstring next_rid_string;
+ fstring algorithmic_rid_base_string;
+ uint32 next_rid;
+ uint32 alg_rid_base;
+ int attempts = 0;
+ char *ld_error = NULL;
+
+ while (attempts < 10) {
+ if (!NT_STATUS_IS_OK(ret = smbldap_search_domain_info(state->smbldap_state,
+ &domain_result, get_global_sam_name(), True))) {
+ return ret;
+ }
+
+ entry = ldap_first_entry(state->smbldap_state->ldap_struct, domain_result);
+ if (!entry) {
+ DEBUG(0, ("Could not get domain info entry\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if ((dn = smbldap_get_dn(state->smbldap_state->ldap_struct, entry)) == NULL) {
+ DEBUG(0, ("Could not get domain info DN\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ /* yes, we keep 3 seperate counters, one for rids between 1000 (BASE_RID) and
+ algorithmic_rid_base. The other two are to avoid stomping on the
+ different sets of algorithmic RIDs */
+
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string)) {
+
+ alg_rid_base = (uint32)atol(algorithmic_rid_base_string);
+ } else {
+ alg_rid_base = algorithmic_rid_base();
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string)-1, "%d", alg_rid_base);
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string);
+ }
+
+ next_rid = 0;
+
+ if (alg_rid_base > BASE_RID) {
+ /* we have a non-default 'algorithmic rid base', so we have 'low' rids that we
+ can allocate to new users */
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ } else {
+ *rid = BASE_RID;
+ }
+
+ next_rid = *rid+1;
+ if (next_rid >= alg_rid_base) {
+ ldap_msgfree(domain_result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ next_rid_string);
+ }
+
+ if (!next_rid) { /* not got one already */
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ }
+ break;
+ case GROUP_RID_TYPE:
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ }
+ break;
+ }
+
+ /* This is the core of the whole routine. If we had
+ scheme-style closures, there would be a *lot* less code
+ duplication... */
+
+ next_rid = *rid+RID_MULTIPLIER;
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ next_rid_string);
+ break;
+
+ case GROUP_RID_TYPE:
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ next_rid_string);
+ break;
+ }
+ }
+
+ if ((rc = smbldap_modify(state->smbldap_state, dn, mods)) == LDAP_SUCCESS) {
+ DOM_SID dom_sid;
+ DOM_SID sid;
+ pstring domain_sid_string;
+ int error = 0;
+
+ if (!smbldap_get_single_pstring(state->smbldap_state->ldap_struct, domain_result,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID),
+ domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if (!string_to_sid(&dom_sid, domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+
+ sid_copy(&sid, &dom_sid);
+ sid_append_rid(&sid, *rid);
+
+ /* check RID is not in use */
+ if (sid_in_use(state, &sid, &error)) {
+ if (error) {
+ return ret;
+ }
+ continue;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to modify rid: %s\n", ld_error ? ld_error : "(NULL"));
+ SAFE_FREE(ld_error);
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+
+ SAFE_FREE(dn);
+
+ ldap_msgfree(domain_result);
+ domain_result = NULL;
+
+ {
+ /* Sleep for a random timeout */
+ unsigned sleeptime = (sys_random()*sys_getpid()*attempts);
+ attempts += 1;
+
+ sleeptime %= 100;
+ smb_msleep(sleeptime);
+ }
+ }
+
+ DEBUG(0, ("Failed to set new RID\n"));
+ return ret;
+}
+
+
+/*****************************************************************************
+ Allocate a new RID
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_rid(uint32 *rid, int rid_type)
+{
+ return ldap_next_rid( &ldap_state, rid, rid_type );
+}
+
+/*****************************************************************************
+ Allocate a new uid or gid
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_id(unid_t *id, int id_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc = LDAP_SERVER_DOWN;
+ int count = 0;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring id_str, new_id_str;
+ LDAPMod **mods = NULL;
+ const char *type;
+ char *dn = NULL;
+ char **attr_list;
+ pstring filter;
+ uid_t luid, huid;
+ gid_t lgid, hgid;
+
+
+ type = (id_type & ID_USERID) ?
+ get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER ) :
+ get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER );
+
+ pstr_sprintf(filter, "(objectClass=%s)", LDAP_OBJ_IDPOOL);
+
+ attr_list = get_attr_list( idpool_attr_list );
+
+ rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_allocate_id: %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto out;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ if (count != 1) {
+ DEBUG(0,("ldap_allocate_id: single %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto out;
+ }
+
+ dn = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result);
+ if (!dn) {
+ goto out;
+ }
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, type, id_str)) {
+ DEBUG(0,("ldap_allocate_id: %s attribute not found\n",
+ type));
+ goto out;
+ }
+
+ /* this must succeed or else we wouldn't have initialized */
+
+ lp_idmap_uid( &luid, &huid);
+ lp_idmap_gid( &lgid, &hgid);
+
+ /* make sure we still have room to grow */
+
+ if (id_type & ID_USERID) {
+ id->uid = strtoul(id_str, NULL, 10);
+ if (id->uid > huid ) {
+ DEBUG(0,("ldap_allocate_id: Cannot allocate uid above %lu!\n",
+ (unsigned long)huid));
+ goto out;
+ }
+ }
+ else {
+ id->gid = strtoul(id_str, NULL, 10);
+ if (id->gid > hgid ) {
+ DEBUG(0,("ldap_allocate_id: Cannot allocate gid above %lu!\n",
+ (unsigned long)hgid));
+ goto out;
+ }
+ }
+
+ pstr_sprintf(new_id_str, "%lu",
+ ((id_type & ID_USERID) ? (unsigned long)id->uid :
+ (unsigned long)id->gid) + 1);
+
+ smbldap_set_mod( &mods, LDAP_MOD_DELETE, type, id_str );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, type, new_id_str );
+
+ if (mods == NULL) {
+ DEBUG(0,("ldap_allocate_id: smbldap_set_mod() failed.\n"));
+ goto out;
+ }
+
+ rc = smbldap_modify(ldap_state.smbldap_state, dn, mods);
+
+ ldap_mods_free( mods, True );
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_allocate_id: Failed to allocate new %s. ldap_modify() failed.\n",
+ type));
+ goto out;
+ }
+
+ ret = NT_STATUS_OK;
+out:
+ SAFE_FREE(dn);
+ if (result != NULL)
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ get a sid from an id
+*****************************************************************************/
+
+static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring sid_str;
+ pstring filter;
+ pstring suffix;
+ const char *type;
+ int rc;
+ int count;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ char **attr_list;
+
+ if ( id_type & ID_USERID )
+ type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER );
+ else
+ type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER );
+
+ pstrcpy( suffix, lp_ldap_idmap_suffix() );
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_IDMAP_ENTRY, type,
+ ((id_type & ID_USERID) ? (unsigned long)id.uid : (unsigned long)id.gid));
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("ldap_get_isd_from_id: Failure looking up entry (%s)\n",
+ ldap_err2string(rc) ));
+ goto out;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (count != 1) {
+ DEBUG(0,("ldap_get_sid_from_id: mapping not found for %s: %lu\n",
+ type, ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid)));
+ goto out;
+ }
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if ( !smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID, sid_str) )
+ goto out;
+
+ if (!string_to_sid(sid, sid_str))
+ goto out;
+
+ ret = NT_STATUS_OK;
+out:
+ free_attr_list( attr_list );
+
+ if (result)
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/***********************************************************************
+ Get an id from a sid
+***********************************************************************/
+
+static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring sid_str;
+ pstring filter;
+ pstring id_str;
+ const char *suffix;
+ const char *type;
+ int rc;
+ int count;
+ char **attr_list;
+ char *dn = NULL;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ sid_to_string(sid_str, sid);
+
+ DEBUG(8,("ldap_get_id_from_sid: %s (%s)\n", sid_str,
+ (*id_type & ID_GROUPID ? "group" : "user") ));
+
+ suffix = lp_ldap_idmap_suffix();
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_SID, sid_str);
+
+ if ( *id_type & ID_GROUPID )
+ type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER );
+ else
+ type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER );
+
+ /* do the search and check for errors */
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("ldap_get_id_from_sid: Failure looking up idmap entry (%s)\n",
+ ldap_err2string(rc) ));
+ goto out;
+ }
+
+ /* check for the number of entries returned */
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ if ( count > 1 ) {
+ DEBUG(0, ("ldap_get_id_from_sid: (2nd) search %s returned [%d] entries!\n",
+ filter, count));
+ goto out;
+ }
+
+ /* try to allocate a new id if we still haven't found one */
+
+ if ( !count ) {
+ int i;
+
+ if (*id_type & ID_QUERY_ONLY) {
+ DEBUG(5,("ldap_get_id_from_sid: No matching entry found and QUERY_ONLY flag set\n"));
+ goto out;
+ }
+
+ DEBUG(8,("ldap_get_id_from_sid: Allocating new id\n"));
+
+ for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
+ ret = ldap_allocate_id(id, *id_type);
+ if ( NT_STATUS_IS_OK(ret) )
+ break;
+ }
+
+ if ( !NT_STATUS_IS_OK(ret) ) {
+ DEBUG(0,("ldap_allocate_id: cannot acquire id lock!\n"));
+ goto out;
+ }
+
+ DEBUG(10,("ldap_get_id_from_sid: Allocated new %cid [%ul]\n",
+ (*id_type & ID_GROUPID ? 'g' : 'u'), (uint32)id->uid ));
+
+ ret = ldap_set_mapping(sid, *id, *id_type);
+
+ /* all done */
+
+ goto out;
+ }
+
+ DEBUG(10,("ldap_get_id_from_sid: success\n"));
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ dn = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result);
+ if (!dn)
+ goto out;
+
+ DEBUG(10, ("Found mapping entry at dn=%s, looking for %s\n", dn, type));
+
+ if ( smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, type, id_str) ) {
+ if ( (*id_type & ID_USERID) )
+ id->uid = strtoul(id_str, NULL, 10);
+ else
+ id->gid = strtoul(id_str, NULL, 10);
+
+ ret = NT_STATUS_OK;
+ goto out;
+ }
+
+out:
+ free_attr_list( attr_list );
+ if (result)
+ ldap_msgfree(result);
+ SAFE_FREE(dn);
+
+ return ret;
+}
+
+/**********************************************************************
+ Verify the sambaUnixIdPool entry in the directiry.
+**********************************************************************/
+
+static NTSTATUS verify_idpool( void )
+{
+ fstring filter;
+ int rc;
+ char **attr_list;
+ LDAPMessage *result = NULL;
+ LDAPMod **mods = NULL;
+ int count;
+
+ fstr_sprintf( filter, "(objectclass=%s)", LDAP_OBJ_IDPOOL );
+
+ attr_list = get_attr_list( idpool_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attr_list, 0, &result);
+ free_attr_list ( attr_list );
+
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ ldap_msgfree(result);
+
+ if ( count > 1 ) {
+ DEBUG(0,("ldap_idmap_init: multiple entries returned from %s (base == %s)\n",
+ filter, lp_ldap_idmap_suffix() ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ else if (count == 0) {
+ uid_t luid, huid;
+ gid_t lgid, hgid;
+ fstring uid_str, gid_str;
+
+ if ( !lp_idmap_uid(&luid, &huid) || !lp_idmap_gid( &lgid, &hgid ) ) {
+ DEBUG(0,("ldap_idmap_init: idmap uid/gid parameters not specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( uid_str, "%lu", (unsigned long)luid );
+ fstr_sprintf( gid_str, "%lu", (unsigned long)lgid );
+
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDPOOL );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER), uid_str );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER), gid_str );
+
+ rc = smbldap_modify(ldap_state.smbldap_state, lp_ldap_idmap_suffix(), mods);
+ }
+
+ return ( rc==LDAP_SUCCESS ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+
+static NTSTATUS ldap_idmap_init( char *params )
+{
+ NTSTATUS nt_status;
+
+ ldap_state.mem_ctx = talloc_init("idmap_ldap");
+ if (!ldap_state.mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* assume location is the only parameter */
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(ldap_state.mem_ctx, params,
+ &ldap_state.smbldap_state))) {
+ talloc_destroy(ldap_state.mem_ctx);
+ return nt_status;
+ }
+
+ /* see if the idmap suffix and sub entries exists */
+
+ nt_status = verify_idpool();
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ End the LDAP session
+*****************************************************************************/
+
+static NTSTATUS ldap_idmap_close(void)
+{
+
+ smbldap_free_struct(&(ldap_state).smbldap_state);
+ talloc_destroy(ldap_state.mem_ctx);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+ return NT_STATUS_OK;
+}
+
+
+/* This function doesn't make as much sense in an LDAP world since the calling
+ node doesn't really control the ID ranges */
+static void ldap_idmap_status(void)
+{
+ DEBUG(0, ("LDAP IDMAP Status not available\n"));
+}
+
+static struct idmap_methods ldap_methods = {
+ ldap_idmap_init,
+ ldap_allocate_rid,
+ ldap_allocate_id,
+ ldap_get_sid_from_id,
+ ldap_get_id_from_sid,
+ ldap_set_mapping,
+ ldap_idmap_close,
+ ldap_idmap_status
+
+};
+
+NTSTATUS idmap_ldap_init(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", &ldap_methods);
+}
diff --git a/source/sam/idmap_tdb.c b/source/sam/idmap_tdb.c
new file mode 100644
index 00000000000..8ab8ec84770
--- /dev/null
+++ b/source/sam/idmap_tdb.c
@@ -0,0 +1,676 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap TDB backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+/* Globals */
+static TDB_CONTEXT *idmap_tdb;
+
+static struct idmap_state {
+
+ /* User and group id pool */
+
+ uid_t uid_low, uid_high; /* Range of uids to allocate */
+ gid_t gid_low, gid_high; /* Range of gids to allocate */
+} idmap_state;
+
+/**********************************************************************
+ allocate a new RID; We don't care if is a user or group
+**********************************************************************/
+
+static NTSTATUS db_allocate_rid(uint32 *rid, int rid_type)
+{
+ uint32 lowrid, highrid;
+ uint32 tmp_rid;
+
+ /* can't handle group rids right now. This is such a mess.... */
+
+ if ( rid_type == GROUP_RID_TYPE )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* cannot fail since idmap is only called winbindd */
+
+ get_free_rid_range( &lowrid, &highrid );
+
+ tmp_rid = lowrid;
+
+ if ( !tdb_change_uint32_atomic(idmap_tdb, "RID_COUNTER", &tmp_rid, RID_MULTIPLIER) ) {
+ DEBUG(3,("db_allocate_rid: Failed to locate next rid record in idmap db\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ( tmp_rid > highrid ) {
+ DEBUG(0, ("db_allocate_rid: no RIDs available!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *rid = tmp_rid;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Allocate either a user or group id from the pool
+**********************************************************************/
+
+static NTSTATUS db_allocate_id(unid_t *id, int id_type)
+{
+ BOOL ret;
+ int hwm;
+
+ if (!id)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Get current high water mark */
+ switch (id_type & ID_TYPEMASK) {
+ case ID_USERID:
+
+ if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* check it is in the range */
+ if (hwm > idmap_state.uid_high) {
+ DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.uid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch a new id and increment it */
+ ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, (unsigned int *)&hwm, 1);
+ if (!ret) {
+ DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > idmap_state.uid_high) {
+ DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.uid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*id).uid = hwm;
+ DEBUG(10,("db_allocate_id: ID_USERID (*id).uid = %d\n", (unsigned int)hwm));
+
+ break;
+ case ID_GROUPID:
+ if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* check it is in the range */
+ if (hwm > idmap_state.gid_high) {
+ DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.gid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch a new id and increment it */
+ ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, (unsigned int *)&hwm, 1);
+
+ if (!ret) {
+ DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > idmap_state.gid_high) {
+ DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.gid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*id).gid = hwm;
+ DEBUG(10,("db_allocate_id: ID_GROUPID (*id).gid = %d\n", (unsigned int)hwm));
+
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Get a sid from an id */
+static NTSTATUS internal_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ TDB_DATA key, data;
+ fstring keystr;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ switch (id_type & ID_TYPEMASK) {
+ case ID_USERID:
+ slprintf(keystr, sizeof(keystr), "UID %lu", (unsigned long)id.uid);
+ break;
+ case ID_GROUPID:
+ slprintf(keystr, sizeof(keystr), "GID %lu", (unsigned long)id.gid);
+ break;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ DEBUG(10,("internal_get_sid_from_id: fetching record %s\n", keystr ));
+
+ data = tdb_fetch(idmap_tdb, key);
+
+ if (data.dptr) {
+ if (string_to_sid(sid, data.dptr)) {
+ DEBUG(10,("internal_get_sid_from_id: fetching record %s -> %s\n", keystr, data.dptr ));
+ ret = NT_STATUS_OK;
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ return ret;
+}
+
+/* Error codes for get_id_from_sid */
+enum getidfromsiderr { GET_ID_FROM_SID_OK = 0, GET_ID_FROM_SID_NOTFOUND, GET_ID_FROM_SID_WRONG_TYPE, GET_ID_FROM_SID_ERR };
+
+static enum getidfromsiderr internal_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ enum getidfromsiderr ret = GET_ID_FROM_SID_ERR;
+ fstring keystr;
+ TDB_DATA key, data;
+ int type = *id_type & ID_TYPEMASK;
+
+ /* Check if sid is present in database */
+ sid_to_string(keystr, sid);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ DEBUG(10,("internal_get_id_from_sid: fetching record %s of type 0x%x\n", keystr, type ));
+
+ data = tdb_fetch(idmap_tdb, key);
+ if (!data.dptr) {
+ DEBUG(10,("internal_get_id_from_sid: record %s not found\n", keystr ));
+ return GET_ID_FROM_SID_NOTFOUND;
+ } else {
+ DEBUG(10,("internal_get_id_from_sid: record %s -> %s\n", keystr, data.dptr ));
+ }
+
+ if (type == ID_EMPTY || type == ID_USERID) {
+ fstring scanstr;
+ /* Parse and return existing uid */
+ fstrcpy(scanstr, "UID %d");
+
+ if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
+ /* uid ok? */
+ if (type == ID_EMPTY) {
+ *id_type = ID_USERID;
+ }
+ DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
+ (type == ID_EMPTY) ? "ID_EMPTY" : "ID_USERID",
+ keystr, data.dptr ));
+ ret = GET_ID_FROM_SID_OK;
+ } else {
+ ret = GET_ID_FROM_SID_WRONG_TYPE;
+ }
+ }
+
+ if ((ret != GET_ID_FROM_SID_OK) && (type == ID_EMPTY || type == ID_GROUPID)) {
+ fstring scanstr;
+ /* Parse and return existing gid */
+ fstrcpy(scanstr, "GID %d");
+
+ if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
+ /* gid ok? */
+ if (type == ID_EMPTY) {
+ *id_type = ID_GROUPID;
+ }
+ DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
+ (type == ID_EMPTY) ? "ID_EMPTY" : "ID_GROUPID",
+ keystr, data.dptr ));
+ ret = GET_ID_FROM_SID_OK;
+ } else {
+ ret = GET_ID_FROM_SID_WRONG_TYPE;
+ }
+ }
+
+ SAFE_FREE(data.dptr);
+
+ return ret;
+}
+
+/* Get a sid from an id */
+static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type_in)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ enum getidfromsiderr iderr;
+ int id_type = id_type_in & ID_TYPEMASK;
+ unid_t id_tmp = id;
+ int id_type_tmp = id_type;
+
+ DEBUG(10,("db_get_sid_from_id: id_type_in = 0x%x\n", id_type_in));
+
+ ret = internal_get_sid_from_id(sid, id, id_type);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+
+ iderr = internal_get_id_from_sid(&id_tmp, &id_type_tmp, sid);
+ if (iderr != GET_ID_FROM_SID_OK) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (id_type_tmp != id_type) {
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (id_type == ID_USERID) {
+ if (id_tmp.uid != id.uid) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (id_type == ID_GROUPID) {
+ if (id_tmp.gid != id.gid) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return ret;
+}
+/* Get an id from a sid */
+static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ enum getidfromsiderr iderr;
+
+ DEBUG(10,("db_get_id_from_sid\n"));
+
+ if (!sid || !id || !id_type)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ iderr = internal_get_id_from_sid(id, id_type, sid);
+ if (iderr == GET_ID_FROM_SID_OK) {
+ DOM_SID sid_tmp;
+ ret = internal_get_sid_from_id(&sid_tmp, *id, *id_type);
+ if (NT_STATUS_IS_OK(ret)) {
+ if (!sid_equal(&sid_tmp, sid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ } else if (iderr == GET_ID_FROM_SID_WRONG_TYPE) {
+ /* We found a record but not the type we wanted.
+ * This is an error, not an opportunity to overwrite...
+ * JRA.
+ */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(*id_type & ID_QUERY_ONLY) && (iderr != GET_ID_FROM_SID_OK) &&
+ (((*id_type & ID_TYPEMASK) == ID_USERID)
+ || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
+ TDB_DATA sid_data;
+ TDB_DATA ugid_data;
+ fstring sid_string;
+
+ sid_to_string(sid_string, sid);
+
+ sid_data.dptr = sid_string;
+ sid_data.dsize = strlen(sid_string)+1;
+
+ /* Lock the record for this SID. */
+ if (tdb_chainlock(idmap_tdb, sid_data) != 0) {
+ DEBUG(10,("db_get_id_from_sid: failed to lock record %s. Error %s\n",
+ sid_string, tdb_errorstr(idmap_tdb) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ do {
+ fstring ugid_str;
+
+ /* Allocate a new id for this sid */
+ ret = db_allocate_id(id, *id_type);
+ if (!NT_STATUS_IS_OK(ret))
+ break;
+
+ /* Store the UID side */
+ /* Store new id */
+ if (*id_type & ID_USERID) {
+ slprintf(ugid_str, sizeof(ugid_str), "UID %lu",
+ (unsigned long)((*id).uid));
+ } else {
+ slprintf(ugid_str, sizeof(ugid_str), "GID %lu",
+ (unsigned long)((*id).gid));
+ }
+
+ ugid_data.dptr = ugid_str;
+ ugid_data.dsize = strlen(ugid_str) + 1;
+
+ DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
+ ugid_data.dptr, sid_data.dptr ));
+
+ if (tdb_store(idmap_tdb, ugid_data, sid_data, TDB_INSERT) != -1) {
+ ret = NT_STATUS_OK;
+ break;
+ }
+ if (tdb_error(idmap_tdb) != TDB_ERR_EXISTS)
+ DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ } while (tdb_error(idmap_tdb) == TDB_ERR_EXISTS);
+
+ if (NT_STATUS_IS_OK(ret)) {
+
+ DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
+ sid_data.dptr, ugid_data.dptr ));
+
+ if (tdb_store(idmap_tdb, sid_data, ugid_data, TDB_REPLACE) == -1) {
+ DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
+ /* TODO: print tdb error !! */
+ tdb_chainunlock(idmap_tdb, sid_data);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ tdb_chainunlock(idmap_tdb, sid_data);
+ }
+
+ return ret;
+}
+
+static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ TDB_DATA ksid, kid, data;
+ fstring ksidstr;
+ fstring kidstr;
+
+ DEBUG(10,("db_set_mapping: id_type = 0x%x\n", id_type));
+
+ if (!sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ sid_to_string(ksidstr, sid);
+
+ ksid.dptr = ksidstr;
+ ksid.dsize = strlen(ksidstr) + 1;
+
+ if (id_type & ID_USERID) {
+ slprintf(kidstr, sizeof(kidstr), "UID %lu", (unsigned long)id.uid);
+ } else if (id_type & ID_GROUPID) {
+ slprintf(kidstr, sizeof(kidstr), "GID %lu", (unsigned long)id.gid);
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ kid.dptr = kidstr;
+ kid.dsize = strlen(kidstr) + 1;
+
+ /* *DELETE* prevoius mappings if any.
+ * This is done both SID and [U|G]ID passed in */
+
+ /* Lock the record for this SID. */
+ if (tdb_chainlock(idmap_tdb, ksid) != 0) {
+ DEBUG(10,("db_set_mapping: failed to lock record %s. Error %s\n",
+ ksidstr, tdb_errorstr(idmap_tdb) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("db_set_mapping: fetching %s\n", ksid.dptr));
+
+ data = tdb_fetch(idmap_tdb, ksid);
+ if (data.dptr) {
+ DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, ksid.dptr ));
+ tdb_delete(idmap_tdb, data);
+ tdb_delete(idmap_tdb, ksid);
+ SAFE_FREE(data.dptr);
+ }
+ data = tdb_fetch(idmap_tdb, kid);
+ if (data.dptr) {
+ DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, kid.dptr ));
+ tdb_delete(idmap_tdb, data);
+ tdb_delete(idmap_tdb, kid);
+ SAFE_FREE(data.dptr);
+ }
+
+ if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
+ DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb)));
+ tdb_chainunlock(idmap_tdb, ksid);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
+ DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb)));
+ tdb_chainunlock(idmap_tdb, ksid);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tdb_chainunlock(idmap_tdb, ksid);
+ DEBUG(10,("db_set_mapping: stored %s -> %s and %s -> %s\n", ksid.dptr, kid.dptr, kid.dptr, ksid.dptr ));
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+
+static NTSTATUS db_idmap_init( char *params )
+{
+ SMB_STRUCT_STAT stbuf;
+ char *tdbfile = NULL;
+ int32 version;
+ BOOL tdb_is_new = False;
+
+ /* use the old database if present */
+ tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
+ if (!tdbfile) {
+ DEBUG(0, ("idmap_init: out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!file_exist(tdbfile, &stbuf)) {
+ tdb_is_new = True;
+ }
+
+ DEBUG(10,("db_idmap_init: Opening tdbfile %s\n", tdbfile ));
+
+ /* Open idmap repository */
+ if (!(idmap_tdb = tdb_open_log(tdbfile, 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT,
+ 0644))) {
+ DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
+ SAFE_FREE(tdbfile);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(tdbfile);
+
+ if (tdb_is_new) {
+ /* the file didn't existed before opening it, let's
+ * store idmap version as nobody else yet opened and
+ * stored it. I do not like this method but didn't
+ * found a way to understand if an opened tdb have
+ * been just created or not --- SSS */
+ tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION);
+ }
+
+ /* check against earlier versions */
+ version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+ if (version != IDMAP_VERSION) {
+ DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* Create high water marks for group and user id */
+ if (!lp_idmap_uid(&idmap_state.uid_low, &idmap_state.uid_high)) {
+ DEBUG(1, ("idmap uid range missing or invalid\n"));
+ DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+ } else {
+ if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
+ if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
+ DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ if (!lp_idmap_gid(&idmap_state.gid_low, &idmap_state.gid_high)) {
+ DEBUG(1, ("idmap gid range missing or invalid\n"));
+ DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+ } else {
+ if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
+ if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
+ DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Close the tdb */
+static NTSTATUS db_idmap_close(void)
+{
+ if (idmap_tdb) {
+ if (tdb_close(idmap_tdb) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+/* Dump status information to log file. Display different stuff based on
+ the debug level:
+
+ Debug Level Information Displayed
+ =================================================================
+ 0 Percentage of [ug]id range allocated
+ 0 High water marks (next allocated ids)
+*/
+
+#define DUMP_INFO 0
+
+static void db_idmap_status(void)
+{
+ int user_hwm, group_hwm;
+
+ DEBUG(0, ("winbindd idmap status:\n"));
+
+ /* Get current high water marks */
+
+ if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get userid high water mark!\n"));
+ }
+
+ if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get groupid high water mark!\n"));
+ }
+
+ /* Display next ids to allocate */
+
+ if (user_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext userid to allocate is %d\n", user_hwm));
+ }
+
+ if (group_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext groupid to allocate is %d\n", group_hwm));
+ }
+
+ /* Display percentage of id range already allocated. */
+
+ if (user_hwm != -1) {
+ int num_users = user_hwm - idmap_state.uid_low;
+ int total_users =
+ idmap_state.uid_high - idmap_state.uid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tUser id range is %d%% full (%d of %d)\n",
+ num_users * 100 / total_users, num_users,
+ total_users));
+ }
+
+ if (group_hwm != -1) {
+ int num_groups = group_hwm - idmap_state.gid_low;
+ int total_groups =
+ idmap_state.gid_high - idmap_state.gid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tGroup id range is %d%% full (%d of %d)\n",
+ num_groups * 100 / total_groups, num_groups,
+ total_groups));
+ }
+
+ /* Display complete mapping of users and groups to rids */
+}
+
+/**********************************************************************
+ Return the TDB_CONTEXT* for winbindd_idmap. I **really** feel
+ dirty doing this, but not so dirty that I want to create another
+ tdb
+***********************************************************************/
+
+TDB_CONTEXT *idmap_tdb_handle( void )
+{
+ if ( idmap_tdb )
+ return idmap_tdb;
+
+ /* go ahead an open it; db_idmap_init() doesn't use any params
+ right now */
+
+ db_idmap_init( NULL );
+ if ( idmap_tdb )
+ return idmap_tdb;
+
+ return NULL;
+}
+
+static struct idmap_methods db_methods = {
+
+ db_idmap_init,
+ db_allocate_rid,
+ db_allocate_id,
+ db_get_sid_from_id,
+ db_get_id_from_sid,
+ db_set_mapping,
+ db_idmap_close,
+ db_idmap_status
+
+};
+
+NTSTATUS idmap_tdb_init(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
+}
diff --git a/source/sam/idmap_util.c b/source/sam/idmap_util.c
new file mode 100644
index 00000000000..f28e11cde74
--- /dev/null
+++ b/source/sam/idmap_util.c
@@ -0,0 +1,295 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Simo Sorce 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#if 0 /* NOT USED */
+
+/**********************************************************************
+ Get the free RID base if idmap is configured, otherwise return 0
+**********************************************************************/
+
+uint32 idmap_get_free_rid_base(void)
+{
+ uint32 low, high;
+ if (idmap_get_free_rid_range(&low, &high)) {
+ return low;
+ }
+ return 0;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_ugid_is_in_free_range(uint32 id)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_ugid_range(&low, &high)) {
+ return False;
+ }
+ if (id < low || id > high) {
+ return False;
+ }
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_rid_is_in_free_range(uint32 rid)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_rid_range(&low, &high)) {
+ return False;
+ }
+ if (rid < algorithmic_rid_base()) {
+ return True;
+ }
+
+ if (rid < low || rid > high) {
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ if it is a foreign SID or if the SID is in the free range, return true
+**********************************************************************/
+
+BOOL idmap_check_sid_is_in_free_range(const DOM_SID *sid)
+{
+ if (sid_compare_domain(get_global_sam_sid(), sid) == 0) {
+
+ uint32 rid;
+
+ if (sid_peek_rid(sid, &rid)) {
+ return idmap_check_rid_is_in_free_range(rid);
+ }
+
+ return False;
+ }
+
+ return True;
+}
+
+#endif /* NOT USED */
+
+/*****************************************************************
+ Returns SID pointer.
+*****************************************************************/
+
+NTSTATUS idmap_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+ unid_t id;
+ int flags;
+
+ DEBUG(10,("idmap_uid_to_sid: uid = [%lu]\n", (unsigned long)uid));
+
+ flags = ID_USERID;
+ id.uid = uid;
+
+ return idmap_get_sid_from_id(sid, id, flags);
+}
+
+/*****************************************************************
+ Group mapping is used for gids that maps to Wellknown SIDs
+ Returns SID pointer.
+*****************************************************************/
+
+NTSTATUS idmap_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+ unid_t id;
+ int flags;
+
+ DEBUG(10,("idmap_gid_to_sid: gid = [%lu]\n", (unsigned long)gid));
+
+ flags = ID_GROUPID;
+#if 0 /* JERRY */
+ if (!idmap_check_ugid_is_in_free_range(gid)) {
+ flags |= ID_QUERY_ONLY;
+ }
+#endif
+ id.gid = gid;
+ return idmap_get_sid_from_id(sid, id, flags);
+}
+
+/*****************************************************************
+ if it is a foreign sid or it is in idmap rid range check idmap,
+ otherwise falls back to the legacy algorithmic mapping.
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+NTSTATUS idmap_sid_to_uid(const DOM_SID *sid, uid_t *uid, uint32 flags)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ unid_t id;
+
+ DEBUG(10,("idmap_sid_to_uid: sid = [%s]\n", sid_string_static(sid)));
+
+ flags |= ID_USERID;
+
+ ret = idmap_get_id_from_sid(&id, (int *)&flags, sid);
+
+ if ( NT_STATUS_IS_OK(ret) ) {
+ DEBUG(10,("idmap_sid_to_uid: uid = [%lu]\n", (unsigned long)id.uid));
+ *uid = id.uid;
+ }
+
+ return ret;
+
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ if it is a foreign sid or it is in idmap rid range check idmap,
+ otherwise falls back to the legacy algorithmic mapping.
+ Group mapping is used for gids that maps to Wellknown SIDs
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+NTSTATUS idmap_sid_to_gid(const DOM_SID *sid, gid_t *gid, uint32 flags)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ unid_t id;
+
+ DEBUG(10,("sid_to_gid: sid = [%s]\n", sid_string_static(sid)));
+
+ flags |= ID_GROUPID;
+
+ ret = idmap_get_id_from_sid(&id, (int *)&flags, sid);
+
+ if ( NT_STATUS_IS_OK(ret) )
+ {
+ DEBUG(10,("idmap_sid_to_gid: gid = [%lu]\n", (unsigned long)id.gid));
+ *gid = id.gid;
+ }
+
+ return ret;
+}
+
+
+/***************************************************************************
+ Check first, call set_mapping if it doesn't already exist.
+***************************************************************************/
+
+static NTSTATUS wellknown_id_init(DOM_SID *sid, unid_t id, int flags)
+{
+ unid_t storedid;
+ int qflags = flags | ID_QUERY_ONLY;
+
+ if (!NT_STATUS_IS_OK(idmap_get_id_from_sid(&storedid, &qflags, sid))) {
+ return idmap_set_mapping(sid, id, flags);
+ } else {
+ if (flags == ID_USERID && id.uid != storedid.uid) {
+ DEBUG(0,("wellknown_id_init: WARNING ! Stored uid %u for SID %s is not the same as the requested uid %u\n",
+ (unsigned int)storedid.uid, sid_string_static(sid), (unsigned int)id.uid ));
+ DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n"));
+ return idmap_set_mapping(sid, id, flags);
+ } else if (flags == ID_GROUPID && id.gid != storedid.gid) {
+ DEBUG(0,("wellknown_id_init: WARNING ! Stored gid %u for SID %s is not the same as the requested gid %u\n",
+ (unsigned int)storedid.gid, sid_string_static(sid), (unsigned int)id.gid ));
+ DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n"));
+ return idmap_set_mapping(sid, id, flags);
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Initialize idmap withWellknown SIDs like Guest, that are necessary
+ to make samba run properly.
+***************************************************************************/
+
+BOOL idmap_init_wellknown_sids(void)
+{
+ const char *guest_account = lp_guestaccount();
+ struct passwd *pass;
+ GROUP_MAP *map=NULL;
+ int num_entries=0;
+ DOM_SID sid;
+ unid_t id;
+ fstring sid_string;
+
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return False;
+ }
+
+ pass = getpwnam_alloc(guest_account);
+ if (!pass) {
+ return False;
+ }
+
+ /* Fill in the SID for the guest account. */
+ id.uid = pass->pw_uid;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_USER_RID_GUEST);
+
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) {
+ DEBUG(0, ("Failed to setup UID mapping for GUEST (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.uid));
+ passwd_free(&pass);
+ return False;
+ }
+
+ /* check if DOMAIN_GROUP_RID_GUESTS SID is set, if not store the
+ * guest account gid as mapping */
+ id.gid = pass->pw_gid;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_GROUP_RID_GUESTS);
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_GROUPID))) {
+ DEBUG(0, ("Failed to setup GID mapping for Group DOMAIN GUESTS (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.gid));
+ passwd_free(&pass);
+ return False;
+ }
+
+ passwd_free(&pass);
+ /* now fill in group mappings */
+ if(pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED)) {
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ id.gid = map[i].gid;
+ wellknown_id_init(&map[i].sid, id, ID_GROUPID);
+ }
+ SAFE_FREE(map);
+ }
+
+ /* Fill in the SID for the administrator account. */
+ id.uid = 0;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_USER_RID_ADMIN);
+
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) {
+ DEBUG(0, ("Failed to setup UID mapping for ADMINISTRATOR (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.uid));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/sam/interface.c b/source/sam/interface.c
new file mode 100644
index 00000000000..51ae561999c
--- /dev/null
+++ b/source/sam/interface.c
@@ -0,0 +1,1338 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Kai Krüger 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+extern DOM_SID global_sid_Builtin;
+
+/** List of various built-in sam modules */
+
+const struct sam_init_function_entry builtin_sam_init_functions[] = {
+ { "plugin", sam_init_plugin },
+#ifdef HAVE_LDAP
+ { "ads", sam_init_ads },
+#endif
+ { "skel", sam_init_skel },
+ { NULL, NULL}
+};
+
+
+static NTSTATUS sam_get_methods_by_sid(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const DOM_SID *domainsid)
+{
+ SAM_METHODS *tmp_methods;
+
+ DEBUG(5,("sam_get_methods_by_sid: %d\n", __LINE__));
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(context && context->methods);
+
+ tmp_methods = context->methods;
+
+ while (tmp_methods) {
+ if (sid_equal(domainsid, &(tmp_methods->domain_sid)))
+ {
+ (*sam_method) = tmp_methods;
+ return NT_STATUS_OK;
+ }
+ tmp_methods = tmp_methods->next;
+ }
+
+ DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", sid_string_static(domainsid)));
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS sam_get_methods_by_name(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const char *domainname)
+{
+ SAM_METHODS *tmp_methods;
+
+ DEBUG(5,("sam_get_methods_by_name: %d\n", __LINE__));
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(context && context->methods);
+
+ tmp_methods = context->methods;
+
+ while (tmp_methods) {
+ if (strequal(domainname, tmp_methods->domain_name))
+ {
+ (*sam_method) = tmp_methods;
+ return NT_STATUS_OK;
+ }
+ tmp_methods = tmp_methods->next;
+ }
+
+ DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", domainname));
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS make_sam_methods(TALLOC_CTX *mem_ctx, SAM_METHODS **methods)
+{
+ *methods = talloc(mem_ctx, sizeof(SAM_METHODS));
+
+ if (!*methods) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*methods);
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Free and cleanup a sam context, any associated data and anything
+ that the attached modules might have associated.
+ *******************************************************************/
+
+void free_sam_context(SAM_CONTEXT **context)
+{
+ SAM_METHODS *sam_selected = (*context)->methods;
+
+ while (sam_selected) {
+ if (sam_selected->free_private_data) {
+ sam_selected->free_private_data(&(sam_selected->private_data));
+ }
+ sam_selected = sam_selected->next;
+ }
+
+ talloc_destroy((*context)->mem_ctx);
+ *context = NULL;
+}
+
+/******************************************************************
+ Make a backend_entry from scratch
+ *******************************************************************/
+
+static NTSTATUS make_backend_entry(SAM_BACKEND_ENTRY *backend_entry, char *sam_backend_string)
+{
+ char *tmp = NULL;
+ char *tmp_string = sam_backend_string;
+
+ DEBUG(5,("make_backend_entry: %d\n", __LINE__));
+
+ SAM_ASSERT(sam_backend_string && backend_entry);
+
+ backend_entry->module_name = sam_backend_string;
+
+ DEBUG(5,("makeing backend_entry for %s\n", backend_entry->module_name));
+
+ if ((tmp = strrchr(tmp_string, '|')) != NULL) {
+ DEBUGADD(20,("a domain name has been specified\n"));
+ *tmp = 0;
+ backend_entry->domain_name = smb_xstrdup(tmp + 1);
+ tmp_string = tmp + 1;
+ }
+
+ if ((tmp = strchr(tmp_string, ':')) != NULL) {
+ DEBUG(20,("options for the backend have been specified\n"));
+ *tmp = 0;
+ backend_entry->module_params = smb_xstrdup(tmp + 1);
+ tmp_string = tmp + 1;
+ }
+
+ if (backend_entry->domain_name == NULL) {
+ DEBUG(10,("make_backend_entry: no domain was specified for sam module %s. Using default domain %s\n",
+ backend_entry->module_name, lp_workgroup()));
+ backend_entry->domain_name = smb_xstrdup(lp_workgroup());
+ }
+
+ if ((backend_entry->domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID))) == NULL) {
+ DEBUG(0,("make_backend_entry: failed to malloc domain_sid\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("looking up sid for domain %s\n", backend_entry->domain_name));
+
+ if (!secrets_fetch_domain_sid(backend_entry->domain_name, backend_entry->domain_sid)) {
+ DEBUG(2,("make_backend_entry: There is no SID stored for domain %s. Creating a new one.\n",
+ backend_entry->domain_name));
+ DEBUG(0, ("FIXME in %s:%d\n", __FILE__, __LINE__));
+ ZERO_STRUCTP(backend_entry->domain_sid);
+ }
+
+ DEBUG(5,("make_backend_entry: module name: %s, module parameters: %s, domain name: %s, domain sid: %s\n",
+ backend_entry->module_name, backend_entry->module_params, backend_entry->domain_name, sid_string_static(backend_entry->domain_sid)));
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ create sam_methods struct based on sam_backend_entry
+ *****************************************************************/
+
+static NTSTATUS make_sam_methods_backend_entry(SAM_CONTEXT *context, SAM_METHODS **methods_ptr, SAM_BACKEND_ENTRY *backend_entry)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ SAM_METHODS *methods;
+ int i;
+
+ DEBUG(5,("make_sam_methods_backend_entry: %d\n", __LINE__));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_methods(context->mem_ctx, methods_ptr))) {
+ return nt_status;
+ }
+
+ methods = *methods_ptr;
+ methods->backendname = talloc_strdup(context->mem_ctx, backend_entry->module_name);
+ methods->domain_name = talloc_strdup(context->mem_ctx, backend_entry->domain_name);
+ sid_copy(&methods->domain_sid, backend_entry->domain_sid);
+ methods->parent = context;
+
+ DEBUG(5,("Attempting to find sam backend %s\n", backend_entry->module_name));
+ for (i = 0; builtin_sam_init_functions[i].module_name; i++)
+ {
+ if (strequal(builtin_sam_init_functions[i].module_name, backend_entry->module_name))
+ {
+ DEBUG(5,("Found sam backend %s (at pos %d)\n", backend_entry->module_name, i));
+ DEBUGADD(5,("initialising it with options=%s for domain %s\n", backend_entry->module_params, sid_string_static(backend_entry->domain_sid)));
+ nt_status = builtin_sam_init_functions[i].init(methods, backend_entry->module_params);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5,("sam backend %s has a valid init\n", backend_entry->module_name));
+ } else {
+ DEBUG(2,("sam backend %s did not correctly init (error was %s)\n",
+ backend_entry->module_name, nt_errstr(nt_status)));
+ }
+ return nt_status;
+ }
+ }
+
+ DEBUG(2,("could not find backend %s\n", backend_entry->module_name));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS sam_context_check_default_backends(SAM_CONTEXT *context)
+{
+ SAM_BACKEND_ENTRY entry;
+ DOM_SID *global_sam_sid = get_global_sam_sid(); /* lp_workgroup doesn't play nicely with multiple domains */
+ SAM_METHODS *methods, *tmpmethods;
+ NTSTATUS ntstatus;
+
+ DEBUG(5,("sam_context_check_default_backends: %d\n", __LINE__));
+
+ /* Make sure domain lp_workgroup() is available */
+
+ ntstatus = sam_get_methods_by_sid(context, &methods, &global_sid_Builtin);
+
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+ DEBUG(4,("There was no backend specified for domain %s(%s); using %s\n",
+ lp_workgroup(), sid_string_static(global_sam_sid), SAM_DEFAULT_BACKEND));
+
+ SAM_ASSERT(global_sam_sid);
+
+ entry.module_name = SAM_DEFAULT_BACKEND;
+ entry.module_params = NULL;
+ entry.domain_name = lp_workgroup();
+ entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy(entry.domain_sid, global_sam_sid);
+
+ if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ return ntstatus;
+ }
+
+ DLIST_ADD_END(context->methods, methods, tmpmethods);
+
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ DEBUG(2, ("sam_get_methods_by_sid failed for %s\n", lp_workgroup()));
+ return ntstatus;
+ }
+
+ /* Make sure the BUILTIN domain is available */
+
+ ntstatus = sam_get_methods_by_sid(context, &methods, global_sam_sid);
+
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+ DEBUG(4,("There was no backend specified for domain BUILTIN; using %s\n",
+ SAM_DEFAULT_BACKEND));
+ entry.module_name = SAM_DEFAULT_BACKEND;
+ entry.module_params = NULL;
+ entry.domain_name = "BUILTIN";
+ entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy(entry.domain_sid, &global_sid_Builtin);
+
+ if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ return ntstatus;
+ }
+
+ DLIST_ADD_END(context->methods, methods, tmpmethods);
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ DEBUG(2, ("sam_get_methods_by_sid failed for BUILTIN\n"));
+ return ntstatus;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS check_duplicate_backend_entries(SAM_BACKEND_ENTRY **backend_entries, int *nBackends)
+{
+ int i, j;
+
+ DEBUG(5,("check_duplicate_backend_entries: %d\n", __LINE__));
+
+ for (i = 0; i < *nBackends; i++) {
+ for (j = i + 1; j < *nBackends; j++) {
+ if (sid_equal((*backend_entries)[i].domain_sid, (*backend_entries)[j].domain_sid)) {
+ DEBUG(0,("two backend modules claim the same domain %s\n",
+ sid_string_static((*backend_entries)[j].domain_sid)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS make_sam_context_list(SAM_CONTEXT **context, char **sam_backends_param)
+{
+ int i = 0, j = 0;
+ SAM_METHODS *curmethods, *tmpmethods;
+ int nBackends = 0;
+ SAM_BACKEND_ENTRY *backends = NULL;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(5,("make_sam_context_from_conf: %d\n", __LINE__));
+
+ if (!sam_backends_param) {
+ DEBUG(1, ("no SAM backeds specified!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_context(context))) {
+ DEBUG(4,("make_sam_context failed\n"));
+ return nt_status;
+ }
+
+ while (sam_backends_param[nBackends])
+ nBackends++;
+
+ DEBUG(6,("There are %d domains listed with their backends\n", nBackends));
+
+ if ((backends = (SAM_BACKEND_ENTRY *)malloc(sizeof(*backends)*nBackends)) == NULL) {
+ DEBUG(0,("make_sam_context_list: failed to allocate backends\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memset(backends, '\0', sizeof(*backends)*nBackends);
+
+ for (i = 0; i < nBackends; i++) {
+ DEBUG(8,("processing %s\n",sam_backends_param[i]));
+ if (!NT_STATUS_IS_OK(nt_status = make_backend_entry(&backends[i], sam_backends_param[i]))) {
+ DEBUG(4,("make_backend_entry failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = check_duplicate_backend_entries(&backends, &nBackends))) {
+ DEBUG(4,("check_duplicate_backend_entries failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+
+ for (i = 0; i < nBackends; i++) {
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_methods_backend_entry(*context, &curmethods, &backends[i]))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+ DLIST_ADD_END((*context)->methods, curmethods, tmpmethods);
+ }
+
+ for (i = 0; i < nBackends; i++) SAFE_FREE(backends[i].domain_sid);
+
+ SAFE_FREE(backends);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Make a sam_context from scratch.
+ *******************************************************************/
+
+NTSTATUS make_sam_context(SAM_CONTEXT **context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("sam_context internal allocation context");
+
+ if (!mem_ctx) {
+ DEBUG(0, ("make_sam_context: talloc init failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *context = talloc(mem_ctx, sizeof(**context));
+ if (!*context) {
+ DEBUG(0, ("make_sam_context: talloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*context);
+
+ (*context)->mem_ctx = mem_ctx;
+
+ (*context)->free_fn = free_sam_context;
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Return an already initialised sam_context, to facilitate backward
+ compatibility (see functions below).
+ *******************************************************************/
+
+static struct sam_context *sam_get_static_context(BOOL reload)
+{
+ static SAM_CONTEXT *sam_context = NULL;
+
+ if ((sam_context) && (reload)) {
+ sam_context->free_fn(&sam_context);
+ sam_context = NULL;
+ }
+
+ if (!sam_context) {
+ if (!NT_STATUS_IS_OK(make_sam_context_list(&sam_context, lp_sam_backend()))) {
+ DEBUG(4,("make_sam_context_list failed\n"));
+ return NULL;
+ }
+
+ /* Make sure the required domains (default domain, builtin) are available */
+ if (!NT_STATUS_IS_OK(sam_context_check_default_backends(sam_context))) {
+ DEBUG(4,("sam_context_check_default_backends failed\n"));
+ return NULL;
+ }
+ }
+
+ return sam_context;
+}
+
+/***************************************************************
+ Initialize the static context (at smbd startup etc).
+
+ If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_sam(BOOL reload)
+{
+ return (sam_get_static_context(reload) != NULL);
+}
+
+
+/**************************************************************
+ External API. This is what the rest of the world calls...
+***************************************************************/
+
+/******************************************************************
+ sam_* functions are used to link the external SAM interface
+ with the internal backends. These functions lookup the appropriate
+ backends for the domain and pass on to the function in sam_methods
+ in the selected backend
+
+ When the context parmater is NULL, the default is used.
+ *******************************************************************/
+
+#define SAM_SETUP_CONTEXT if (!context) \
+ context = sam_get_static_context(False);\
+ if (!context) {\
+ return NT_STATUS_UNSUCCESSFUL; \
+ }\
+
+
+
+NTSTATUS sam_get_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_sec_desc: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_sec_desc) {
+ DEBUG(3, ("sam_get_sec_desc: sam_methods of the domain did not specify sam_get_sec_desc\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_sec_desc(tmp_methods, access_token, sid, sd))) {
+ DEBUG(4,("sam_get_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_set_sec_desc: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_set_sec_desc) {
+ DEBUG(3, ("sam_set_sec_desc: sam_methods of the domain did not specify sam_set_sec_desc\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_set_sec_desc(tmp_methods, access_token, sid, sd))) {
+ DEBUG(4,("sam_set_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_lookup_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, const char *name, DOM_SID *sid, uint32 *type)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_lookup_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_lookup_name) {
+ DEBUG(3, ("sam_lookup_name: sam_methods of the domain did not specify sam_lookup_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_name(tmp_methods, access_token, name, sid, type))) {
+ DEBUG(4,("sam_lookup_name for %s\\%s in backend %s failed\n",
+ tmp_methods->domain_name, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+ DOM_SID domainsid;
+
+ DEBUG(5,("sam_lookup_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ sid_copy(&domainsid, sid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_lookup_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_lookup_sid) {
+ DEBUG(3, ("sam_lookup_sid: sam_methods of the domain did not specify sam_lookup_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_sid(tmp_methods, access_token, mem_ctx, sid, name, type))) {
+ DEBUG(4,("sam_lookup_name for %s in backend %s failed\n",
+ sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_update_domain(const SAM_CONTEXT *context, const SAM_DOMAIN_HANDLE *domain)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_domain: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid domain specified */
+ SAM_ASSERT(domain && domain->current_sam_methods);
+
+ tmp_methods = domain->current_sam_methods;
+
+ if (!tmp_methods->sam_update_domain) {
+ DEBUG(3, ("sam_update_domain: sam_methods of the domain did not specify sam_update_domain\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_domain(tmp_methods, domain))){
+ DEBUG(4,("sam_update_domain in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_domains(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, int32 *domain_count, DOM_SID **domains, char ***domain_names)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SEC_DESC *sd;
+ size_t sd_size;
+ uint32 acc_granted;
+ int i = 0;
+
+ DEBUG(5,("sam_enum_domains: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters specified */
+ SAM_ASSERT(domain_count && domains && domain_names);
+
+ if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+ DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+ return nt_status;
+ }
+
+ if (!se_access_check(sd, access_token, SA_RIGHT_SAM_ENUM_DOMAINS, &acc_granted, &nt_status)) {
+ DEBUG(3,("sam_enum_domains: ACCESS DENIED\n"));
+ return nt_status;
+ }
+
+ tmp_methods= context->methods;
+ *domain_count = 0;
+
+ while (tmp_methods) {
+ (*domain_count)++;
+ tmp_methods= tmp_methods->next;
+ }
+
+ DEBUG(6,("sam_enum_domains: enumerating %d domains\n", (*domain_count)));
+
+ tmp_methods = context->methods;
+
+ if (((*domains) = malloc( sizeof(DOM_SID) * (*domain_count))) == NULL) {
+ DEBUG(0,("sam_enum_domains: Out of memory allocating domain SID list\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (((*domain_names) = malloc( sizeof(char*) * (*domain_count))) == NULL) {
+ DEBUG(0,("sam_enum_domains: Out of memory allocating domain name list\n"));
+ SAFE_FREE((*domains));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (tmp_methods) {
+ DEBUGADD(7,(" [%d] %s: %s\n", i, tmp_methods->domain_name, sid_string_static(&tmp_methods->domain_sid)));
+ sid_copy(domains[i],&tmp_methods->domain_sid);
+ *domain_names[i] = smb_xstrdup(tmp_methods->domain_name);
+ i++;
+ tmp_methods= tmp_methods->next;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_domain(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, DOM_SID **domainsid)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SEC_DESC *sd;
+ size_t sd_size;
+ uint32 acc_granted;
+
+ DEBUG(5,("sam_lookup_domain: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid paramters */
+ SAM_ASSERT(access_token && domain && domainsid);
+
+ if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+ DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+ return nt_status;
+ }
+
+ if (!se_access_check(sd, access_token, SA_RIGHT_SAM_OPEN_DOMAIN, &acc_granted, &nt_status)) {
+ DEBUG(3,("sam_lookup_domain: ACCESS DENIED\n"));
+ return nt_status;
+ }
+
+ tmp_methods= context->methods;
+
+ while (tmp_methods) {
+ if (strcmp(domain, tmp_methods->domain_name) == 0) {
+ (*domainsid) = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy((*domainsid), &tmp_methods->domain_sid);
+ return NT_STATUS_OK;
+ }
+ tmp_methods= tmp_methods->next;
+ }
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+
+NTSTATUS sam_get_domain_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_domain_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && domain);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_domain_handle) {
+ DEBUG(3, ("sam_get_domain_by_sid: sam_methods of the domain did not specify sam_get_domain_handle\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_domain_handle(tmp_methods, access_token, access_desired, domain))) {
+ DEBUG(4,("sam_get_domain_handle for %s in backend %s failed\n",
+ sid_string_static(domainsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_account(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_create_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters */
+ SAM_ASSERT(access_token && domainsid && account_name && account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_create_account) {
+ DEBUG(3, ("sam_create_account: sam_methods of the domain did not specify sam_create_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_account(tmp_methods, access_token, access_desired, account_name, acct_ctrl, account))) {
+ DEBUG(4,("sam_create_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ DOM_SID domainsid;
+ const DOM_SID *accountsid;
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_add_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters */
+ SAM_ASSERT(account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_account_sid(account, &accountsid))) {
+ DEBUG(0,("Can't get account SID\n"));
+ return nt_status;
+ }
+
+ sid_copy(&domainsid, accountsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_add_account) {
+ DEBUG(3, ("sam_add_account: sam_methods of the domain did not specify sam_add_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_account(tmp_methods, account))){
+ DEBUG(4,("sam_add_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid account specified */
+ SAM_ASSERT(account && account->current_sam_methods);
+
+ tmp_methods = account->current_sam_methods;
+
+ if (!tmp_methods->sam_update_account) {
+ DEBUG(3, ("sam_update_account: sam_methods of the domain did not specify sam_update_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_account(tmp_methods, account))){
+ DEBUG(4,("sam_update_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_delete_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid account specified */
+ SAM_ASSERT(account && account->current_sam_methods);
+
+ tmp_methods = account->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_account) {
+ DEBUG(3, ("sam_delete_account: sam_methods of the domain did not specify sam_delete_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_account(tmp_methods, account))){
+ DEBUG(4,("sam_delete_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_accounts(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 acct_ctrl, int32 *account_count, SAM_ACCOUNT_ENUM **accounts)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_enum_accounts: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && account_count && accounts);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_enum_accounts) {
+ DEBUG(3, ("sam_enum_accounts: sam_methods of the domain did not specify sam_enum_accounts\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_accounts(tmp_methods, access_token, acct_ctrl, account_count, accounts))) {
+ DEBUG(4,("sam_enum_accounts for domain %s in backend %s failed\n",
+ tmp_methods->domain_name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_get_account_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ DOM_SID domainsid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_account_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && accountsid && account);
+
+ sid_copy(&domainsid, accountsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_account_by_sid) {
+ DEBUG(3, ("sam_get_account_by_sid: sam_methods of the domain did not specify sam_get_account_by_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_sid(tmp_methods, access_token, access_desired, accountsid, account))) {
+ DEBUG(4,("sam_get_account_by_sid for %s in backend %s failed\n",
+ sid_string_static(accountsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_account_by_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domain && name && account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_account_by_name) {
+ DEBUG(3, ("sam_get_account_by_name: sam_methods of the domain did not specify sam_get_account_by_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_name(tmp_methods, access_token, access_desired, name, account))) {
+ DEBUG(4,("sam_get_account_by_name for %s\\%s in backend %s failed\n",
+ domain, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_group(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_create_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && group_name && group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_create_group) {
+ DEBUG(3, ("sam_create_group: sam_methods of the domain did not specify sam_create_group\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_group(tmp_methods, access_token, access_desired, group_name, group_ctrl, group))) {
+ DEBUG(4,("sam_create_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ DOM_SID domainsid;
+ const DOM_SID *groupsid;
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_add_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_group_sid(group, &groupsid))) {
+ DEBUG(0,("Can't get group SID\n"));
+ return nt_status;
+ }
+
+ sid_copy(&domainsid, groupsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_add_group) {
+ DEBUG(3, ("sam_add_group: sam_methods of the domain did not specify sam_add_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_group(tmp_methods, group))){
+ DEBUG(4,("sam_add_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_update_group) {
+ DEBUG(3, ("sam_update_group: sam_methods of the domain did not specify sam_update_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_group(tmp_methods, group))){
+ DEBUG(4,("sam_update_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_delete_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_group) {
+ DEBUG(3, ("sam_delete_group: sam_methods of the domain did not specify sam_delete_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_group(tmp_methods, group))){
+ DEBUG(4,("sam_delete_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groups(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_enum_groups: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && groups_count && groups);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_enum_accounts) {
+ DEBUG(3, ("sam_enum_groups: sam_methods of the domain did not specify sam_enum_groups\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groups(tmp_methods, access_token, group_ctrl, groups_count, groups))) {
+ DEBUG(4,("sam_enum_groups for domain %s in backend %s failed\n",
+ tmp_methods->domain_name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+ DOM_SID domainsid;
+
+ DEBUG(5,("sam_get_group_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && groupsid && group);
+
+ sid_copy(&domainsid, groupsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_group_by_sid) {
+ DEBUG(3, ("sam_get_group_by_sid: sam_methods of the domain did not specify sam_get_group_by_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_sid(tmp_methods, access_token, access_desired, groupsid, group))) {
+ DEBUG(4,("sam_get_group_by_sid for %s in backend %s failed\n",
+ sid_string_static(groupsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_group_by_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domain && name && group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_group_by_name) {
+ DEBUG(3, ("sam_get_group_by_name: sam_methods of the domain did not specify sam_get_group_by_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_name(tmp_methods, access_token, access_desired, name, group))) {
+ DEBUG(4,("sam_get_group_by_name for %s\\%s in backend %s failed\n",
+ domain, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_member_to_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group or member specified */
+ SAM_ASSERT(group && group->current_sam_methods && member);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_add_member_to_group) {
+ DEBUG(3, ("sam_add_member_to_group: sam_methods of the domain did not specify sam_add_member_to_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_member_to_group(tmp_methods, group, member))) {
+ DEBUG(4,("sam_add_member_to_group in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+NTSTATUS sam_delete_member_from_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group or member specified */
+ SAM_ASSERT(group && group->current_sam_methods && member);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_member_from_group) {
+ DEBUG(3, ("sam_delete_member_from_group: sam_methods of the domain did not specify sam_delete_member_from_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_member_from_group(tmp_methods, group, member))) {
+ DEBUG(4,("sam_delete_member_from_group in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groupmembers(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods && members_count && members);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_enum_groupmembers) {
+ DEBUG(3, ("sam_enum_groupmembers: sam_methods of the domain did not specify sam_enum_group_members\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groupmembers(tmp_methods, group, members_count, members))) {
+ DEBUG(4,("sam_enum_groupmembers in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_groups_of_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ uint32 tmp_group_count;
+ SAM_GROUP_ENUM *tmp_groups;
+
+ DEBUG(5,("sam_get_groups_of_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(access_token && sids && context && context->methods);
+
+ *group_count = 0;
+
+ *groups = NULL;
+
+ tmp_methods= context->methods;
+
+ while (tmp_methods) {
+ DEBUG(5,("getting groups from domain \n"));
+ if (!tmp_methods->sam_get_groups_of_sid) {
+ DEBUG(3, ("sam_get_groups_of_sid: sam_methods of domain did not specify sam_get_groups_of_sid\n"));
+ SAFE_FREE(*groups);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_groups_of_sid(tmp_methods, access_token, sids, group_ctrl, &tmp_group_count, &tmp_groups))) {
+ DEBUG(4,("sam_get_groups_of_sid in backend %s failed\n", tmp_methods->backendname));
+ SAFE_FREE(*groups);
+ return nt_status;
+ }
+
+ *groups = Realloc(*groups, ((*group_count) + tmp_group_count) * sizeof(SAM_GROUP_ENUM));
+
+ memcpy(&(*groups)[*group_count], tmp_groups, tmp_group_count);
+
+ SAFE_FREE(tmp_groups);
+
+ *group_count += tmp_group_count;
+
+ tmp_methods = tmp_methods->next;
+ }
+
+ return NT_STATUS_OK;
+}
+
+