diff options
Diffstat (limited to 'source/sam')
-rw-r--r-- | source/sam/account.c | 305 | ||||
-rw-r--r-- | source/sam/group.c | 193 | ||||
-rw-r--r-- | source/sam/gums.c | 173 | ||||
-rw-r--r-- | source/sam/gums_api.c | 1426 | ||||
-rw-r--r-- | source/sam/gums_helper.c | 383 | ||||
-rw-r--r-- | source/sam/gums_tdbsam2.c | 1220 | ||||
-rw-r--r-- | source/sam/idmap.c | 317 | ||||
-rw-r--r-- | source/sam/idmap_ldap.c | 790 | ||||
-rw-r--r-- | source/sam/idmap_tdb.c | 676 | ||||
-rw-r--r-- | source/sam/idmap_util.c | 295 | ||||
-rw-r--r-- | source/sam/interface.c | 1338 |
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; +} + + |