summaryrefslogtreecommitdiff
path: root/source3/passdb
diff options
context:
space:
mode:
Diffstat (limited to 'source3/passdb')
-rw-r--r--source3/passdb/login_cache.c188
-rw-r--r--source3/passdb/lookup_sid.c1501
-rw-r--r--source3/passdb/machine_sid.c248
-rw-r--r--source3/passdb/passdb.c1657
-rw-r--r--source3/passdb/pdb_compat.c103
-rw-r--r--source3/passdb/pdb_get_set.c1081
-rw-r--r--source3/passdb/pdb_interface.c2071
-rw-r--r--source3/passdb/pdb_ldap.c6389
-rw-r--r--source3/passdb/pdb_nds.c913
-rw-r--r--source3/passdb/pdb_smbpasswd.c1718
-rw-r--r--source3/passdb/pdb_tdb.c1639
-rw-r--r--source3/passdb/secrets.c1366
-rw-r--r--source3/passdb/util_builtin.c109
-rw-r--r--source3/passdb/util_unixsids.c105
-rw-r--r--source3/passdb/util_wellknown.c172
15 files changed, 19260 insertions, 0 deletions
diff --git a/source3/passdb/login_cache.c b/source3/passdb/login_cache.c
new file mode 100644
index 0000000000..8222f77b95
--- /dev/null
+++ b/source3/passdb/login_cache.c
@@ -0,0 +1,188 @@
+/*
+ Unix SMB/CIFS implementation.
+ struct samu local cache for
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2004.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#define LOGIN_CACHE_FILE "login_cache.tdb"
+
+#define SAM_CACHE_FORMAT "dwwd"
+
+static TDB_CONTEXT *cache;
+
+bool login_cache_init(void)
+{
+ char* cache_fname = NULL;
+
+ /* skip file open if it's already opened */
+ if (cache) return True;
+
+ asprintf(&cache_fname, "%s/%s", lp_lockdir(), LOGIN_CACHE_FILE);
+ if (cache_fname)
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+ else {
+ DEBUG(0, ("Filename allocation failed.\n"));
+ return False;
+ }
+
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0644);
+
+ if (!cache)
+ DEBUG(5, ("Attempt to open %s failed.\n", cache_fname));
+
+ SAFE_FREE(cache_fname);
+
+ return (cache ? True : False);
+}
+
+bool login_cache_shutdown(void)
+{
+ /* tdb_close routine returns -1 on error */
+ if (!cache) return False;
+ DEBUG(5, ("Closing cache file\n"));
+ return tdb_close(cache) != -1;
+}
+
+/* if we can't read the cache, oh well, no need to return anything */
+LOGIN_CACHE * login_cache_read(struct samu *sampass)
+{
+ char *keystr;
+ TDB_DATA databuf;
+ LOGIN_CACHE *entry;
+
+ if (!login_cache_init())
+ return NULL;
+
+ if (pdb_get_nt_username(sampass) == NULL) {
+ return NULL;
+ }
+
+ keystr = SMB_STRDUP(pdb_get_nt_username(sampass));
+ if (!keystr || !keystr[0]) {
+ SAFE_FREE(keystr);
+ return NULL;
+ }
+
+ DEBUG(7, ("Looking up login cache for user %s\n",
+ keystr));
+ databuf = tdb_fetch_bystring(cache, keystr);
+ SAFE_FREE(keystr);
+
+ if (!(entry = SMB_MALLOC_P(LOGIN_CACHE))) {
+ DEBUG(1, ("Unable to allocate cache entry buffer!\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ if (tdb_unpack (databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ &entry->entry_timestamp, &entry->acct_ctrl,
+ &entry->bad_password_count,
+ &entry->bad_password_time) == -1) {
+ DEBUG(7, ("No cache entry found\n"));
+ SAFE_FREE(entry);
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ SAFE_FREE(databuf.dptr);
+
+ DEBUG(5, ("Found login cache entry: timestamp %12u, flags 0x%x, count %d, time %12u\n",
+ (unsigned int)entry->entry_timestamp, entry->acct_ctrl,
+ entry->bad_password_count, (unsigned int)entry->bad_password_time));
+ return entry;
+}
+
+bool login_cache_write(const struct samu *sampass, LOGIN_CACHE entry)
+{
+ char *keystr;
+ TDB_DATA databuf;
+ bool ret;
+
+ if (!login_cache_init())
+ return False;
+
+ if (pdb_get_nt_username(sampass) == NULL) {
+ return False;
+ }
+
+ keystr = SMB_STRDUP(pdb_get_nt_username(sampass));
+ if (!keystr || !keystr[0]) {
+ SAFE_FREE(keystr);
+ return False;
+ }
+
+ entry.entry_timestamp = time(NULL);
+
+ databuf.dsize =
+ tdb_pack(NULL, 0, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time);
+ databuf.dptr = SMB_MALLOC_ARRAY(uint8, databuf.dsize);
+ if (!databuf.dptr) {
+ SAFE_FREE(keystr);
+ return False;
+ }
+
+ if (tdb_pack(databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time)
+ != databuf.dsize) {
+ SAFE_FREE(keystr);
+ SAFE_FREE(databuf.dptr);
+ return False;
+ }
+
+ ret = tdb_store_bystring(cache, keystr, databuf, 0);
+ SAFE_FREE(keystr);
+ SAFE_FREE(databuf.dptr);
+ return ret == 0;
+}
+
+bool login_cache_delentry(const struct samu *sampass)
+{
+ int ret;
+ char *keystr;
+
+ if (!login_cache_init())
+ return False;
+
+ if (pdb_get_nt_username(sampass) == NULL) {
+ return False;
+ }
+
+ keystr = SMB_STRDUP(pdb_get_nt_username(sampass));
+ if (!keystr || !keystr[0]) {
+ SAFE_FREE(keystr);
+ return False;
+ }
+
+ DEBUG(9, ("About to delete entry for %s\n", keystr));
+ ret = tdb_delete_bystring(cache, keystr);
+ DEBUG(9, ("tdb_delete returned %d\n", ret));
+
+ SAFE_FREE(keystr);
+ return ret == 0;
+}
diff --git a/source3/passdb/lookup_sid.c b/source3/passdb/lookup_sid.c
new file mode 100644
index 0000000000..3861c8e229
--- /dev/null
+++ b/source3/passdb/lookup_sid.c
@@ -0,0 +1,1501 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Gerald (Jerry) Carter 2003
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*****************************************************************
+ Dissect a user-provided name into domain, name, sid and type.
+
+ If an explicit domain name was given in the form domain\user, it
+ has to try that. If no explicit domain name was given, we have
+ to do guesswork.
+*****************************************************************/
+
+bool lookup_name(TALLOC_CTX *mem_ctx,
+ const char *full_name, int flags,
+ const char **ret_domain, const char **ret_name,
+ DOM_SID *ret_sid, enum lsa_SidType *ret_type)
+{
+ char *p;
+ const char *tmp;
+ const char *domain = NULL;
+ const char *name = NULL;
+ uint32 rid;
+ DOM_SID sid;
+ enum lsa_SidType type;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return false;
+ }
+
+ p = strchr_m(full_name, '\\');
+
+ if (p != NULL) {
+ domain = talloc_strndup(tmp_ctx, full_name,
+ PTR_DIFF(p, full_name));
+ name = talloc_strdup(tmp_ctx, p+1);
+ } else {
+ domain = talloc_strdup(tmp_ctx, "");
+ name = talloc_strdup(tmp_ctx, full_name);
+ }
+
+ if ((domain == NULL) || (name == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ DEBUG(10,("lookup_name: %s => %s (domain), %s (name)\n",
+ full_name, domain, name));
+ DEBUG(10, ("lookup_name: flags = 0x0%x\n", flags));
+
+ if ((flags & LOOKUP_NAME_DOMAIN) &&
+ strequal(domain, get_global_sam_name()))
+ {
+
+ /* It's our own domain, lookup the name in passdb */
+ if (lookup_global_sam_name(name, flags, &rid, &type)) {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ goto ok;
+ }
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ if ((flags & LOOKUP_NAME_BUILTIN) &&
+ strequal(domain, builtin_domain_name()))
+ {
+ /* Explicit request for a name in BUILTIN */
+ if (lookup_builtin_name(name, &rid)) {
+ sid_copy(&sid, &global_sid_Builtin);
+ sid_append_rid(&sid, rid);
+ type = SID_NAME_ALIAS;
+ goto ok;
+ }
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ /* Try the explicit winbind lookup first, don't let it guess the
+ * domain yet at this point yet. This comes later. */
+
+ if ((domain[0] != '\0') &&
+ (flags & ~(LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED)) &&
+ (winbind_lookup_name(domain, name, &sid, &type))) {
+ goto ok;
+ }
+
+ if (!(flags & LOOKUP_NAME_EXPLICIT) && strequal(domain, unix_users_domain_name())) {
+ if (lookup_unix_user_name(name, &sid)) {
+ type = SID_NAME_USER;
+ goto ok;
+ }
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ if (!(flags & LOOKUP_NAME_EXPLICIT) && strequal(domain, unix_groups_domain_name())) {
+ if (lookup_unix_group_name(name, &sid)) {
+ type = SID_NAME_DOM_GRP;
+ goto ok;
+ }
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ if ((domain[0] == '\0') && (!(flags & LOOKUP_NAME_ISOLATED))) {
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ /* Now the guesswork begins, we haven't been given an explicit
+ * domain. Try the sequence as documented on
+ * http://msdn.microsoft.com/library/en-us/secmgmt/security/lsalookupnames.asp
+ * November 27, 2005 */
+
+ /* 1. well-known names */
+
+ if ((flags & LOOKUP_NAME_WKN) &&
+ lookup_wellknown_name(tmp_ctx, name, &sid, &domain))
+ {
+ type = SID_NAME_WKN_GRP;
+ goto ok;
+ }
+
+ /* 2. Builtin domain as such */
+
+ if ((flags & (LOOKUP_NAME_BUILTIN|LOOKUP_NAME_REMOTE)) &&
+ strequal(name, builtin_domain_name()))
+ {
+ /* Swap domain and name */
+ tmp = name; name = domain; domain = tmp;
+ sid_copy(&sid, &global_sid_Builtin);
+ type = SID_NAME_DOMAIN;
+ goto ok;
+ }
+
+ /* 3. Account domain */
+
+ if ((flags & LOOKUP_NAME_DOMAIN) &&
+ strequal(name, get_global_sam_name()))
+ {
+ if (!secrets_fetch_domain_sid(name, &sid)) {
+ DEBUG(3, ("Could not fetch my SID\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+ /* Swap domain and name */
+ tmp = name; name = domain; domain = tmp;
+ type = SID_NAME_DOMAIN;
+ goto ok;
+ }
+
+ /* 4. Primary domain */
+
+ if ((flags & LOOKUP_NAME_DOMAIN) && !IS_DC &&
+ strequal(name, lp_workgroup()))
+ {
+ if (!secrets_fetch_domain_sid(name, &sid)) {
+ DEBUG(3, ("Could not fetch the domain SID\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+ /* Swap domain and name */
+ tmp = name; name = domain; domain = tmp;
+ type = SID_NAME_DOMAIN;
+ goto ok;
+ }
+
+ /* 5. Trusted domains as such, to me it looks as if members don't do
+ this, tested an XP workstation in a NT domain -- vl */
+
+ if ((flags & LOOKUP_NAME_REMOTE) && IS_DC &&
+ (pdb_get_trusteddom_pw(name, NULL, &sid, NULL)))
+ {
+ /* Swap domain and name */
+ tmp = name; name = domain; domain = tmp;
+ type = SID_NAME_DOMAIN;
+ goto ok;
+ }
+
+ /* 6. Builtin aliases */
+
+ if ((flags & LOOKUP_NAME_BUILTIN) &&
+ lookup_builtin_name(name, &rid))
+ {
+ domain = talloc_strdup(tmp_ctx, builtin_domain_name());
+ sid_copy(&sid, &global_sid_Builtin);
+ sid_append_rid(&sid, rid);
+ type = SID_NAME_ALIAS;
+ goto ok;
+ }
+
+ /* 7. Local systems' SAM (DCs don't have a local SAM) */
+ /* 8. Primary SAM (On members, this is the domain) */
+
+ /* Both cases are done by looking at our passdb */
+
+ if ((flags & LOOKUP_NAME_DOMAIN) &&
+ lookup_global_sam_name(name, flags, &rid, &type))
+ {
+ domain = talloc_strdup(tmp_ctx, get_global_sam_name());
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ goto ok;
+ }
+
+ /* Now our local possibilities are exhausted. */
+
+ if (!(flags & LOOKUP_NAME_REMOTE)) {
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ /* If we are not a DC, we have to ask in our primary domain. Let
+ * winbind do that. */
+
+ if (!IS_DC &&
+ (winbind_lookup_name(lp_workgroup(), name, &sid, &type))) {
+ domain = talloc_strdup(tmp_ctx, lp_workgroup());
+ goto ok;
+ }
+
+ /* 9. Trusted domains */
+
+ /* If we're a DC we have to ask all trusted DC's. Winbind does not do
+ * that (yet), but give it a chance. */
+
+ if (IS_DC && winbind_lookup_name("", name, &sid, &type)) {
+ DOM_SID dom_sid;
+ uint32 tmp_rid;
+ enum lsa_SidType domain_type;
+
+ if (type == SID_NAME_DOMAIN) {
+ /* Swap name and type */
+ tmp = name; name = domain; domain = tmp;
+ goto ok;
+ }
+
+ /* Here we have to cope with a little deficiency in the
+ * winbind API: We have to ask it again for the name of the
+ * domain it figured out itself. Maybe fix that later... */
+
+ sid_copy(&dom_sid, &sid);
+ sid_split_rid(&dom_sid, &tmp_rid);
+
+ if (!winbind_lookup_sid(tmp_ctx, &dom_sid, &domain, NULL,
+ &domain_type) ||
+ (domain_type != SID_NAME_DOMAIN)) {
+ DEBUG(2, ("winbind could not find the domain's name "
+ "it just looked up for us\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+ goto ok;
+ }
+
+ /* 10. Don't translate */
+
+ /* 11. Ok, windows would end here. Samba has two more options:
+ Unmapped users and unmapped groups */
+
+ if (!(flags & LOOKUP_NAME_EXPLICIT) && lookup_unix_user_name(name, &sid)) {
+ domain = talloc_strdup(tmp_ctx, unix_users_domain_name());
+ type = SID_NAME_USER;
+ goto ok;
+ }
+
+ if (!(flags & LOOKUP_NAME_EXPLICIT) && lookup_unix_group_name(name, &sid)) {
+ domain = talloc_strdup(tmp_ctx, unix_groups_domain_name());
+ type = SID_NAME_DOM_GRP;
+ goto ok;
+ }
+
+ /*
+ * Ok, all possibilities tried. Fail.
+ */
+
+ TALLOC_FREE(tmp_ctx);
+ return false;
+
+ ok:
+ if ((domain == NULL) || (name == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ /*
+ * Hand over the results to the talloc context we've been given.
+ */
+
+ if ((ret_name != NULL) &&
+ !(*ret_name = talloc_strdup(mem_ctx, name))) {
+ DEBUG(0, ("talloc failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ if (ret_domain != NULL) {
+ char *tmp_dom;
+ if (!(tmp_dom = talloc_strdup(mem_ctx, domain))) {
+ DEBUG(0, ("talloc failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+ strupper_m(tmp_dom);
+ *ret_domain = tmp_dom;
+ }
+
+ if (ret_sid != NULL) {
+ sid_copy(ret_sid, &sid);
+ }
+
+ if (ret_type != NULL) {
+ *ret_type = type;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return true;
+}
+
+/************************************************************************
+ Names from smb.conf can be unqualified. eg. valid users = foo
+ These names should never map to a remote name. Try global_sam_name()\foo,
+ and then "Unix Users"\foo (or "Unix Groups"\foo).
+************************************************************************/
+
+bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
+ const char *full_name, int flags,
+ const char **ret_domain, const char **ret_name,
+ DOM_SID *ret_sid, enum lsa_SidType *ret_type)
+{
+ char *qualified_name;
+ const char *p;
+
+ /* NB. No winbindd_separator here as lookup_name needs \\' */
+ if ((p = strchr_m(full_name, *lp_winbind_separator())) != NULL) {
+
+ /* The name is already qualified with a domain. */
+
+ if (*lp_winbind_separator() != '\\') {
+ char *tmp;
+
+ /* lookup_name() needs '\\' as a separator */
+
+ tmp = talloc_strdup(mem_ctx, full_name);
+ if (!tmp) {
+ return false;
+ }
+ tmp[p - full_name] = '\\';
+ full_name = tmp;
+ }
+
+ return lookup_name(mem_ctx, full_name, flags,
+ ret_domain, ret_name,
+ ret_sid, ret_type);
+ }
+
+ /* Try with our own SAM name. */
+ qualified_name = talloc_asprintf(mem_ctx, "%s\\%s",
+ get_global_sam_name(),
+ full_name );
+ if (!qualified_name) {
+ return false;
+ }
+
+ if (lookup_name(mem_ctx, qualified_name, flags,
+ ret_domain, ret_name,
+ ret_sid, ret_type)) {
+ return true;
+ }
+
+ /* Finally try with "Unix Users" or "Unix Group" */
+ qualified_name = talloc_asprintf(mem_ctx, "%s\\%s",
+ flags & LOOKUP_NAME_GROUP ?
+ unix_groups_domain_name() :
+ unix_users_domain_name(),
+ full_name );
+ if (!qualified_name) {
+ return false;
+ }
+
+ return lookup_name(mem_ctx, qualified_name, flags,
+ ret_domain, ret_name,
+ ret_sid, ret_type);
+}
+
+static bool wb_lookup_rids(TALLOC_CTX *mem_ctx,
+ const DOM_SID *domain_sid,
+ int num_rids, uint32 *rids,
+ const char **domain_name,
+ const char **names, enum lsa_SidType *types)
+{
+ int i;
+ const char **my_names;
+ enum lsa_SidType *my_types;
+ TALLOC_CTX *tmp_ctx;
+
+ if (!(tmp_ctx = talloc_init("wb_lookup_rids"))) {
+ return false;
+ }
+
+ if (!winbind_lookup_rids(tmp_ctx, domain_sid, num_rids, rids,
+ domain_name, &my_names, &my_types)) {
+ *domain_name = "";
+ for (i=0; i<num_rids; i++) {
+ names[i] = "";
+ types[i] = SID_NAME_UNKNOWN;
+ }
+ TALLOC_FREE(tmp_ctx);
+ return true;
+ }
+
+ if (!(*domain_name = talloc_strdup(mem_ctx, *domain_name))) {
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+
+ /*
+ * winbind_lookup_rids allocates its own array. We've been given the
+ * array, so copy it over
+ */
+
+ for (i=0; i<num_rids; i++) {
+ if (my_names[i] == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+ if (!(names[i] = talloc_strdup(names, my_names[i]))) {
+ TALLOC_FREE(tmp_ctx);
+ return false;
+ }
+ types[i] = my_types[i];
+ }
+ TALLOC_FREE(tmp_ctx);
+ return true;
+}
+
+static bool lookup_rids(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
+ int num_rids, uint32_t *rids,
+ const char **domain_name,
+ const char ***names, enum lsa_SidType **types)
+{
+ int i;
+
+ DEBUG(10, ("lookup_rids called for domain sid '%s'\n",
+ sid_string_dbg(domain_sid)));
+
+ if (num_rids) {
+ *names = TALLOC_ARRAY(mem_ctx, const char *, num_rids);
+ *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
+
+ if ((*names == NULL) || (*types == NULL)) {
+ return false;
+ }
+ } else {
+ *names = NULL;
+ *types = NULL;
+ }
+
+ if (sid_check_is_domain(domain_sid)) {
+ NTSTATUS result;
+
+ if (*domain_name == NULL) {
+ *domain_name = talloc_strdup(
+ mem_ctx, get_global_sam_name());
+ }
+
+ if (*domain_name == NULL) {
+ return false;
+ }
+
+ become_root();
+ result = pdb_lookup_rids(domain_sid, num_rids, rids,
+ *names, *types);
+ unbecome_root();
+
+ return (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED) ||
+ NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED));
+ }
+
+ if (sid_check_is_builtin(domain_sid)) {
+
+ if (*domain_name == NULL) {
+ *domain_name = talloc_strdup(
+ mem_ctx, builtin_domain_name());
+ }
+
+ if (*domain_name == NULL) {
+ return false;
+ }
+
+ for (i=0; i<num_rids; i++) {
+ if (lookup_builtin_rid(*names, rids[i],
+ &(*names)[i])) {
+ if ((*names)[i] == NULL) {
+ return false;
+ }
+ (*types)[i] = SID_NAME_ALIAS;
+ } else {
+ (*types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+ return true;
+ }
+
+ if (sid_check_is_wellknown_domain(domain_sid, NULL)) {
+ for (i=0; i<num_rids; i++) {
+ DOM_SID sid;
+ sid_copy(&sid, domain_sid);
+ sid_append_rid(&sid, rids[i]);
+ if (lookup_wellknown_sid(mem_ctx, &sid,
+ domain_name, &(*names)[i])) {
+ if ((*names)[i] == NULL) {
+ return false;
+ }
+ (*types)[i] = SID_NAME_WKN_GRP;
+ } else {
+ (*types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+ return true;
+ }
+
+ if (sid_check_is_unix_users(domain_sid)) {
+ if (*domain_name == NULL) {
+ *domain_name = talloc_strdup(
+ mem_ctx, unix_users_domain_name());
+ if (*domain_name == NULL) {
+ return false;
+ }
+ }
+ for (i=0; i<num_rids; i++) {
+ (*names)[i] = talloc_strdup(
+ (*names), uidtoname(rids[i]));
+ if ((*names)[i] == NULL) {
+ return false;
+ }
+ (*types)[i] = SID_NAME_USER;
+ }
+ return true;
+ }
+
+ if (sid_check_is_unix_groups(domain_sid)) {
+ if (*domain_name == NULL) {
+ *domain_name = talloc_strdup(
+ mem_ctx, unix_groups_domain_name());
+ if (*domain_name == NULL) {
+ return false;
+ }
+ }
+ for (i=0; i<num_rids; i++) {
+ (*names)[i] = talloc_strdup(
+ (*names), gidtoname(rids[i]));
+ if ((*names)[i] == NULL) {
+ return false;
+ }
+ (*types)[i] = SID_NAME_DOM_GRP;
+ }
+ return true;
+ }
+
+ return wb_lookup_rids(mem_ctx, domain_sid, num_rids, rids,
+ domain_name, *names, *types);
+}
+
+/*
+ * Is the SID a domain as such? If yes, lookup its name.
+ */
+
+static bool lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx,
+ const char **name)
+{
+ const char *tmp;
+ enum lsa_SidType type;
+
+ if (sid_check_is_domain(sid)) {
+ *name = talloc_strdup(mem_ctx, get_global_sam_name());
+ return true;
+ }
+
+ if (sid_check_is_builtin(sid)) {
+ *name = talloc_strdup(mem_ctx, builtin_domain_name());
+ return true;
+ }
+
+ if (sid_check_is_wellknown_domain(sid, &tmp)) {
+ *name = talloc_strdup(mem_ctx, tmp);
+ return true;
+ }
+
+ if (sid_check_is_unix_users(sid)) {
+ *name = talloc_strdup(mem_ctx, unix_users_domain_name());
+ return true;
+ }
+
+ if (sid_check_is_unix_groups(sid)) {
+ *name = talloc_strdup(mem_ctx, unix_groups_domain_name());
+ return true;
+ }
+
+ if (sid->num_auths != 4) {
+ /* This can't be a domain */
+ return false;
+ }
+
+ if (IS_DC) {
+ uint32 i, num_domains;
+ struct trustdom_info **domains;
+
+ /* This is relatively expensive, but it happens only on DCs
+ * and for SIDs that have 4 sub-authorities and thus look like
+ * domains */
+
+ if (!NT_STATUS_IS_OK(pdb_enum_trusteddoms(mem_ctx,
+ &num_domains,
+ &domains))) {
+ return false;
+ }
+
+ for (i=0; i<num_domains; i++) {
+ if (sid_equal(sid, &domains[i]->sid)) {
+ *name = talloc_strdup(mem_ctx,
+ domains[i]->name);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (winbind_lookup_sid(mem_ctx, sid, &tmp, NULL, &type) &&
+ (type == SID_NAME_DOMAIN)) {
+ *name = tmp;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * This tries to implement the rather weird rules for the lsa_lookup level
+ * parameter.
+ *
+ * This is as close as we can get to what W2k3 does. With this we survive the
+ * RPC-LSALOOKUP samba4 test as of 2006-01-08. NT4 as a PDC is a bit more
+ * different, but I assume that's just being too liberal. For example, W2k3
+ * replies to everything else but the levels 1-6 with INVALID_PARAMETER
+ * whereas NT4 does the same as level 1 (I think). I did not fully test that
+ * with NT4, this is what w2k3 does.
+ *
+ * Level 1: Ask everywhere
+ * Level 2: Ask domain and trusted domains, no builtin and wkn
+ * Level 3: Only ask domain
+ * Level 4: W2k3ad: Only ask AD trusts
+ * Level 5: Only ask transitive forest trusts
+ * Level 6: Like 4
+ */
+
+static bool check_dom_sid_to_level(const DOM_SID *sid, int level)
+{
+ int ret = false;
+
+ switch(level) {
+ case 1:
+ ret = true;
+ break;
+ case 2:
+ ret = (!sid_check_is_builtin(sid) &&
+ !sid_check_is_wellknown_domain(sid, NULL));
+ break;
+ case 3:
+ case 4:
+ case 6:
+ ret = sid_check_is_domain(sid);
+ break;
+ case 5:
+ ret = false;
+ break;
+ }
+
+ DEBUG(10, ("%s SID %s in level %d\n",
+ ret ? "Accepting" : "Rejecting",
+ sid_string_dbg(sid), level));
+ return ret;
+}
+
+/*
+ * Lookup a bunch of SIDs. This is modeled after lsa_lookup_sids with
+ * references to domains, it is explicitly made for this.
+ *
+ * This attempts to be as efficient as possible: It collects all SIDs
+ * belonging to a domain and hands them in bulk to the appropriate lookup
+ * function. In particular pdb_lookup_rids with ldapsam_trusted benefits
+ * *hugely* from this. Winbind is going to be extended with a lookup_rids
+ * interface as well, so on a DC we can do a bulk lsa_lookuprids to the
+ * appropriate DC.
+ */
+
+NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
+ const DOM_SID **sids, int level,
+ struct lsa_dom_info **ret_domains,
+ struct lsa_name_info **ret_names)
+{
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct lsa_name_info *name_infos;
+ struct lsa_dom_info *dom_infos = NULL;
+
+ int i, j;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (num_sids) {
+ name_infos = TALLOC_ARRAY(mem_ctx, struct lsa_name_info, num_sids);
+ if (name_infos == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ } else {
+ name_infos = NULL;
+ }
+
+ dom_infos = TALLOC_ZERO_ARRAY(mem_ctx, struct lsa_dom_info,
+ MAX_REF_DOMAINS);
+ if (dom_infos == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ /* First build up the data structures:
+ *
+ * dom_infos is a list of domains referenced in the list of
+ * SIDs. Later we will walk the list of domains and look up the RIDs
+ * in bulk.
+ *
+ * name_infos is a shadow-copy of the SIDs array to collect the real
+ * data.
+ *
+ * dom_info->idxs is an index into the name_infos array. The
+ * difficulty we have here is that we need to keep the SIDs the client
+ * asked for in the same order for the reply
+ */
+
+ for (i=0; i<num_sids; i++) {
+ DOM_SID sid;
+ uint32 rid;
+ const char *domain_name = NULL;
+
+ sid_copy(&sid, sids[i]);
+ name_infos[i].type = SID_NAME_USE_NONE;
+
+ if (lookup_as_domain(&sid, name_infos, &domain_name)) {
+ /* We can't push that through the normal lookup
+ * process, as this would reference illegal
+ * domains.
+ *
+ * For example S-1-5-32 would end up referencing
+ * domain S-1-5- with RID 32 which is clearly wrong.
+ */
+ if (domain_name == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ name_infos[i].rid = 0;
+ name_infos[i].type = SID_NAME_DOMAIN;
+ name_infos[i].name = NULL;
+
+ if (sid_check_is_builtin(&sid)) {
+ /* Yes, W2k3 returns "BUILTIN" both as domain
+ * and name here */
+ name_infos[i].name = talloc_strdup(
+ name_infos, builtin_domain_name());
+ if (name_infos[i].name == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+ } else {
+ /* This is a normal SID with rid component */
+ if (!sid_split_rid(&sid, &rid)) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+
+ if (!check_dom_sid_to_level(&sid, level)) {
+ name_infos[i].rid = 0;
+ name_infos[i].type = SID_NAME_UNKNOWN;
+ name_infos[i].name = NULL;
+ continue;
+ }
+
+ for (j=0; j<MAX_REF_DOMAINS; j++) {
+ if (!dom_infos[j].valid) {
+ break;
+ }
+ if (sid_equal(&sid, &dom_infos[j].sid)) {
+ break;
+ }
+ }
+
+ if (j == MAX_REF_DOMAINS) {
+ /* TODO: What's the right error message here? */
+ result = NT_STATUS_NONE_MAPPED;
+ goto fail;
+ }
+
+ if (!dom_infos[j].valid) {
+ /* We found a domain not yet referenced, create a new
+ * ref. */
+ dom_infos[j].valid = true;
+ sid_copy(&dom_infos[j].sid, &sid);
+
+ if (domain_name != NULL) {
+ /* This name was being found above in the case
+ * when we found a domain SID */
+ dom_infos[j].name =
+ talloc_strdup(dom_infos, domain_name);
+ if (dom_infos[j].name == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ } else {
+ /* lookup_rids will take care of this */
+ dom_infos[j].name = NULL;
+ }
+ }
+
+ name_infos[i].dom_idx = j;
+
+ if (name_infos[i].type == SID_NAME_USE_NONE) {
+ name_infos[i].rid = rid;
+
+ ADD_TO_ARRAY(dom_infos, int, i, &dom_infos[j].idxs,
+ &dom_infos[j].num_idxs);
+
+ if (dom_infos[j].idxs == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+ }
+
+ /* Iterate over the domains found */
+
+ for (i=0; i<MAX_REF_DOMAINS; i++) {
+ uint32_t *rids;
+ const char *domain_name = NULL;
+ const char **names;
+ enum lsa_SidType *types;
+ struct lsa_dom_info *dom = &dom_infos[i];
+
+ if (!dom->valid) {
+ /* No domains left, we're done */
+ break;
+ }
+
+ if (dom->num_idxs) {
+ if (!(rids = TALLOC_ARRAY(tmp_ctx, uint32, dom->num_idxs))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ } else {
+ rids = NULL;
+ }
+
+ for (j=0; j<dom->num_idxs; j++) {
+ rids[j] = name_infos[dom->idxs[j]].rid;
+ }
+
+ if (!lookup_rids(tmp_ctx, &dom->sid,
+ dom->num_idxs, rids, &domain_name,
+ &names, &types)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!(dom->name = talloc_strdup(dom_infos, domain_name))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ for (j=0; j<dom->num_idxs; j++) {
+ int idx = dom->idxs[j];
+ name_infos[idx].type = types[j];
+ if (types[j] != SID_NAME_UNKNOWN) {
+ name_infos[idx].name =
+ talloc_strdup(name_infos, names[j]);
+ if (name_infos[idx].name == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ } else {
+ name_infos[idx].name = NULL;
+ }
+ }
+ }
+
+ *ret_domains = dom_infos;
+ *ret_names = name_infos;
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+
+ fail:
+ TALLOC_FREE(dom_infos);
+ TALLOC_FREE(name_infos);
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to name function.
+*****************************************************************/
+
+bool lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
+ const char **ret_domain, const char **ret_name,
+ enum lsa_SidType *ret_type)
+{
+ struct lsa_dom_info *domain;
+ struct lsa_name_info *name;
+ TALLOC_CTX *tmp_ctx;
+ bool ret = false;
+
+ DEBUG(10, ("lookup_sid called for SID '%s'\n", sid_string_dbg(sid)));
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(lookup_sids(tmp_ctx, 1, &sid, 1,
+ &domain, &name))) {
+ goto done;
+ }
+
+ if (name->type == SID_NAME_UNKNOWN) {
+ goto done;
+ }
+
+ if ((ret_domain != NULL) &&
+ !(*ret_domain = talloc_strdup(mem_ctx, domain->name))) {
+ goto done;
+ }
+
+ if ((ret_name != NULL) &&
+ !(*ret_name = talloc_strdup(mem_ctx, name->name))) {
+ goto done;
+ }
+
+ if (ret_type != NULL) {
+ *ret_type = name->type;
+ }
+
+ ret = true;
+
+ done:
+ if (ret) {
+ DEBUG(10, ("Sid %s -> %s\\%s(%d)\n", sid_string_dbg(sid),
+ domain->name, name->name, name->type));
+ } else {
+ DEBUG(10, ("failed to lookup sid %s\n", sid_string_dbg(sid)));
+ }
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+/*****************************************************************
+ Id mapping cache. This is to avoid Winbind mappings already
+ seen by smbd to be queried too frequently, keeping winbindd
+ busy, and blocking smbd while winbindd is busy with other
+ stuff. Written by Michael Steffens <michael.steffens@hp.com>,
+ modified to use linked lists by jra.
+*****************************************************************/
+
+/*****************************************************************
+ Find a SID given a uid.
+*****************************************************************/
+
+static bool fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid)
+{
+ DATA_BLOB cache_value;
+
+ if (!memcache_lookup(NULL, UID_SID_CACHE,
+ data_blob_const(&uid, sizeof(uid)),
+ &cache_value)) {
+ return false;
+ }
+
+ memcpy(psid, cache_value.data, MIN(sizeof(*psid), cache_value.length));
+ SMB_ASSERT(cache_value.length >= offsetof(struct dom_sid, id_auth));
+ SMB_ASSERT(cache_value.length == ndr_size_dom_sid(psid, 0));
+
+ return true;
+}
+
+/*****************************************************************
+ Find a uid given a SID.
+*****************************************************************/
+
+static bool fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid )
+{
+ DATA_BLOB cache_value;
+
+ if (!memcache_lookup(NULL, SID_UID_CACHE,
+ data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
+ &cache_value)) {
+ return false;
+ }
+
+ SMB_ASSERT(cache_value.length == sizeof(*puid));
+ memcpy(puid, cache_value.data, sizeof(*puid));
+
+ return true;
+}
+
+/*****************************************************************
+ Store uid to SID mapping in cache.
+*****************************************************************/
+
+void store_uid_sid_cache(const DOM_SID *psid, uid_t uid)
+{
+ memcache_add(NULL, SID_UID_CACHE,
+ data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
+ data_blob_const(&uid, sizeof(uid)));
+ memcache_add(NULL, UID_SID_CACHE,
+ data_blob_const(&uid, sizeof(uid)),
+ data_blob_const(psid, ndr_size_dom_sid(psid, 0)));
+}
+
+/*****************************************************************
+ Find a SID given a gid.
+*****************************************************************/
+
+static bool fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
+{
+ DATA_BLOB cache_value;
+
+ if (!memcache_lookup(NULL, GID_SID_CACHE,
+ data_blob_const(&gid, sizeof(gid)),
+ &cache_value)) {
+ return false;
+ }
+
+ memcpy(psid, cache_value.data, MIN(sizeof(*psid), cache_value.length));
+ SMB_ASSERT(cache_value.length >= offsetof(struct dom_sid, id_auth));
+ SMB_ASSERT(cache_value.length == ndr_size_dom_sid(psid, 0));
+
+ return true;
+}
+
+/*****************************************************************
+ Find a gid given a SID.
+*****************************************************************/
+
+static bool fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid)
+{
+ DATA_BLOB cache_value;
+
+ if (!memcache_lookup(NULL, SID_UID_CACHE,
+ data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
+ &cache_value)) {
+ return false;
+ }
+
+ SMB_ASSERT(cache_value.length == sizeof(*pgid));
+ memcpy(pgid, cache_value.data, sizeof(*pgid));
+
+ return true;
+}
+
+/*****************************************************************
+ Store gid to SID mapping in cache.
+*****************************************************************/
+
+void store_gid_sid_cache(const DOM_SID *psid, gid_t gid)
+{
+ memcache_add(NULL, SID_GID_CACHE,
+ data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
+ data_blob_const(&gid, sizeof(gid)));
+ memcache_add(NULL, GID_SID_CACHE,
+ data_blob_const(&gid, sizeof(gid)),
+ data_blob_const(psid, ndr_size_dom_sid(psid, 0)));
+}
+
+/*****************************************************************
+ *THE LEGACY* convert uid_t to SID function.
+*****************************************************************/
+
+static void legacy_uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ uint32 rid;
+ bool ret;
+
+ ZERO_STRUCTP(psid);
+
+ become_root();
+ ret = pdb_uid_to_rid(uid, &rid);
+ unbecome_root();
+
+ if (ret) {
+ /* This is a mapped user */
+ sid_copy(psid, get_global_sam_sid());
+ sid_append_rid(psid, rid);
+ goto done;
+ }
+
+ /* This is an unmapped user */
+
+ uid_to_unix_users_sid(uid, psid);
+
+ done:
+ DEBUG(10,("LEGACY: uid %u -> sid %s\n", (unsigned int)uid,
+ sid_string_dbg(psid)));
+
+ store_uid_sid_cache(psid, uid);
+ return;
+}
+
+/*****************************************************************
+ *THE LEGACY* convert gid_t to SID function.
+*****************************************************************/
+
+static void legacy_gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ bool ret;
+
+ ZERO_STRUCTP(psid);
+
+ become_root();
+ ret = pdb_gid_to_sid(gid, psid);
+ unbecome_root();
+
+ if (ret) {
+ /* This is a mapped group */
+ goto done;
+ }
+
+ /* This is an unmapped group */
+
+ gid_to_unix_groups_sid(gid, psid);
+
+ done:
+ DEBUG(10,("LEGACY: gid %u -> sid %s\n", (unsigned int)gid,
+ sid_string_dbg(psid)));
+
+ store_gid_sid_cache(psid, gid);
+ return;
+}
+
+/*****************************************************************
+ *THE LEGACY* convert SID to uid function.
+*****************************************************************/
+
+static bool legacy_sid_to_uid(const DOM_SID *psid, uid_t *puid)
+{
+ enum lsa_SidType type;
+ uint32 rid;
+
+ if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
+ union unid_t id;
+ bool ret;
+
+ become_root();
+ ret = pdb_sid_to_id(psid, &id, &type);
+ unbecome_root();
+
+ if (ret) {
+ if (type != SID_NAME_USER) {
+ DEBUG(5, ("sid %s is a %s, expected a user\n",
+ sid_string_dbg(psid),
+ sid_type_lookup(type)));
+ return false;
+ }
+ *puid = id.uid;
+ goto done;
+ }
+
+ /* This was ours, but it was not mapped. Fail */
+ }
+
+ DEBUG(10,("LEGACY: mapping failed for sid %s\n",
+ sid_string_dbg(psid)));
+ return false;
+
+done:
+ DEBUG(10,("LEGACY: sid %s -> uid %u\n", sid_string_dbg(psid),
+ (unsigned int)*puid ));
+
+ store_uid_sid_cache(psid, *puid);
+ return true;
+}
+
+/*****************************************************************
+ *THE LEGACY* convert SID to gid function.
+ Group mapping is used for gids that maps to Wellknown SIDs
+*****************************************************************/
+
+static bool legacy_sid_to_gid(const DOM_SID *psid, gid_t *pgid)
+{
+ uint32 rid;
+ GROUP_MAP map;
+ union unid_t id;
+ enum lsa_SidType type;
+
+ if ((sid_check_is_in_builtin(psid) ||
+ sid_check_is_in_wellknown_domain(psid))) {
+ bool ret;
+
+ become_root();
+ ret = pdb_getgrsid(&map, *psid);
+ unbecome_root();
+
+ if (ret) {
+ *pgid = map.gid;
+ goto done;
+ }
+ DEBUG(10,("LEGACY: mapping failed for sid %s\n",
+ sid_string_dbg(psid)));
+ return false;
+ }
+
+ if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
+ bool ret;
+
+ become_root();
+ ret = pdb_sid_to_id(psid, &id, &type);
+ unbecome_root();
+
+ if (ret) {
+ if ((type != SID_NAME_DOM_GRP) &&
+ (type != SID_NAME_ALIAS)) {
+ DEBUG(5, ("LEGACY: sid %s is a %s, expected "
+ "a group\n", sid_string_dbg(psid),
+ sid_type_lookup(type)));
+ return false;
+ }
+ *pgid = id.gid;
+ goto done;
+ }
+
+ /* This was ours, but it was not mapped. Fail */
+ }
+
+ DEBUG(10,("LEGACY: mapping failed for sid %s\n",
+ sid_string_dbg(psid)));
+ return false;
+
+ done:
+ DEBUG(10,("LEGACY: sid %s -> gid %u\n", sid_string_dbg(psid),
+ (unsigned int)*pgid ));
+
+ store_gid_sid_cache(psid, *pgid);
+
+ return true;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert uid_t to SID function.
+*****************************************************************/
+
+void uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ bool expired = true;
+ bool ret;
+ ZERO_STRUCTP(psid);
+
+ if (fetch_sid_from_uid_cache(psid, uid))
+ return;
+
+ /* Check the winbindd cache directly. */
+ ret = idmap_cache_find_uid2sid(uid, psid, &expired);
+
+ if (ret && !expired && is_null_sid(psid)) {
+ /*
+ * Negative cache entry, we already asked.
+ * do legacy.
+ */
+ legacy_uid_to_sid(psid, uid);
+ return;
+ }
+
+ if (!ret || expired) {
+ /* Not in cache. Ask winbindd. */
+ if (!winbind_uid_to_sid(psid, uid)) {
+ if (!winbind_ping()) {
+ legacy_uid_to_sid(psid, uid);
+ return;
+ }
+
+ DEBUG(5, ("uid_to_sid: winbind failed to find a sid for uid %u\n",
+ uid));
+ return;
+ }
+ }
+
+ DEBUG(10,("uid %u -> sid %s\n", (unsigned int)uid,
+ sid_string_dbg(psid)));
+
+ store_uid_sid_cache(psid, uid);
+ return;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert gid_t to SID function.
+*****************************************************************/
+
+void gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ bool expired = true;
+ bool ret;
+ ZERO_STRUCTP(psid);
+
+ if (fetch_sid_from_gid_cache(psid, gid))
+ return;
+
+ /* Check the winbindd cache directly. */
+ ret = idmap_cache_find_gid2sid(gid, psid, &expired);
+
+ if (ret && !expired && is_null_sid(psid)) {
+ /*
+ * Negative cache entry, we already asked.
+ * do legacy.
+ */
+ legacy_gid_to_sid(psid, gid);
+ return;
+ }
+
+ if (!ret || expired) {
+ /* Not in cache. Ask winbindd. */
+ if (!winbind_gid_to_sid(psid, gid)) {
+ if (!winbind_ping()) {
+ legacy_gid_to_sid(psid, gid);
+ return;
+ }
+
+ DEBUG(5, ("gid_to_sid: winbind failed to find a sid for gid %u\n",
+ gid));
+ return;
+ }
+ }
+
+ DEBUG(10,("gid %u -> sid %s\n", (unsigned int)gid,
+ sid_string_dbg(psid)));
+
+ store_gid_sid_cache(psid, gid);
+ return;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to uid function.
+*****************************************************************/
+
+bool sid_to_uid(const DOM_SID *psid, uid_t *puid)
+{
+ bool expired = true;
+ bool ret;
+ uint32 rid;
+ gid_t gid;
+
+ if (fetch_uid_from_cache(puid, psid))
+ return true;
+
+ if (fetch_gid_from_cache(&gid, psid)) {
+ return false;
+ }
+
+ /* Optimize for the Unix Users Domain
+ * as the conversion is straightforward */
+ if (sid_peek_check_rid(&global_sid_Unix_Users, psid, &rid)) {
+ uid_t uid = rid;
+ *puid = uid;
+
+ /* return here, don't cache */
+ DEBUG(10,("sid %s -> uid %u\n", sid_string_dbg(psid),
+ (unsigned int)*puid ));
+ return true;
+ }
+
+ /* Check the winbindd cache directly. */
+ ret = idmap_cache_find_sid2uid(psid, puid, &expired);
+
+ if (ret && !expired && (*puid == (uid_t)-1)) {
+ /*
+ * Negative cache entry, we already asked.
+ * do legacy.
+ */
+ return legacy_sid_to_uid(psid, puid);
+ }
+
+ if (!ret || expired) {
+ /* Not in cache. Ask winbindd. */
+ if (!winbind_sid_to_uid(puid, psid)) {
+ if (!winbind_ping()) {
+ return legacy_sid_to_uid(psid, puid);
+ }
+
+ DEBUG(5, ("winbind failed to find a uid for sid %s\n",
+ sid_string_dbg(psid)));
+ return false;
+ }
+ }
+
+ /* TODO: Here would be the place to allocate both a gid and a uid for
+ * the SID in question */
+
+ DEBUG(10,("sid %s -> uid %u\n", sid_string_dbg(psid),
+ (unsigned int)*puid ));
+
+ store_uid_sid_cache(psid, *puid);
+ return true;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ Group mapping is used for gids that maps to Wellknown SIDs
+*****************************************************************/
+
+bool sid_to_gid(const DOM_SID *psid, gid_t *pgid)
+{
+ bool expired = true;
+ bool ret;
+ uint32 rid;
+ uid_t uid;
+
+ if (fetch_gid_from_cache(pgid, psid))
+ return true;
+
+ if (fetch_uid_from_cache(&uid, psid))
+ return false;
+
+ /* Optimize for the Unix Groups Domain
+ * as the conversion is straightforward */
+ if (sid_peek_check_rid(&global_sid_Unix_Groups, psid, &rid)) {
+ gid_t gid = rid;
+ *pgid = gid;
+
+ /* return here, don't cache */
+ DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid),
+ (unsigned int)*pgid ));
+ return true;
+ }
+
+ /* Check the winbindd cache directly. */
+ ret = idmap_cache_find_sid2gid(psid, pgid, &expired);
+
+ if (ret && !expired && (*pgid == (gid_t)-1)) {
+ /*
+ * Negative cache entry, we already asked.
+ * do legacy.
+ */
+ return legacy_sid_to_gid(psid, pgid);
+ }
+
+ if (!ret || expired) {
+ /* Not in cache or negative. Ask winbindd. */
+ /* Ask winbindd if it can map this sid to a gid.
+ * (Idmap will check it is a valid SID and of the right type) */
+
+ if ( !winbind_sid_to_gid(pgid, psid) ) {
+ if (!winbind_ping()) {
+ return legacy_sid_to_gid(psid, pgid);
+ }
+
+ DEBUG(10,("winbind failed to find a gid for sid %s\n",
+ sid_string_dbg(psid)));
+ return false;
+ }
+ }
+
+ DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid),
+ (unsigned int)*pgid ));
+
+ store_gid_sid_cache(psid, *pgid);
+ return true;
+}
diff --git a/source3/passdb/machine_sid.c b/source3/passdb/machine_sid.c
new file mode 100644
index 0000000000..ff2c9bcb0d
--- /dev/null
+++ b/source3/passdb/machine_sid.c
@@ -0,0 +1,248 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2002
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Gerald (Jerry) Carter 2000
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* NOTE! the global_sam_sid is the SID of our local SAM. This is only
+ equal to the domain SID when we are a DC, otherwise its our
+ workstation SID */
+static DOM_SID *global_sam_sid=NULL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/****************************************************************************
+ Read a SID from a file. This is for compatibility with the old MACHINE.SID
+ style of SID storage
+****************************************************************************/
+
+static bool read_sid_from_file(const char *fname, DOM_SID *sid)
+{
+ char **lines;
+ int numlines;
+ bool ret;
+
+ lines = file_lines_load(fname, &numlines,0);
+
+ if (!lines || numlines < 1) {
+ if (lines) file_lines_free(lines);
+ return False;
+ }
+
+ ret = string_to_sid(sid, lines[0]);
+ file_lines_free(lines);
+ return ret;
+}
+
+/*
+ generate a random sid - used to build our own sid if we don't have one
+*/
+static void generate_random_sid(DOM_SID *sid)
+{
+ int i;
+ uchar raw_sid_data[12];
+
+ memset((char *)sid, '\0', sizeof(*sid));
+ sid->sid_rev_num = 1;
+ sid->id_auth[5] = 5;
+ sid->num_auths = 0;
+ sid->sub_auths[sid->num_auths++] = 21;
+
+ generate_random_buffer(raw_sid_data, 12);
+ for (i = 0; i < 3; i++)
+ sid->sub_auths[sid->num_auths++] = IVAL(raw_sid_data, i*4);
+}
+
+/****************************************************************************
+ Generate the global machine sid.
+****************************************************************************/
+
+static DOM_SID *pdb_generate_sam_sid(void)
+{
+ DOM_SID domain_sid;
+ char *fname = NULL;
+ DOM_SID *sam_sid;
+
+ if(!(sam_sid=SMB_MALLOC_P(DOM_SID)))
+ return NULL;
+
+ if ( IS_DC ) {
+ if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+ sid_copy(sam_sid, &domain_sid);
+ return sam_sid;
+ }
+ }
+
+ if (secrets_fetch_domain_sid(global_myname(), sam_sid)) {
+
+ /* We got our sid. If not a pdc/bdc, we're done. */
+ if ( !IS_DC )
+ return sam_sid;
+
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+
+ /* No domain sid and we're a pdc/bdc. Store it */
+
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Can't store domain SID as a pdc/bdc.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ return sam_sid;
+ }
+
+ if (!sid_equal(&domain_sid, sam_sid)) {
+
+ /* Domain name sid doesn't match global sam sid. Re-store domain sid as 'local' sid. */
+
+ DEBUG(0,("pdb_generate_sam_sid: Mismatched SIDs as a pdc/bdc.\n"));
+ if (!secrets_store_domain_sid(global_myname(), &domain_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Can't re-store domain SID for local sid as PDC/BDC.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ return sam_sid;
+ }
+
+ return sam_sid;
+
+ }
+
+ /* check for an old MACHINE.SID file for backwards compatibility */
+ if (asprintf(&fname, "%s/MACHINE.SID", lp_private_dir()) == -1) {
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+
+ if (read_sid_from_file(fname, sam_sid)) {
+ /* remember it for future reference and unlink the old MACHINE.SID */
+ if (!secrets_store_domain_sid(global_myname(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store SID from file.\n"));
+ SAFE_FREE(fname);
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ unlink(fname);
+ if ( !IS_DC ) {
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store domain SID from file.\n"));
+ SAFE_FREE(fname);
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ }
+
+ /* Stored the old sid from MACHINE.SID successfully.*/
+ SAFE_FREE(fname);
+ return sam_sid;
+ }
+
+ SAFE_FREE(fname);
+
+ /* we don't have the SID in secrets.tdb, we will need to
+ generate one and save it */
+ generate_random_sid(sam_sid);
+
+ if (!secrets_store_domain_sid(global_myname(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store generated machine SID.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ if ( IS_DC ) {
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store generated domain SID.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ }
+
+ return sam_sid;
+}
+
+/* return our global_sam_sid */
+DOM_SID *get_global_sam_sid(void)
+{
+ struct db_context *db;
+
+ if (global_sam_sid != NULL)
+ return global_sam_sid;
+
+ /*
+ * memory for global_sam_sid is allocated in
+ * pdb_generate_sam_sid() as needed
+ *
+ * Note: this is garded by a transaction
+ * to prevent races on startup which
+ * can happen with some dbwrap backends
+ */
+
+ db = secrets_db_ctx();
+ if (!db) {
+ smb_panic("could not open secrets db");
+ }
+
+ if (db->transaction_start(db) != 0) {
+ smb_panic("could not start transaction on secrets db");
+ }
+
+ if (!(global_sam_sid = pdb_generate_sam_sid())) {
+ db->transaction_cancel(db);
+ smb_panic("could not generate a machine SID");
+ }
+
+ if (db->transaction_commit(db) != 0) {
+ smb_panic("could not start commit secrets db");
+ }
+
+ return global_sam_sid;
+}
+
+/**
+ * Force get_global_sam_sid to requery the backends
+ */
+void reset_global_sam_sid(void)
+{
+ SAFE_FREE(global_sam_sid);
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/
+
+bool sid_check_is_domain(const DOM_SID *sid)
+{
+ return sid_equal(sid, get_global_sam_sid());
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/
+
+bool sid_check_is_in_our_domain(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+ return sid_check_is_domain(&dom_sid);
+}
diff --git a/source3/passdb/passdb.c b/source3/passdb/passdb.c
new file mode 100644
index 0000000000..a670b46d69
--- /dev/null
+++ b/source3/passdb/passdb.c
@@ -0,0 +1,1657 @@
+/*
+ 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-2006
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Volker Lendecke 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/******************************************************************
+ get the default domain/netbios name to be used when
+ testing authentication. For example, if you connect
+ to a Windows member server using a bogus domain name, the
+ Windows box will map the BOGUS\user to DOMAIN\user. A
+ standalone box will map to WKS\user.
+******************************************************************/
+
+const char *my_sam_name(void)
+{
+ /* standalone servers can only use the local netbios name */
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return global_myname();
+
+ /* Windows domain members default to the DOMAIN
+ name when not specified */
+ return lp_workgroup();
+}
+
+/**********************************************************************
+***********************************************************************/
+
+static int samu_destroy(struct samu *user)
+{
+ data_blob_clear_free( &user->lm_pw );
+ data_blob_clear_free( &user->nt_pw );
+
+ if ( user->plaintext_pw )
+ memset( user->plaintext_pw, 0x0, strlen(user->plaintext_pw) );
+
+ return 0;
+}
+
+/**********************************************************************
+ generate a new struct samuser
+***********************************************************************/
+
+struct samu *samu_new( TALLOC_CTX *ctx )
+{
+ struct samu *user;
+
+ if ( !(user = TALLOC_ZERO_P( ctx, struct samu )) ) {
+ DEBUG(0,("samuser_new: Talloc failed!\n"));
+ return NULL;
+ }
+
+ talloc_set_destructor( user, samu_destroy );
+
+ /* no initial methods */
+
+ user->methods = NULL;
+
+ /* Don't change these timestamp settings without a good reason.
+ They are important for NT member server compatibility. */
+
+ user->logon_time = (time_t)0;
+ user->pass_last_set_time = (time_t)0;
+ user->pass_can_change_time = (time_t)0;
+ user->logoff_time = get_time_t_max();
+ user->kickoff_time = get_time_t_max();
+ user->pass_must_change_time = get_time_t_max();
+ user->fields_present = 0x00ffffff;
+ user->logon_divs = 168; /* hours per week */
+ user->hours_len = 21; /* 21 times 8 bits = 168 */
+ memset(user->hours, 0xff, user->hours_len); /* available at all hours */
+ user->bad_password_count = 0;
+ user->logon_count = 0;
+ user->unknown_6 = 0x000004ec; /* don't know */
+
+ /* Some parts of samba strlen their pdb_get...() returns,
+ so this keeps the interface unchanged for now. */
+
+ user->username = "";
+ user->domain = "";
+ user->nt_username = "";
+ user->full_name = "";
+ user->home_dir = "";
+ user->logon_script = "";
+ user->profile_path = "";
+ user->acct_desc = "";
+ user->workstations = "";
+ user->comment = "";
+ user->munged_dial = "";
+
+ user->plaintext_pw = NULL;
+
+ /* Unless we know otherwise have a Account Control Bit
+ value of 'normal user'. This helps User Manager, which
+ asks for a filtered list of users. */
+
+ user->acct_ctrl = ACB_NORMAL;
+
+
+ return user;
+}
+
+/*********************************************************************
+ Initialize a struct samu from a struct passwd including the user
+ and group SIDs. The *user structure is filled out with the Unix
+ attributes and a user SID.
+*********************************************************************/
+
+static NTSTATUS samu_set_unix_internal(struct samu *user, const struct passwd *pwd, bool create)
+{
+ const char *guest_account = lp_guestaccount();
+ const char *domain = global_myname();
+ uint32 urid;
+
+ if ( !pwd ) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* Basic properties based upon the Unix account information */
+
+ pdb_set_username(user, pwd->pw_name, PDB_SET);
+ pdb_set_fullname(user, pwd->pw_gecos, PDB_SET);
+ pdb_set_domain (user, get_global_sam_name(), PDB_DEFAULT);
+#if 0
+ /* This can lead to a primary group of S-1-22-2-XX which
+ will be rejected by other parts of the Samba code.
+ Rely on pdb_get_group_sid() to "Do The Right Thing" (TM)
+ --jerry */
+
+ gid_to_sid(&group_sid, pwd->pw_gid);
+ pdb_set_group_sid(user, &group_sid, PDB_SET);
+#endif
+
+ /* save the password structure for later use */
+
+ user->unix_pw = tcopy_passwd( user, pwd );
+
+ /* Special case for the guest account which must have a RID of 501 */
+
+ if ( strequal( pwd->pw_name, guest_account ) ) {
+ if ( !pdb_set_user_sid_from_rid(user, DOMAIN_USER_RID_GUEST, PDB_DEFAULT)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ return NT_STATUS_OK;
+ }
+
+ /* Non-guest accounts...Check for a workstation or user account */
+
+ if (pwd->pw_name[strlen(pwd->pw_name)-1] == '$') {
+ /* workstation */
+
+ if (!pdb_set_acct_ctrl(user, ACB_WSTRUST, PDB_DEFAULT)) {
+ DEBUG(1, ("Failed to set 'workstation account' flags for user %s.\n",
+ pwd->pw_name));
+ return NT_STATUS_INVALID_COMPUTER_NAME;
+ }
+ }
+ else {
+ /* user */
+
+ if (!pdb_set_acct_ctrl(user, ACB_NORMAL, PDB_DEFAULT)) {
+ DEBUG(1, ("Failed to set 'normal account' flags for user %s.\n",
+ pwd->pw_name));
+ return NT_STATUS_INVALID_ACCOUNT_NAME;
+ }
+
+ /* set some basic attributes */
+
+ pdb_set_profile_path(user, talloc_sub_specified(user,
+ lp_logon_path(), pwd->pw_name, domain, pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+ pdb_set_homedir(user, talloc_sub_specified(user,
+ lp_logon_home(), pwd->pw_name, domain, pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+ pdb_set_dir_drive(user, talloc_sub_specified(user,
+ lp_logon_drive(), pwd->pw_name, domain, pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+ pdb_set_logon_script(user, talloc_sub_specified(user,
+ lp_logon_script(), pwd->pw_name, domain, pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+ }
+
+ /* Now deal with the user SID. If we have a backend that can generate
+ RIDs, then do so. But sometimes the caller just wanted a structure
+ initialized and will fill in these fields later (such as from a
+ netr_SamInfo3 structure) */
+
+ if ( create && !pdb_rid_algorithm() ) {
+ uint32 user_rid;
+ DOM_SID user_sid;
+
+ if ( !pdb_new_rid( &user_rid ) ) {
+ DEBUG(3, ("Could not allocate a new RID\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sid_copy( &user_sid, get_global_sam_sid() );
+ sid_append_rid( &user_sid, user_rid );
+
+ if ( !pdb_set_user_sid(user, &user_sid, PDB_SET) ) {
+ DEBUG(3, ("pdb_set_user_sid failed\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ /* generate a SID for the user with the RID algorithm */
+
+ urid = algorithmic_pdb_uid_to_user_rid( user->unix_pw->pw_uid );
+
+ if ( !pdb_set_user_sid_from_rid( user, urid, PDB_SET) ) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Set the Unix user attributes
+********************************************************************/
+
+NTSTATUS samu_set_unix(struct samu *user, const struct passwd *pwd)
+{
+ return samu_set_unix_internal( user, pwd, False );
+}
+
+NTSTATUS samu_alloc_rid_unix(struct samu *user, const struct passwd *pwd)
+{
+ return samu_set_unix_internal( user, pwd, True );
+}
+
+/**********************************************************
+ 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 *pdb_encode_acct_ctrl(uint32 acct_ctrl, size_t length)
+{
+ fstring acct_str;
+ char *result;
+
+ size_t i = 0;
+
+ SMB_ASSERT(length <= sizeof(acct_str));
+
+ 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';
+
+ result = talloc_strdup(talloc_tos(), acct_str);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint32 pdb_decode_acct_ctrl(const char *p)
+{
+ uint32 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 pdb_sethexpwd(char p[33], const unsigned char *pwd, uint32 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", 32);
+ else
+ safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 32);
+ }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+bool pdb_gethexpwd(const char *p, unsigned char *pwd)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p)
+ return (False);
+
+ for (i = 0; i < 32; i += 2) {
+ hinybble = toupper_ascii(p[i]);
+ lonybble = toupper_ascii(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);
+}
+
+/*************************************************************
+ Routine to set 42 hex hours characters from a 21 byte array.
+**************************************************************/
+
+void pdb_sethexhours(char *p, const unsigned char *hours)
+{
+ if (hours != NULL) {
+ int i;
+ for (i = 0; i < 21; i++) {
+ slprintf(&p[i*2], 3, "%02X", hours[i]);
+ }
+ } else {
+ safe_strcpy(p, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 43);
+ }
+}
+
+/*************************************************************
+ Routine to get the 42 hex characters and turn them
+ into a 21 byte array.
+**************************************************************/
+
+bool pdb_gethexhours(const char *p, unsigned char *hours)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p) {
+ return (False);
+ }
+
+ for (i = 0; i < 42; i += 2) {
+ hinybble = toupper_ascii(p[i]);
+ lonybble = toupper_ascii(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);
+
+ hours[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return (True);
+}
+
+/********************************************************************
+********************************************************************/
+
+int algorithmic_rid_base(void)
+{
+ int rid_offset;
+
+ rid_offset = lp_algorithmic_rid_base();
+
+ if (rid_offset < BASE_RID) {
+ /* Try to prevent admin foot-shooting, we can't put algorithmic
+ rids below 1000, that's the 'well known RIDs' on NT */
+ DEBUG(0, ("'algorithmic rid base' must be equal to or above %ld\n", BASE_RID));
+ rid_offset = BASE_RID;
+ }
+ if (rid_offset & 1) {
+ DEBUG(0, ("algorithmic rid base must be even\n"));
+ rid_offset += 1;
+ }
+ return rid_offset;
+}
+
+/*******************************************************************
+ Converts NT user RID to a UNIX uid.
+ ********************************************************************/
+
+uid_t algorithmic_pdb_user_rid_to_uid(uint32 user_rid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (uid_t)(((user_rid & (~USER_RID_TYPE)) - rid_offset)/RID_MULTIPLIER);
+}
+
+uid_t max_algorithmic_uid(void)
+{
+ return algorithmic_pdb_user_rid_to_uid(0xfffffffe);
+}
+
+/*******************************************************************
+ converts UNIX uid to an NT User RID.
+ ********************************************************************/
+
+uint32 algorithmic_pdb_uid_to_user_rid(uid_t uid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (((((uint32)uid)*RID_MULTIPLIER) + rid_offset) | USER_RID_TYPE);
+}
+
+/*******************************************************************
+ Converts NT group RID to a UNIX gid.
+ ********************************************************************/
+
+gid_t pdb_group_rid_to_gid(uint32 group_rid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (gid_t)(((group_rid & (~GROUP_RID_TYPE))- rid_offset)/RID_MULTIPLIER);
+}
+
+gid_t max_algorithmic_gid(void)
+{
+ return pdb_group_rid_to_gid(0xffffffff);
+}
+
+/*******************************************************************
+ converts NT Group RID to a UNIX uid.
+
+ warning: you must not call that function only
+ you must do a call to the group mapping first.
+ there is not anymore a direct link between the gid and the rid.
+ ********************************************************************/
+
+uint32 algorithmic_pdb_gid_to_group_rid(gid_t gid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (((((uint32)gid)*RID_MULTIPLIER) + rid_offset) | GROUP_RID_TYPE);
+}
+
+/*******************************************************************
+ Decides if a RID is a well known RID.
+ ********************************************************************/
+
+static bool rid_is_well_known(uint32 rid)
+{
+ /* Not using rid_offset here, because this is the actual
+ NT fixed value (1000) */
+
+ return (rid < BASE_RID);
+}
+
+/*******************************************************************
+ Decides if a RID is a user or group RID.
+ ********************************************************************/
+
+bool algorithmic_pdb_rid_is_user(uint32 rid)
+{
+ if ( rid_is_well_known(rid) ) {
+ /*
+ * The only well known user RIDs are DOMAIN_USER_RID_ADMIN
+ * and DOMAIN_USER_RID_GUEST.
+ */
+ if(rid == DOMAIN_USER_RID_ADMIN || rid == DOMAIN_USER_RID_GUEST)
+ return True;
+ } else if((rid & RID_TYPE_MASK) == USER_RID_TYPE) {
+ return True;
+ }
+ return False;
+}
+
+/*******************************************************************
+ Convert a name into a SID. Used in the lookup name rpc.
+ ********************************************************************/
+
+bool lookup_global_sam_name(const char *name, int flags, uint32_t *rid,
+ enum lsa_SidType *type)
+{
+ GROUP_MAP map;
+ bool ret;
+
+ /* Windows treats "MACHINE\None" as a special name for
+ rid 513 on non-DCs. You cannot create a user or group
+ name "None" on Windows. You will get an error that
+ the group already exists. */
+
+ if ( strequal( name, "None" ) ) {
+ *rid = DOMAIN_GROUP_RID_USERS;
+ *type = SID_NAME_DOM_GRP;
+
+ return True;
+ }
+
+ /* LOOKUP_NAME_GROUP is a hack to allow valid users = @foo to work
+ * correctly in the case where foo also exists as a user. If the flag
+ * is set, don't look for users at all. */
+
+ if ((flags & LOOKUP_NAME_GROUP) == 0) {
+ struct samu *sam_account = NULL;
+ DOM_SID user_sid;
+
+ if ( !(sam_account = samu_new( NULL )) ) {
+ return False;
+ }
+
+ become_root();
+ ret = pdb_getsampwnam(sam_account, name);
+ unbecome_root();
+
+ if (ret) {
+ sid_copy(&user_sid, pdb_get_user_sid(sam_account));
+ }
+
+ TALLOC_FREE(sam_account);
+
+ if (ret) {
+ if (!sid_check_is_in_our_domain(&user_sid)) {
+ DEBUG(0, ("User %s with invalid SID %s in passdb\n",
+ name, sid_string_dbg(&user_sid)));
+ return False;
+ }
+
+ sid_peek_rid(&user_sid, rid);
+ *type = SID_NAME_USER;
+ return True;
+ }
+ }
+
+ /*
+ * Maybe it is a group ?
+ */
+
+ become_root();
+ ret = pdb_getgrnam(&map, name);
+ unbecome_root();
+
+ if (!ret) {
+ return False;
+ }
+
+ /* BUILTIN groups are looked up elsewhere */
+ if (!sid_check_is_in_our_domain(&map.sid)) {
+ DEBUG(10, ("Found group %s (%s) not in our domain -- "
+ "ignoring.", name, sid_string_dbg(&map.sid)));
+ return False;
+ }
+
+ /* yes it's a mapped group */
+ sid_peek_rid(&map.sid, rid);
+ *type = map.sid_name_use;
+ return True;
+}
+
+/*************************************************************
+ Change a password entry in the local smbpasswd file.
+ *************************************************************/
+
+NTSTATUS local_password_change(const char *user_name,
+ int local_flags,
+ const char *new_passwd,
+ char **pp_err_str,
+ char **pp_msg_str)
+{
+ struct samu *sam_pass=NULL;
+ uint32 other_acb;
+ NTSTATUS result;
+
+ *pp_err_str = NULL;
+ *pp_msg_str = NULL;
+
+ /* Get the smb passwd entry for this user */
+
+ if ( !(sam_pass = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ if(!pdb_getsampwnam(sam_pass, user_name)) {
+ unbecome_root();
+ TALLOC_FREE(sam_pass);
+
+ if ((local_flags & LOCAL_ADD_USER) || (local_flags & LOCAL_DELETE_USER)) {
+ int tmp_debug = DEBUGLEVEL;
+ struct passwd *pwd;
+
+ /* Might not exist in /etc/passwd. */
+
+ if (tmp_debug < 1) {
+ DEBUGLEVEL = 1;
+ }
+
+ if ( !(pwd = getpwnam_alloc( NULL, user_name)) ) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* create the struct samu and initialize the basic Unix properties */
+
+ if ( !(sam_pass = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result = samu_set_unix( sam_pass, pwd );
+
+ DEBUGLEVEL = tmp_debug;
+
+ TALLOC_FREE( pwd );
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PRIMARY_GROUP)) {
+ return result;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ asprintf(pp_err_str, "Failed to " "initialize account for user %s: %s\n",
+ user_name, nt_errstr(result));
+ return result;
+ }
+ } else {
+ asprintf(pp_err_str, "Failed to find entry for user %s.\n", user_name);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ } else {
+ unbecome_root();
+ /* the entry already existed */
+ local_flags &= ~LOCAL_ADD_USER;
+ }
+
+ /* the 'other' acb bits not being changed here */
+ other_acb = (pdb_get_acct_ctrl(sam_pass) & (~(ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST|ACB_NORMAL)));
+ if (local_flags & LOCAL_TRUST_ACCOUNT) {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_WSTRUST | other_acb, PDB_CHANGED) ) {
+ asprintf(pp_err_str, "Failed to set 'trusted workstation account' flags for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_DOMTRUST | other_acb, PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to set 'domain trust account' flags for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_NORMAL | other_acb, PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to set 'normal account' flags for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /*
+ * We are root - just write the new password
+ * and the valid last change time.
+ */
+
+ if (local_flags & LOCAL_DISABLE_USER) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_DISABLED, PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to set 'disabled' flag for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (local_flags & LOCAL_ENABLE_USER) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (local_flags & LOCAL_SET_NO_PASSWORD) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_PWNOTREQ, PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to set 'no password required' flag for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (local_flags & LOCAL_SET_PASSWORD) {
+ /*
+ * If we're dealing with setting a completely empty user account
+ * ie. One with a password of 'XXXX', but not set disabled (like
+ * an account created from scratch) then if the old password was
+ * 'XX's then getsmbpwent will have set the ACB_DISABLED flag.
+ * We remove that as we're giving this user their first password
+ * and the decision hasn't really been made to disable them (ie.
+ * don't create them disabled). JRA.
+ */
+ if ((pdb_get_lanman_passwd(sam_pass)==NULL) && (pdb_get_acct_ctrl(sam_pass)&ACB_DISABLED)) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_PWNOTREQ), PDB_CHANGED)) {
+ asprintf(pp_err_str, "Failed to unset 'no password required' flag for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_plaintext_passwd (sam_pass, new_passwd)) {
+ asprintf(pp_err_str, "Failed to set password for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ if (NT_STATUS_IS_OK(pdb_add_sam_account(sam_pass))) {
+ asprintf(pp_msg_str, "Added user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_OK;
+ } else {
+ asprintf(pp_err_str, "Failed to add entry for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (local_flags & LOCAL_DELETE_USER) {
+ if (!NT_STATUS_IS_OK(pdb_delete_sam_account(sam_pass))) {
+ asprintf(pp_err_str, "Failed to delete entry for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ asprintf(pp_msg_str, "Deleted user %s.\n", user_name);
+ } else {
+ result = pdb_update_sam_account(sam_pass);
+ if(!NT_STATUS_IS_OK(result)) {
+ asprintf(pp_err_str, "Failed to modify entry for user %s.\n", user_name);
+ TALLOC_FREE(sam_pass);
+ return result;
+ }
+ if(local_flags & LOCAL_DISABLE_USER)
+ asprintf(pp_msg_str, "Disabled user %s.\n", user_name);
+ else if (local_flags & LOCAL_ENABLE_USER)
+ asprintf(pp_msg_str, "Enabled user %s.\n", user_name);
+ else if (local_flags & LOCAL_SET_NO_PASSWORD)
+ asprintf(pp_msg_str, "User %s password set to none.\n", user_name);
+ }
+
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Marshall/unmarshall struct samu structs.
+ *********************************************************************/
+
+#define TDB_FORMAT_STRING_V3 "dddddddBBBBBBBBBBBBddBBBdwdBwwd"
+
+/*********************************************************************
+*********************************************************************/
+
+bool init_sam_from_buffer_v3(struct samu *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username = NULL;
+ char *domain = NULL;
+ char *nt_username = NULL;
+ char *dir_drive = NULL;
+ char *unknown_str = NULL;
+ char *munged_dial = NULL;
+ char *fullname = NULL;
+ char *homedir = NULL;
+ char *logon_script = NULL;
+ char *profile_path = NULL;
+ char *acct_desc = NULL;
+ char *workstations = NULL;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, hours_len, unknown_6, acct_ctrl;
+ uint16 logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours = NULL;
+ uint8 *lm_pw_ptr = NULL, *nt_pw_ptr = NULL, *nt_pw_hist_ptr = NULL;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, nt_pw_hist_len, hourslen;
+ uint32 pwHistLen = 0;
+ bool ret = True;
+ fstring tmp_string;
+ bool expand_explicit = lp_passdb_expand_explicit();
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer_v3: NULL parameters found!\n"));
+ return False;
+ }
+
+/* TDB_FORMAT_STRING_V3 "dddddddBBBBBBBBBBBBddBBBdwdBwwd" */
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING_V3,
+ &logon_time, /* d */
+ &logoff_time, /* d */
+ &kickoff_time, /* d */
+ &bad_password_time, /* d */
+ &pass_last_set_time, /* d */
+ &pass_can_change_time, /* d */
+ &pass_must_change_time, /* d */
+ &username_len, &username, /* B */
+ &domain_len, &domain, /* B */
+ &nt_username_len, &nt_username, /* B */
+ &fullname_len, &fullname, /* B */
+ &homedir_len, &homedir, /* B */
+ &dir_drive_len, &dir_drive, /* B */
+ &logon_script_len, &logon_script, /* B */
+ &profile_path_len, &profile_path, /* B */
+ &acct_desc_len, &acct_desc, /* B */
+ &workstations_len, &workstations, /* B */
+ &unknown_str_len, &unknown_str, /* B */
+ &munged_dial_len, &munged_dial, /* B */
+ &user_rid, /* d */
+ &group_rid, /* d */
+ &lm_pw_len, &lm_pw_ptr, /* B */
+ &nt_pw_len, &nt_pw_ptr, /* B */
+ /* Change from V1 is addition of password history field. */
+ &nt_pw_hist_len, &nt_pw_hist_ptr, /* B */
+ /* Change from V2 is the uint32 acb_mask */
+ &acct_ctrl, /* d */
+ /* Also "remove_me" field was removed. */
+ &logon_divs, /* w */
+ &hours_len, /* d */
+ &hourslen, &hours, /* B */
+ &bad_password_count, /* w */
+ &logon_count, /* w */
+ &unknown_6); /* d */
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, convert_uint32_to_time_t(logon_time), PDB_SET);
+ pdb_set_logoff_time(sampass, convert_uint32_to_time_t(logoff_time), PDB_SET);
+ pdb_set_kickoff_time(sampass, convert_uint32_to_time_t(kickoff_time), PDB_SET);
+ pdb_set_bad_password_time(sampass, convert_uint32_to_time_t(bad_password_time), PDB_SET);
+ pdb_set_pass_can_change_time(sampass, convert_uint32_to_time_t(pass_can_change_time), PDB_SET);
+ pdb_set_pass_must_change_time(sampass, convert_uint32_to_time_t(pass_must_change_time), PDB_SET);
+ pdb_set_pass_last_set_time(sampass, convert_uint32_to_time_t(pass_last_set_time), PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ fstrcpy( tmp_string, homedir );
+ if (expand_explicit) {
+ standard_sub_basic( username, domain, tmp_string,
+ sizeof(tmp_string) );
+ }
+ pdb_set_homedir(sampass, tmp_string, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else
+ pdb_set_dir_drive(sampass, lp_logon_drive(), PDB_DEFAULT );
+
+ if (logon_script) {
+ fstrcpy( tmp_string, logon_script );
+ if (expand_explicit) {
+ standard_sub_basic( username, domain, tmp_string,
+ sizeof(tmp_string) );
+ }
+ pdb_set_logon_script(sampass, tmp_string, PDB_SET);
+ }
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ fstrcpy( tmp_string, profile_path );
+ if (expand_explicit) {
+ standard_sub_basic( username, domain, tmp_string,
+ sizeof(tmp_string) );
+ }
+ pdb_set_profile_path(sampass, tmp_string, PDB_SET);
+ }
+ else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass, username, domain, lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHistLen);
+ if (pwHistLen) {
+ uint8 *pw_hist = (uint8 *)SMB_MALLOC(pwHistLen * PW_HISTORY_ENTRY_LEN);
+ if (!pw_hist) {
+ ret = False;
+ goto done;
+ }
+ memset(pw_hist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
+ if (nt_pw_hist_ptr && nt_pw_hist_len) {
+ int i;
+ SMB_ASSERT((nt_pw_hist_len % PW_HISTORY_ENTRY_LEN) == 0);
+ nt_pw_hist_len /= PW_HISTORY_ENTRY_LEN;
+ for (i = 0; (i < pwHistLen) && (i < nt_pw_hist_len); i++) {
+ memcpy(&pw_hist[i*PW_HISTORY_ENTRY_LEN],
+ &nt_pw_hist_ptr[i*PW_HISTORY_ENTRY_LEN],
+ PW_HISTORY_ENTRY_LEN);
+ }
+ }
+ if (!pdb_set_pw_history(sampass, pw_hist, pwHistLen, PDB_SET)) {
+ SAFE_FREE(pw_hist);
+ ret = False;
+ goto done;
+ }
+ SAFE_FREE(pw_hist);
+ } else {
+ pdb_set_pw_history(sampass, NULL, 0, PDB_SET);
+ }
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ /* Change from V2 is the uint32 acct_ctrl */
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(lm_pw_ptr);
+ SAFE_FREE(nt_pw_ptr);
+ SAFE_FREE(nt_pw_hist_ptr);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+uint32 init_buffer_from_sam_v3 (uint8 **buf, struct samu *sampass, bool size_only)
+{
+ size_t len, buflen;
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+
+ uint32 user_rid, group_rid;
+
+ const char *username;
+ const char *domain;
+ const char *nt_username;
+ const char *dir_drive;
+ const char *unknown_str;
+ const char *munged_dial;
+ const char *fullname;
+ const char *homedir;
+ const char *logon_script;
+ const char *profile_path;
+ const char *acct_desc;
+ const char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ const uint8 *lm_pw;
+ const uint8 *nt_pw;
+ const uint8 *nt_pw_hist;
+ uint32 lm_pw_len = 16;
+ uint32 nt_pw_len = 16;
+ uint32 nt_pw_hist_len;
+ uint32 pwHistLen = 0;
+
+ *buf = NULL;
+ buflen = 0;
+
+ logon_time = convert_time_t_to_uint32(pdb_get_logon_time(sampass));
+ logoff_time = convert_time_t_to_uint32(pdb_get_logoff_time(sampass));
+ kickoff_time = convert_time_t_to_uint32(pdb_get_kickoff_time(sampass));
+ bad_password_time = convert_time_t_to_uint32(pdb_get_bad_password_time(sampass));
+ pass_can_change_time = convert_time_t_to_uint32(pdb_get_pass_can_change_time_noncalc(sampass));
+ pass_must_change_time = convert_time_t_to_uint32(pdb_get_pass_must_change_time(sampass));
+ pass_last_set_time = convert_time_t_to_uint32(pdb_get_pass_last_set_time(sampass));
+
+ user_rid = pdb_get_user_rid(sampass);
+ group_rid = pdb_get_group_rid(sampass);
+
+ username = pdb_get_username(sampass);
+ if (username) {
+ username_len = strlen(username) +1;
+ } else {
+ username_len = 0;
+ }
+
+ domain = pdb_get_domain(sampass);
+ if (domain) {
+ domain_len = strlen(domain) +1;
+ } else {
+ domain_len = 0;
+ }
+
+ nt_username = pdb_get_nt_username(sampass);
+ if (nt_username) {
+ nt_username_len = strlen(nt_username) +1;
+ } else {
+ nt_username_len = 0;
+ }
+
+ fullname = pdb_get_fullname(sampass);
+ if (fullname) {
+ fullname_len = strlen(fullname) +1;
+ } else {
+ fullname_len = 0;
+ }
+
+ /*
+ * Only updates fields which have been set (not defaults from smb.conf)
+ */
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_DRIVE)) {
+ dir_drive = pdb_get_dir_drive(sampass);
+ } else {
+ dir_drive = NULL;
+ }
+ if (dir_drive) {
+ dir_drive_len = strlen(dir_drive) +1;
+ } else {
+ dir_drive_len = 0;
+ }
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_SMBHOME)) {
+ homedir = pdb_get_homedir(sampass);
+ } else {
+ homedir = NULL;
+ }
+ if (homedir) {
+ homedir_len = strlen(homedir) +1;
+ } else {
+ homedir_len = 0;
+ }
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_LOGONSCRIPT)) {
+ logon_script = pdb_get_logon_script(sampass);
+ } else {
+ logon_script = NULL;
+ }
+ if (logon_script) {
+ logon_script_len = strlen(logon_script) +1;
+ } else {
+ logon_script_len = 0;
+ }
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_PROFILE)) {
+ profile_path = pdb_get_profile_path(sampass);
+ } else {
+ profile_path = NULL;
+ }
+ if (profile_path) {
+ profile_path_len = strlen(profile_path) +1;
+ } else {
+ profile_path_len = 0;
+ }
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ if (!lm_pw) {
+ lm_pw_len = 0;
+ }
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+ if (!nt_pw) {
+ nt_pw_len = 0;
+ }
+
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHistLen);
+ nt_pw_hist = pdb_get_pw_history(sampass, &nt_pw_hist_len);
+ if (pwHistLen && nt_pw_hist && nt_pw_hist_len) {
+ nt_pw_hist_len *= PW_HISTORY_ENTRY_LEN;
+ } else {
+ nt_pw_hist_len = 0;
+ }
+
+ acct_desc = pdb_get_acct_desc(sampass);
+ if (acct_desc) {
+ acct_desc_len = strlen(acct_desc) +1;
+ } else {
+ acct_desc_len = 0;
+ }
+
+ workstations = pdb_get_workstations(sampass);
+ if (workstations) {
+ workstations_len = strlen(workstations) +1;
+ } else {
+ workstations_len = 0;
+ }
+
+ unknown_str = NULL;
+ unknown_str_len = 0;
+
+ munged_dial = pdb_get_munged_dial(sampass);
+ if (munged_dial) {
+ munged_dial_len = strlen(munged_dial) +1;
+ } else {
+ munged_dial_len = 0;
+ }
+
+/* TDB_FORMAT_STRING_V3 "dddddddBBBBBBBBBBBBddBBBdwdBwwd" */
+
+ /* one time to get the size needed */
+ len = tdb_pack(NULL, 0, TDB_FORMAT_STRING_V3,
+ logon_time, /* d */
+ logoff_time, /* d */
+ kickoff_time, /* d */
+ bad_password_time, /* d */
+ pass_last_set_time, /* d */
+ pass_can_change_time, /* d */
+ pass_must_change_time, /* d */
+ username_len, username, /* B */
+ domain_len, domain, /* B */
+ nt_username_len, nt_username, /* B */
+ fullname_len, fullname, /* B */
+ homedir_len, homedir, /* B */
+ dir_drive_len, dir_drive, /* B */
+ logon_script_len, logon_script, /* B */
+ profile_path_len, profile_path, /* B */
+ acct_desc_len, acct_desc, /* B */
+ workstations_len, workstations, /* B */
+ unknown_str_len, unknown_str, /* B */
+ munged_dial_len, munged_dial, /* B */
+ user_rid, /* d */
+ group_rid, /* d */
+ lm_pw_len, lm_pw, /* B */
+ nt_pw_len, nt_pw, /* B */
+ nt_pw_hist_len, nt_pw_hist, /* B */
+ pdb_get_acct_ctrl(sampass), /* d */
+ pdb_get_logon_divs(sampass), /* w */
+ pdb_get_hours_len(sampass), /* d */
+ MAX_HOURS_LEN, pdb_get_hours(sampass), /* B */
+ pdb_get_bad_password_count(sampass), /* w */
+ pdb_get_logon_count(sampass), /* w */
+ pdb_get_unknown_6(sampass)); /* d */
+
+ if (size_only) {
+ return buflen;
+ }
+
+ /* malloc the space needed */
+ if ( (*buf=(uint8*)SMB_MALLOC(len)) == NULL) {
+ DEBUG(0,("init_buffer_from_sam_v3: Unable to malloc() memory for buffer!\n"));
+ return (-1);
+ }
+
+ /* now for the real call to tdb_pack() */
+ buflen = tdb_pack(*buf, len, TDB_FORMAT_STRING_V3,
+ logon_time, /* d */
+ logoff_time, /* d */
+ kickoff_time, /* d */
+ bad_password_time, /* d */
+ pass_last_set_time, /* d */
+ pass_can_change_time, /* d */
+ pass_must_change_time, /* d */
+ username_len, username, /* B */
+ domain_len, domain, /* B */
+ nt_username_len, nt_username, /* B */
+ fullname_len, fullname, /* B */
+ homedir_len, homedir, /* B */
+ dir_drive_len, dir_drive, /* B */
+ logon_script_len, logon_script, /* B */
+ profile_path_len, profile_path, /* B */
+ acct_desc_len, acct_desc, /* B */
+ workstations_len, workstations, /* B */
+ unknown_str_len, unknown_str, /* B */
+ munged_dial_len, munged_dial, /* B */
+ user_rid, /* d */
+ group_rid, /* d */
+ lm_pw_len, lm_pw, /* B */
+ nt_pw_len, nt_pw, /* B */
+ nt_pw_hist_len, nt_pw_hist, /* B */
+ pdb_get_acct_ctrl(sampass), /* d */
+ pdb_get_logon_divs(sampass), /* w */
+ pdb_get_hours_len(sampass), /* d */
+ MAX_HOURS_LEN, pdb_get_hours(sampass), /* B */
+ pdb_get_bad_password_count(sampass), /* w */
+ pdb_get_logon_count(sampass), /* w */
+ pdb_get_unknown_6(sampass)); /* d */
+
+ /* check to make sure we got it correct */
+ if (buflen != len) {
+ DEBUG(0, ("init_buffer_from_sam_v3: somthing odd is going on here: bufflen (%lu) != len (%lu) in tdb_pack operations!\n",
+ (unsigned long)buflen, (unsigned long)len));
+ /* error */
+ SAFE_FREE (*buf);
+ return (-1);
+ }
+
+ return (buflen);
+}
+
+
+/*********************************************************************
+*********************************************************************/
+
+bool pdb_copy_sam_account(struct samu *dst, struct samu *src )
+{
+ uint8 *buf = NULL;
+ int len;
+
+ len = init_buffer_from_sam_v3(&buf, src, False);
+ if (len == -1 || !buf) {
+ SAFE_FREE(buf);
+ return False;
+ }
+
+ if (!init_sam_from_buffer_v3( dst, buf, len )) {
+ free(buf);
+ return False;
+ }
+
+ dst->methods = src->methods;
+
+ if ( src->unix_pw ) {
+ dst->unix_pw = tcopy_passwd( dst, src->unix_pw );
+ if (!dst->unix_pw) {
+ free(buf);
+ return False;
+ }
+ }
+
+ free(buf);
+ return True;
+}
+
+/*********************************************************************
+ Update the bad password count checking the AP_RESET_COUNT_TIME
+*********************************************************************/
+
+bool pdb_update_bad_password_count(struct samu *sampass, bool *updated)
+{
+ time_t LastBadPassword;
+ uint16 BadPasswordCount;
+ uint32 resettime;
+ bool res;
+
+ BadPasswordCount = pdb_get_bad_password_count(sampass);
+ if (!BadPasswordCount) {
+ DEBUG(9, ("No bad password attempts.\n"));
+ return True;
+ }
+
+ become_root();
+ res = pdb_get_account_policy(AP_RESET_COUNT_TIME, &resettime);
+ unbecome_root();
+
+ if (!res) {
+ DEBUG(0, ("pdb_update_bad_password_count: pdb_get_account_policy failed.\n"));
+ return False;
+ }
+
+ /* First, check if there is a reset time to compare */
+ if ((resettime == (uint32) -1) || (resettime == 0)) {
+ DEBUG(9, ("No reset time, can't reset bad pw count\n"));
+ return True;
+ }
+
+ LastBadPassword = pdb_get_bad_password_time(sampass);
+ DEBUG(7, ("LastBadPassword=%d, resettime=%d, current time=%d.\n",
+ (uint32) LastBadPassword, resettime, (uint32)time(NULL)));
+ if (time(NULL) > (LastBadPassword + convert_uint32_to_time_t(resettime)*60)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ if (updated) {
+ *updated = True;
+ }
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Update the ACB_AUTOLOCK flag checking the AP_LOCK_ACCOUNT_DURATION
+*********************************************************************/
+
+bool pdb_update_autolock_flag(struct samu *sampass, bool *updated)
+{
+ uint32 duration;
+ time_t LastBadPassword;
+ bool res;
+
+ if (!(pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK)) {
+ DEBUG(9, ("pdb_update_autolock_flag: Account %s not autolocked, no check needed\n",
+ pdb_get_username(sampass)));
+ return True;
+ }
+
+ become_root();
+ res = pdb_get_account_policy(AP_LOCK_ACCOUNT_DURATION, &duration);
+ unbecome_root();
+
+ if (!res) {
+ DEBUG(0, ("pdb_update_autolock_flag: pdb_get_account_policy failed.\n"));
+ return False;
+ }
+
+ /* First, check if there is a duration to compare */
+ if ((duration == (uint32) -1) || (duration == 0)) {
+ DEBUG(9, ("pdb_update_autolock_flag: No reset duration, can't reset autolock\n"));
+ return True;
+ }
+
+ LastBadPassword = pdb_get_bad_password_time(sampass);
+ DEBUG(7, ("pdb_update_autolock_flag: Account %s, LastBadPassword=%d, duration=%d, current time =%d.\n",
+ pdb_get_username(sampass), (uint32)LastBadPassword, duration*60, (uint32)time(NULL)));
+
+ if (LastBadPassword == (time_t)0) {
+ DEBUG(1,("pdb_update_autolock_flag: Account %s "
+ "administratively locked out with no bad password "
+ "time. Leaving locked out.\n",
+ pdb_get_username(sampass) ));
+ return True;
+ }
+
+ if ((time(NULL) > (LastBadPassword + convert_uint32_to_time_t(duration) * 60))) {
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) & ~ACB_AUTOLOCK,
+ PDB_CHANGED);
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ if (updated) {
+ *updated = True;
+ }
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Increment the bad_password_count
+*********************************************************************/
+
+bool pdb_increment_bad_password_count(struct samu *sampass)
+{
+ uint32 account_policy_lockout;
+ bool autolock_updated = False, badpw_updated = False;
+ bool ret;
+
+ /* Retrieve the account lockout policy */
+ become_root();
+ ret = pdb_get_account_policy(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_lockout);
+ unbecome_root();
+ if ( !ret ) {
+ DEBUG(0, ("pdb_increment_bad_password_count: pdb_get_account_policy failed.\n"));
+ return False;
+ }
+
+ /* If there is no policy, we don't need to continue checking */
+ if (!account_policy_lockout) {
+ DEBUG(9, ("No lockout policy, don't track bad passwords\n"));
+ return True;
+ }
+
+ /* Check if the autolock needs to be cleared */
+ if (!pdb_update_autolock_flag(sampass, &autolock_updated))
+ return False;
+
+ /* Check if the badpw count needs to be reset */
+ if (!pdb_update_bad_password_count(sampass, &badpw_updated))
+ return False;
+
+ /*
+ Ok, now we can assume that any resetting that needs to be
+ done has been done, and just get on with incrementing
+ and autolocking if necessary
+ */
+
+ pdb_set_bad_password_count(sampass,
+ pdb_get_bad_password_count(sampass)+1,
+ PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, time(NULL), PDB_CHANGED);
+
+
+ if (pdb_get_bad_password_count(sampass) < account_policy_lockout)
+ return True;
+
+ if (!pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) | ACB_AUTOLOCK,
+ PDB_CHANGED)) {
+ DEBUG(1, ("pdb_increment_bad_password_count:failed to set 'autolock' flag. \n"));
+ return False;
+ }
+
+ return True;
+}
+
+bool is_dc_trusted_domain_situation(const char *domain_name)
+{
+ return IS_DC && !strequal(domain_name, lp_workgroup());
+}
+
+/*******************************************************************
+ Wrapper around retrieving the clear text trust account password.
+ appropriate account name is stored in account_name.
+ Caller must free password, but not account_name.
+*******************************************************************/
+
+bool get_trust_pw_clear(const char *domain, char **ret_pwd,
+ const char **account_name, uint32 *channel)
+{
+ char *pwd;
+ time_t last_set_time;
+
+ /* if we are a DC and this is not our domain, then lookup an account
+ * for the domain trust */
+
+ if (is_dc_trusted_domain_situation(domain)) {
+ if (!lp_allow_trusted_domains()) {
+ return false;
+ }
+
+ if (!pdb_get_trusteddom_pw(domain, ret_pwd, NULL,
+ &last_set_time))
+ {
+ DEBUG(0, ("get_trust_pw: could not fetch trust "
+ "account password for trusted domain %s\n",
+ domain));
+ return false;
+ }
+
+ if (channel != NULL) {
+ *channel = SEC_CHAN_DOMAIN;
+ }
+
+ if (account_name != NULL) {
+ *account_name = lp_workgroup();
+ }
+
+ return true;
+ }
+
+ /*
+ * Since we can only be member of one single domain, we are now
+ * in a member situation:
+ *
+ * - Either we are a DC (selfjoined) and the domain is our
+ * own domain.
+ * - Or we are on a member and the domain is our own or some
+ * other (potentially trusted) domain.
+ *
+ * In both cases, we can only get the machine account password
+ * for our own domain to connect to our own dc. (For a member,
+ * request to trusted domains are performed through our dc.)
+ *
+ * So we simply use our own domain name to retrieve the
+ * machine account passowrd and ignore the request domain here.
+ */
+
+ pwd = secrets_fetch_machine_password(lp_workgroup(), &last_set_time, channel);
+
+ if (pwd != NULL) {
+ *ret_pwd = pwd;
+ if (account_name != NULL) {
+ *account_name = global_myname();
+ }
+
+ return true;
+ }
+
+ DEBUG(5, ("get_trust_pw_clear: could not fetch clear text trust "
+ "account password for domain %s\n", domain));
+ return false;
+}
+
+/*******************************************************************
+ Wrapper around retrieving the trust account password.
+ appropriate account name is stored in account_name.
+*******************************************************************/
+
+bool get_trust_pw_hash(const char *domain, uint8 ret_pwd[16],
+ const char **account_name, uint32 *channel)
+{
+ char *pwd = NULL;
+ time_t last_set_time;
+
+ if (get_trust_pw_clear(domain, &pwd, account_name, channel)) {
+ E_md4hash(pwd, ret_pwd);
+ SAFE_FREE(pwd);
+ return true;
+ } else if (is_dc_trusted_domain_situation(domain)) {
+ return false;
+ }
+
+ /* as a fallback, try to get the hashed pwd directly from the tdb... */
+
+ if (secrets_fetch_trust_account_password_legacy(domain, ret_pwd,
+ &last_set_time,
+ channel))
+ {
+ if (account_name != NULL) {
+ *account_name = global_myname();
+ }
+
+ return true;
+ }
+
+ DEBUG(5, ("get_trust_pw_hash: could not fetch trust account "
+ "password for domain %s\n", domain));
+ return False;
+}
+
+struct samr_LogonHours get_logon_hours_from_pdb(TALLOC_CTX *mem_ctx,
+ struct samu *pw)
+{
+ struct samr_LogonHours hours;
+ const int units_per_week = 168;
+
+ ZERO_STRUCT(hours);
+ hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
+ if (!hours.bits) {
+ return hours;
+ }
+
+ hours.units_per_week = units_per_week;
+ memset(hours.bits, 0xFF, units_per_week);
+
+ if (pdb_get_hours(pw)) {
+ memcpy(hours.bits, pdb_get_hours(pw),
+ MIN(pdb_get_hours_len(pw), units_per_week));
+ }
+
+ return hours;
+}
+
diff --git a/source3/passdb/pdb_compat.c b/source3/passdb/pdb_compat.c
new file mode 100644
index 0000000000..9967eb53ad
--- /dev/null
+++ b/source3/passdb/pdb_compat.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+ struct samu access routines
+ 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
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+uint32 pdb_get_user_rid (const struct samu *sampass)
+{
+ uint32 u_rid;
+
+ if (sampass)
+ if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_user_sid(sampass),&u_rid))
+ return u_rid;
+
+ return (0);
+}
+
+uint32 pdb_get_group_rid (struct samu *sampass)
+{
+ uint32 g_rid;
+
+ if (sampass)
+ if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_group_sid(sampass),&g_rid))
+ return g_rid;
+ return (0);
+}
+
+bool pdb_set_user_sid_from_rid (struct samu *sampass, uint32 rid, enum pdb_value_state flag)
+{
+ DOM_SID u_sid;
+ const DOM_SID *global_sam_sid;
+
+ if (!sampass)
+ return False;
+
+ if (!(global_sam_sid = get_global_sam_sid())) {
+ DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+ return False;
+ }
+
+ sid_copy(&u_sid, global_sam_sid);
+
+ if (!sid_append_rid(&u_sid, rid))
+ return False;
+
+ if (!pdb_set_user_sid(sampass, &u_sid, flag))
+ return False;
+
+ DEBUG(10, ("pdb_set_user_sid_from_rid:\n\tsetting user sid %s from rid %d\n",
+ sid_string_dbg(&u_sid),rid));
+
+ return True;
+}
+
+bool pdb_set_group_sid_from_rid (struct samu *sampass, uint32 grid, enum pdb_value_state flag)
+{
+ DOM_SID g_sid;
+ const DOM_SID *global_sam_sid;
+
+ if (!sampass)
+ return False;
+
+ if (!(global_sam_sid = get_global_sam_sid())) {
+ DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+ return False;
+ }
+
+ sid_copy(&g_sid, global_sam_sid);
+
+ if (!sid_append_rid(&g_sid, grid))
+ return False;
+
+ if (!pdb_set_group_sid(sampass, &g_sid, flag))
+ return False;
+
+ DEBUG(10, ("pdb_set_group_sid_from_rid:\n\tsetting group sid %s from rid %d\n",
+ sid_string_dbg(&g_sid), grid));
+
+ return True;
+}
+
diff --git a/source3/passdb/pdb_get_set.c b/source3/passdb/pdb_get_set.c
new file mode 100644
index 0000000000..7a8086c63e
--- /dev/null
+++ b/source3/passdb/pdb_get_set.c
@@ -0,0 +1,1081 @@
+/*
+ Unix SMB/CIFS implementation.
+ struct samu access routines
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2006
+ Copyright (C) Andrew Bartlett 2001-2002
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/**
+ * @todo Redefine this to NULL, but this changes the API because
+ * much of samba assumes that the pdb_get...() funtions
+ * return strings. (ie not null-pointers).
+ * See also pdb_fill_default_sam().
+ */
+
+#define PDB_NOT_QUITE_NULL ""
+
+/*********************************************************************
+ Collection of get...() functions for struct samu.
+ ********************************************************************/
+
+uint32 pdb_get_acct_ctrl(const struct samu *sampass)
+{
+ return sampass->acct_ctrl;
+}
+
+time_t pdb_get_logon_time(const struct samu *sampass)
+{
+ return sampass->logon_time;
+}
+
+time_t pdb_get_logoff_time(const struct samu *sampass)
+{
+ return sampass->logoff_time;
+}
+
+time_t pdb_get_kickoff_time(const struct samu *sampass)
+{
+ return sampass->kickoff_time;
+}
+
+time_t pdb_get_bad_password_time(const struct samu *sampass)
+{
+ return sampass->bad_password_time;
+}
+
+time_t pdb_get_pass_last_set_time(const struct samu *sampass)
+{
+ return sampass->pass_last_set_time;
+}
+
+time_t pdb_get_pass_can_change_time(const struct samu *sampass)
+{
+ uint32 allow;
+
+ /* if the last set time is zero, it means the user cannot
+ change their password, and this time must be zero. jmcd
+ */
+ if (sampass->pass_last_set_time == 0)
+ return (time_t) 0;
+
+ /* if the time is max, and the field has been changed,
+ we're trying to update this real value from the sampass
+ to indicate that the user cannot change their password. jmcd
+ */
+ if (sampass->pass_can_change_time == get_time_t_max() &&
+ pdb_get_init_flags(sampass, PDB_CANCHANGETIME) == PDB_CHANGED)
+ return sampass->pass_can_change_time;
+
+ if (!pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &allow))
+ allow = 0;
+
+ /* in normal cases, just calculate it from policy */
+ return sampass->pass_last_set_time + allow;
+}
+
+/* we need this for loading from the backend, so that we don't overwrite
+ non-changed max times, otherwise the pass_can_change checking won't work */
+time_t pdb_get_pass_can_change_time_noncalc(const struct samu *sampass)
+{
+ return sampass->pass_can_change_time;
+}
+
+time_t pdb_get_pass_must_change_time(const struct samu *sampass)
+{
+ uint32 expire;
+
+ if (sampass->pass_last_set_time == 0)
+ return (time_t) 0;
+
+ if (sampass->acct_ctrl & ACB_PWNOEXP)
+ return get_time_t_max();
+
+ if (!pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &expire)
+ || expire == (uint32)-1 || expire == 0)
+ return get_time_t_max();
+
+ return sampass->pass_last_set_time + expire;
+}
+
+bool pdb_get_pass_can_change(const struct samu *sampass)
+{
+ if (sampass->pass_can_change_time == get_time_t_max() &&
+ sampass->pass_last_set_time != 0)
+ return False;
+ return True;
+}
+
+uint16 pdb_get_logon_divs(const struct samu *sampass)
+{
+ return sampass->logon_divs;
+}
+
+uint32 pdb_get_hours_len(const struct samu *sampass)
+{
+ return sampass->hours_len;
+}
+
+const uint8 *pdb_get_hours(const struct samu *sampass)
+{
+ return (sampass->hours);
+}
+
+const uint8 *pdb_get_nt_passwd(const struct samu *sampass)
+{
+ SMB_ASSERT((!sampass->nt_pw.data)
+ || sampass->nt_pw.length == NT_HASH_LEN);
+ return (uint8 *)sampass->nt_pw.data;
+}
+
+const uint8 *pdb_get_lanman_passwd(const struct samu *sampass)
+{
+ SMB_ASSERT((!sampass->lm_pw.data)
+ || sampass->lm_pw.length == LM_HASH_LEN);
+ return (uint8 *)sampass->lm_pw.data;
+}
+
+const uint8 *pdb_get_pw_history(const struct samu *sampass, uint32 *current_hist_len)
+{
+ SMB_ASSERT((!sampass->nt_pw_his.data)
+ || ((sampass->nt_pw_his.length % PW_HISTORY_ENTRY_LEN) == 0));
+ *current_hist_len = sampass->nt_pw_his.length / PW_HISTORY_ENTRY_LEN;
+ return (uint8 *)sampass->nt_pw_his.data;
+}
+
+/* Return the plaintext password if known. Most of the time
+ it isn't, so don't assume anything magic about this function.
+
+ Used to pass the plaintext to passdb backends that might
+ want to store more than just the NTLM hashes.
+*/
+const char *pdb_get_plaintext_passwd(const struct samu *sampass)
+{
+ return sampass->plaintext_pw;
+}
+
+const DOM_SID *pdb_get_user_sid(const struct samu *sampass)
+{
+ return &sampass->user_sid;
+}
+
+const DOM_SID *pdb_get_group_sid(struct samu *sampass)
+{
+ DOM_SID *gsid;
+ struct passwd *pwd;
+
+ /* Return the cached group SID if we have that */
+ if ( sampass->group_sid ) {
+ return sampass->group_sid;
+ }
+
+ /* generate the group SID from the user's primary Unix group */
+
+ if ( !(gsid = TALLOC_P( sampass, DOM_SID )) ) {
+ return NULL;
+ }
+
+ /* No algorithmic mapping, meaning that we have to figure out the
+ primary group SID according to group mapping and the user SID must
+ be a newly allocated one. We rely on the user's Unix primary gid.
+ We have no choice but to fail if we can't find it. */
+
+ if ( sampass->unix_pw ) {
+ pwd = sampass->unix_pw;
+ } else {
+ pwd = Get_Pwnam_alloc( sampass, pdb_get_username(sampass) );
+ }
+
+ if ( !pwd ) {
+ DEBUG(0,("pdb_get_group_sid: Failed to find Unix account for %s\n", pdb_get_username(sampass) ));
+ return NULL;
+ }
+
+ if ( pdb_gid_to_sid(pwd->pw_gid, gsid) ) {
+ enum lsa_SidType type = SID_NAME_UNKNOWN;
+ TALLOC_CTX *mem_ctx = talloc_init("pdb_get_group_sid");
+ bool lookup_ret;
+
+ if (!mem_ctx) {
+ return NULL;
+ }
+
+ /* Now check that it's actually a domain group and not something else */
+
+ lookup_ret = lookup_sid(mem_ctx, gsid, NULL, NULL, &type);
+
+ TALLOC_FREE( mem_ctx );
+
+ if ( lookup_ret && (type == SID_NAME_DOM_GRP) ) {
+ sampass->group_sid = gsid;
+ return sampass->group_sid;
+ }
+
+ DEBUG(3, ("Primary group for user %s is a %s and not a domain group\n",
+ pwd->pw_name, sid_type_lookup(type)));
+ }
+
+ /* Just set it to the 'Domain Users' RID of 512 which will
+ always resolve to a name */
+
+ sid_copy( gsid, get_global_sam_sid() );
+ sid_append_rid( gsid, DOMAIN_GROUP_RID_USERS );
+
+ sampass->group_sid = gsid;
+
+ return sampass->group_sid;
+}
+
+/**
+ * Get flags showing what is initalised in the struct samu
+ * @param sampass the struct samu in question
+ * @return the flags indicating the members initialised in the struct.
+ **/
+
+enum pdb_value_state pdb_get_init_flags(const struct samu *sampass, enum pdb_elements element)
+{
+ enum pdb_value_state ret = PDB_DEFAULT;
+
+ if (!sampass->change_flags || !sampass->set_flags)
+ return ret;
+
+ if (bitmap_query(sampass->set_flags, element)) {
+ DEBUG(11, ("element %d: SET\n", element));
+ ret = PDB_SET;
+ }
+
+ if (bitmap_query(sampass->change_flags, element)) {
+ DEBUG(11, ("element %d: CHANGED\n", element));
+ ret = PDB_CHANGED;
+ }
+
+ if (ret == PDB_DEFAULT) {
+ DEBUG(11, ("element %d: DEFAULT\n", element));
+ }
+
+ return ret;
+}
+
+const char *pdb_get_username(const struct samu *sampass)
+{
+ return sampass->username;
+}
+
+const char *pdb_get_domain(const struct samu *sampass)
+{
+ return sampass->domain;
+}
+
+const char *pdb_get_nt_username(const struct samu *sampass)
+{
+ return sampass->nt_username;
+}
+
+const char *pdb_get_fullname(const struct samu *sampass)
+{
+ return sampass->full_name;
+}
+
+const char *pdb_get_homedir(const struct samu *sampass)
+{
+ return sampass->home_dir;
+}
+
+const char *pdb_get_dir_drive(const struct samu *sampass)
+{
+ return sampass->dir_drive;
+}
+
+const char *pdb_get_logon_script(const struct samu *sampass)
+{
+ return sampass->logon_script;
+}
+
+const char *pdb_get_profile_path(const struct samu *sampass)
+{
+ return sampass->profile_path;
+}
+
+const char *pdb_get_acct_desc(const struct samu *sampass)
+{
+ return sampass->acct_desc;
+}
+
+const char *pdb_get_workstations(const struct samu *sampass)
+{
+ return sampass->workstations;
+}
+
+const char *pdb_get_comment(const struct samu *sampass)
+{
+ return sampass->comment;
+}
+
+const char *pdb_get_munged_dial(const struct samu *sampass)
+{
+ return sampass->munged_dial;
+}
+
+uint16 pdb_get_bad_password_count(const struct samu *sampass)
+{
+ return sampass->bad_password_count;
+}
+
+uint16 pdb_get_logon_count(const struct samu *sampass)
+{
+ return sampass->logon_count;
+}
+
+uint32 pdb_get_unknown_6(const struct samu *sampass)
+{
+ return sampass->unknown_6;
+}
+
+void *pdb_get_backend_private_data(const struct samu *sampass, const struct pdb_methods *my_methods)
+{
+ if (my_methods == sampass->backend_private_methods) {
+ return sampass->backend_private_data;
+ } else {
+ return NULL;
+ }
+}
+
+/*********************************************************************
+ Collection of set...() functions for struct samu.
+ ********************************************************************/
+
+bool pdb_set_acct_ctrl(struct samu *sampass, uint32 acct_ctrl, enum pdb_value_state flag)
+{
+ sampass->acct_ctrl = acct_ctrl;
+ return pdb_set_init_flags(sampass, PDB_ACCTCTRL, flag);
+}
+
+bool pdb_set_logon_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->logon_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_LOGONTIME, flag);
+}
+
+bool pdb_set_logoff_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->logoff_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_LOGOFFTIME, flag);
+}
+
+bool pdb_set_kickoff_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->kickoff_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_KICKOFFTIME, flag);
+}
+
+bool pdb_set_bad_password_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->bad_password_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_TIME, flag);
+}
+
+bool pdb_set_pass_can_change_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->pass_can_change_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_CANCHANGETIME, flag);
+}
+
+bool pdb_set_pass_must_change_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->pass_must_change_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_MUSTCHANGETIME, flag);
+}
+
+bool pdb_set_pass_last_set_time(struct samu *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ sampass->pass_last_set_time = mytime;
+ return pdb_set_init_flags(sampass, PDB_PASSLASTSET, flag);
+}
+
+bool pdb_set_hours_len(struct samu *sampass, uint32 len, enum pdb_value_state flag)
+{
+ sampass->hours_len = len;
+ return pdb_set_init_flags(sampass, PDB_HOURSLEN, flag);
+}
+
+bool pdb_set_logon_divs(struct samu *sampass, uint16 hours, enum pdb_value_state flag)
+{
+ sampass->logon_divs = hours;
+ return pdb_set_init_flags(sampass, PDB_LOGONDIVS, flag);
+}
+
+/**
+ * Set flags showing what is initalised in the struct samu
+ * @param sampass the struct samu in question
+ * @param flag The *new* flag to be set. Old flags preserved
+ * this flag is only added.
+ **/
+
+bool pdb_set_init_flags(struct samu *sampass, enum pdb_elements element, enum pdb_value_state value_flag)
+{
+ if (!sampass->set_flags) {
+ if ((sampass->set_flags =
+ bitmap_talloc(sampass,
+ PDB_COUNT))==NULL) {
+ DEBUG(0,("bitmap_talloc failed\n"));
+ return False;
+ }
+ }
+ if (!sampass->change_flags) {
+ if ((sampass->change_flags =
+ bitmap_talloc(sampass,
+ PDB_COUNT))==NULL) {
+ DEBUG(0,("bitmap_talloc failed\n"));
+ return False;
+ }
+ }
+
+ switch(value_flag) {
+ case PDB_CHANGED:
+ if (!bitmap_set(sampass->change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_set(sampass->set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now CHANGED\n", element));
+ break;
+ case PDB_SET:
+ if (!bitmap_clear(sampass->change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_set(sampass->set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now SET\n", element));
+ break;
+ case PDB_DEFAULT:
+ default:
+ if (!bitmap_clear(sampass->change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_clear(sampass->set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now DEFAULT\n", element));
+ break;
+ }
+
+ return True;
+}
+
+bool pdb_set_user_sid(struct samu *sampass, const DOM_SID *u_sid, enum pdb_value_state flag)
+{
+ if (!u_sid)
+ return False;
+
+ sid_copy(&sampass->user_sid, u_sid);
+
+ DEBUG(10, ("pdb_set_user_sid: setting user sid %s\n",
+ sid_string_dbg(&sampass->user_sid)));
+
+ return pdb_set_init_flags(sampass, PDB_USERSID, flag);
+}
+
+bool pdb_set_user_sid_from_string(struct samu *sampass, fstring u_sid, enum pdb_value_state flag)
+{
+ DOM_SID new_sid;
+
+ if (!u_sid)
+ return False;
+
+ DEBUG(10, ("pdb_set_user_sid_from_string: setting user sid %s\n",
+ u_sid));
+
+ if (!string_to_sid(&new_sid, u_sid)) {
+ DEBUG(1, ("pdb_set_user_sid_from_string: %s isn't a valid SID!\n", u_sid));
+ return False;
+ }
+
+ if (!pdb_set_user_sid(sampass, &new_sid, flag)) {
+ DEBUG(1, ("pdb_set_user_sid_from_string: could not set sid %s on struct samu!\n", u_sid));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ We never fill this in from a passdb backend but rather set is
+ based on the user's primary group membership. However, the
+ struct samu* is overloaded and reused in domain memship code
+ as well and built from the netr_SamInfo3 or PAC so we
+ have to allow the explicitly setting of a group SID here.
+********************************************************************/
+
+bool pdb_set_group_sid(struct samu *sampass, const DOM_SID *g_sid, enum pdb_value_state flag)
+{
+ gid_t gid;
+
+ if (!g_sid)
+ return False;
+
+ if ( !(sampass->group_sid = TALLOC_P( sampass, DOM_SID )) ) {
+ return False;
+ }
+
+ /* if we cannot resolve the SID to gid, then just ignore it and
+ store DOMAIN_USERS as the primary groupSID */
+
+ if ( sid_to_gid( g_sid, &gid ) ) {
+ sid_copy(sampass->group_sid, g_sid);
+ } else {
+ sid_copy( sampass->group_sid, get_global_sam_sid() );
+ sid_append_rid( sampass->group_sid, DOMAIN_GROUP_RID_USERS );
+ }
+
+ DEBUG(10, ("pdb_set_group_sid: setting group sid %s\n",
+ sid_string_dbg(sampass->group_sid)));
+
+ return pdb_set_init_flags(sampass, PDB_GROUPSID, flag);
+}
+
+/*********************************************************************
+ Set the user's UNIX name.
+ ********************************************************************/
+
+bool pdb_set_username(struct samu *sampass, const char *username, enum pdb_value_state flag)
+{
+ if (username) {
+ DEBUG(10, ("pdb_set_username: setting username %s, was %s\n", username,
+ (sampass->username)?(sampass->username):"NULL"));
+
+ sampass->username = talloc_strdup(sampass, username);
+
+ if (!sampass->username) {
+ DEBUG(0, ("pdb_set_username: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->username = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_USERNAME, flag);
+}
+
+/*********************************************************************
+ Set the domain name.
+ ********************************************************************/
+
+bool pdb_set_domain(struct samu *sampass, const char *domain, enum pdb_value_state flag)
+{
+ if (domain) {
+ DEBUG(10, ("pdb_set_domain: setting domain %s, was %s\n", domain,
+ (sampass->domain)?(sampass->domain):"NULL"));
+
+ sampass->domain = talloc_strdup(sampass, domain);
+
+ if (!sampass->domain) {
+ DEBUG(0, ("pdb_set_domain: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->domain = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_DOMAIN, flag);
+}
+
+/*********************************************************************
+ Set the user's NT name.
+ ********************************************************************/
+
+bool pdb_set_nt_username(struct samu *sampass, const char *nt_username, enum pdb_value_state flag)
+{
+ if (nt_username) {
+ DEBUG(10, ("pdb_set_nt_username: setting nt username %s, was %s\n", nt_username,
+ (sampass->nt_username)?(sampass->nt_username):"NULL"));
+
+ sampass->nt_username = talloc_strdup(sampass, nt_username);
+
+ if (!sampass->nt_username) {
+ DEBUG(0, ("pdb_set_nt_username: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->nt_username = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_NTUSERNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's full name.
+ ********************************************************************/
+
+bool pdb_set_fullname(struct samu *sampass, const char *full_name, enum pdb_value_state flag)
+{
+ if (full_name) {
+ DEBUG(10, ("pdb_set_full_name: setting full name %s, was %s\n", full_name,
+ (sampass->full_name)?(sampass->full_name):"NULL"));
+
+ sampass->full_name = talloc_strdup(sampass, full_name);
+
+ if (!sampass->full_name) {
+ DEBUG(0, ("pdb_set_fullname: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->full_name = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_FULLNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's logon script.
+ ********************************************************************/
+
+bool pdb_set_logon_script(struct samu *sampass, const char *logon_script, enum pdb_value_state flag)
+{
+ if (logon_script) {
+ DEBUG(10, ("pdb_set_logon_script: setting logon script %s, was %s\n", logon_script,
+ (sampass->logon_script)?(sampass->logon_script):"NULL"));
+
+ sampass->logon_script = talloc_strdup(sampass, logon_script);
+
+ if (!sampass->logon_script) {
+ DEBUG(0, ("pdb_set_logon_script: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->logon_script = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_LOGONSCRIPT, flag);
+}
+
+/*********************************************************************
+ Set the user's profile path.
+ ********************************************************************/
+
+bool pdb_set_profile_path(struct samu *sampass, const char *profile_path, enum pdb_value_state flag)
+{
+ if (profile_path) {
+ DEBUG(10, ("pdb_set_profile_path: setting profile path %s, was %s\n", profile_path,
+ (sampass->profile_path)?(sampass->profile_path):"NULL"));
+
+ sampass->profile_path = talloc_strdup(sampass, profile_path);
+
+ if (!sampass->profile_path) {
+ DEBUG(0, ("pdb_set_profile_path: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->profile_path = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PROFILE, flag);
+}
+
+/*********************************************************************
+ Set the user's directory drive.
+ ********************************************************************/
+
+bool pdb_set_dir_drive(struct samu *sampass, const char *dir_drive, enum pdb_value_state flag)
+{
+ if (dir_drive) {
+ DEBUG(10, ("pdb_set_dir_drive: setting dir drive %s, was %s\n", dir_drive,
+ (sampass->dir_drive)?(sampass->dir_drive):"NULL"));
+
+ sampass->dir_drive = talloc_strdup(sampass, dir_drive);
+
+ if (!sampass->dir_drive) {
+ DEBUG(0, ("pdb_set_dir_drive: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->dir_drive = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_DRIVE, flag);
+}
+
+/*********************************************************************
+ Set the user's home directory.
+ ********************************************************************/
+
+bool pdb_set_homedir(struct samu *sampass, const char *home_dir, enum pdb_value_state flag)
+{
+ if (home_dir) {
+ DEBUG(10, ("pdb_set_homedir: setting home dir %s, was %s\n", home_dir,
+ (sampass->home_dir)?(sampass->home_dir):"NULL"));
+
+ sampass->home_dir = talloc_strdup(sampass, home_dir);
+
+ if (!sampass->home_dir) {
+ DEBUG(0, ("pdb_set_home_dir: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->home_dir = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_SMBHOME, flag);
+}
+
+/*********************************************************************
+ Set the user's account description.
+ ********************************************************************/
+
+bool pdb_set_acct_desc(struct samu *sampass, const char *acct_desc, enum pdb_value_state flag)
+{
+ if (acct_desc) {
+ sampass->acct_desc = talloc_strdup(sampass, acct_desc);
+
+ if (!sampass->acct_desc) {
+ DEBUG(0, ("pdb_set_acct_desc: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->acct_desc = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_ACCTDESC, flag);
+}
+
+/*********************************************************************
+ Set the user's workstation allowed list.
+ ********************************************************************/
+
+bool pdb_set_workstations(struct samu *sampass, const char *workstations, enum pdb_value_state flag)
+{
+ if (workstations) {
+ DEBUG(10, ("pdb_set_workstations: setting workstations %s, was %s\n", workstations,
+ (sampass->workstations)?(sampass->workstations):"NULL"));
+
+ sampass->workstations = talloc_strdup(sampass, workstations);
+
+ if (!sampass->workstations) {
+ DEBUG(0, ("pdb_set_workstations: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->workstations = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_WORKSTATIONS, flag);
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+bool pdb_set_comment(struct samu *sampass, const char *comment, enum pdb_value_state flag)
+{
+ if (comment) {
+ sampass->comment = talloc_strdup(sampass, comment);
+
+ if (!sampass->comment) {
+ DEBUG(0, ("pdb_set_comment: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->comment = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_COMMENT, flag);
+}
+
+/*********************************************************************
+ Set the user's dial string.
+ ********************************************************************/
+
+bool pdb_set_munged_dial(struct samu *sampass, const char *munged_dial, enum pdb_value_state flag)
+{
+ if (munged_dial) {
+ sampass->munged_dial = talloc_strdup(sampass, munged_dial);
+
+ if (!sampass->munged_dial) {
+ DEBUG(0, ("pdb_set_munged_dial: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->munged_dial = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_MUNGEDDIAL, flag);
+}
+
+/*********************************************************************
+ Set the user's NT hash.
+ ********************************************************************/
+
+bool pdb_set_nt_passwd(struct samu *sampass, const uint8 pwd[NT_HASH_LEN], enum pdb_value_state flag)
+{
+ data_blob_clear_free(&sampass->nt_pw);
+
+ if (pwd) {
+ sampass->nt_pw =
+ data_blob_talloc(sampass, pwd, NT_HASH_LEN);
+ } else {
+ sampass->nt_pw = data_blob_null;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_NTPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's LM hash.
+ ********************************************************************/
+
+bool pdb_set_lanman_passwd(struct samu *sampass, const uint8 pwd[LM_HASH_LEN], enum pdb_value_state flag)
+{
+ data_blob_clear_free(&sampass->lm_pw);
+
+ /* on keep the password if we are allowing LANMAN authentication */
+
+ if (pwd && lp_lanman_auth() ) {
+ sampass->lm_pw = data_blob_talloc(sampass, pwd, LM_HASH_LEN);
+ } else {
+ sampass->lm_pw = data_blob_null;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_LMPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's password history hash. historyLen is the number of
+ PW_HISTORY_SALT_LEN+SALTED_MD5_HASH_LEN length
+ entries to store in the history - this must match the size of the uint8 array
+ in pwd.
+********************************************************************/
+
+bool pdb_set_pw_history(struct samu *sampass, const uint8 *pwd, uint32 historyLen, enum pdb_value_state flag)
+{
+ if (historyLen && pwd){
+ sampass->nt_pw_his = data_blob_talloc(sampass,
+ pwd, historyLen*PW_HISTORY_ENTRY_LEN);
+ if (!sampass->nt_pw_his.length) {
+ DEBUG(0, ("pdb_set_pw_history: data_blob_talloc() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->nt_pw_his = data_blob_talloc(sampass, NULL, 0);
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PWHISTORY, flag);
+}
+
+/*********************************************************************
+ Set the user's plaintext password only (base procedure, see helper
+ below)
+ ********************************************************************/
+
+bool pdb_set_plaintext_pw_only(struct samu *sampass, const char *password, enum pdb_value_state flag)
+{
+ if (password) {
+ if (sampass->plaintext_pw!=NULL)
+ memset(sampass->plaintext_pw,'\0',strlen(sampass->plaintext_pw)+1);
+
+ sampass->plaintext_pw = talloc_strdup(sampass, password);
+
+ if (!sampass->plaintext_pw) {
+ DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->plaintext_pw = NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PLAINTEXT_PW, flag);
+}
+
+bool pdb_set_bad_password_count(struct samu *sampass, uint16 bad_password_count, enum pdb_value_state flag)
+{
+ sampass->bad_password_count = bad_password_count;
+ return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_COUNT, flag);
+}
+
+bool pdb_set_logon_count(struct samu *sampass, uint16 logon_count, enum pdb_value_state flag)
+{
+ sampass->logon_count = logon_count;
+ return pdb_set_init_flags(sampass, PDB_LOGON_COUNT, flag);
+}
+
+bool pdb_set_unknown_6(struct samu *sampass, uint32 unkn, enum pdb_value_state flag)
+{
+ sampass->unknown_6 = unkn;
+ return pdb_set_init_flags(sampass, PDB_UNKNOWN6, flag);
+}
+
+bool pdb_set_hours(struct samu *sampass, const uint8 *hours, enum pdb_value_state flag)
+{
+ if (!hours) {
+ memset ((char *)sampass->hours, 0, MAX_HOURS_LEN);
+ } else {
+ memcpy (sampass->hours, hours, MAX_HOURS_LEN);
+ }
+
+ return pdb_set_init_flags(sampass, PDB_HOURS, flag);
+}
+
+bool pdb_set_backend_private_data(struct samu *sampass, void *private_data,
+ void (*free_fn)(void **),
+ const struct pdb_methods *my_methods,
+ enum pdb_value_state flag)
+{
+ if (sampass->backend_private_data &&
+ sampass->backend_private_data_free_fn) {
+ sampass->backend_private_data_free_fn(
+ &sampass->backend_private_data);
+ }
+
+ sampass->backend_private_data = private_data;
+ sampass->backend_private_data_free_fn = free_fn;
+ sampass->backend_private_methods = my_methods;
+
+ return pdb_set_init_flags(sampass, PDB_BACKEND_PRIVATE_DATA, flag);
+}
+
+
+/* Helpful interfaces to the above */
+
+bool pdb_set_pass_can_change(struct samu *sampass, bool canchange)
+{
+ return pdb_set_pass_can_change_time(sampass,
+ canchange ? 0 : get_time_t_max(),
+ PDB_CHANGED);
+}
+
+
+/*********************************************************************
+ Set the user's PLAINTEXT password. Used as an interface to the above.
+ Also sets the last change time to NOW.
+ ********************************************************************/
+
+bool pdb_set_plaintext_passwd(struct samu *sampass, const char *plaintext)
+{
+ uchar new_lanman_p16[LM_HASH_LEN];
+ uchar new_nt_p16[NT_HASH_LEN];
+
+ if (!plaintext)
+ return False;
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(plaintext, new_nt_p16);
+
+ if (!pdb_set_nt_passwd (sampass, new_nt_p16, PDB_CHANGED))
+ return False;
+
+ if (!E_deshash(plaintext, new_lanman_p16)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14 */
+
+ if (!pdb_set_lanman_passwd (sampass, NULL, PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_lanman_passwd (sampass, new_lanman_p16, PDB_CHANGED))
+ return False;
+ }
+
+ if (!pdb_set_plaintext_pw_only (sampass, plaintext, PDB_CHANGED))
+ return False;
+
+ if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED))
+ return False;
+
+ /* Store the password history. */
+ if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) {
+ uchar *pwhistory;
+ uint32 pwHistLen;
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHistLen);
+ if (pwHistLen != 0){
+ uint32 current_history_len;
+ /* We need to make sure we don't have a race condition here - the
+ account policy history length can change between when the pw_history
+ was first loaded into the struct samu struct and now.... JRA. */
+ pwhistory = (uchar *)pdb_get_pw_history(sampass, &current_history_len);
+
+ if (current_history_len != pwHistLen) {
+ /* After closing and reopening struct samu the history
+ values will sync up. We can't do this here. */
+
+ /* current_history_len > pwHistLen is not a problem - we
+ have more history than we need. */
+
+ if (current_history_len < pwHistLen) {
+ /* Ensure we have space for the needed history. */
+ uchar *new_history = (uchar *)TALLOC(sampass,
+ pwHistLen*PW_HISTORY_ENTRY_LEN);
+ if (!new_history) {
+ return False;
+ }
+
+ /* And copy it into the new buffer. */
+ if (current_history_len) {
+ memcpy(new_history, pwhistory,
+ current_history_len*PW_HISTORY_ENTRY_LEN);
+ }
+ /* Clearing out any extra space. */
+ memset(&new_history[current_history_len*PW_HISTORY_ENTRY_LEN],
+ '\0', (pwHistLen-current_history_len)*PW_HISTORY_ENTRY_LEN);
+ /* Finally replace it. */
+ pwhistory = new_history;
+ }
+ }
+ if (pwhistory && pwHistLen){
+ /* Make room for the new password in the history list. */
+ if (pwHistLen > 1) {
+ memmove(&pwhistory[PW_HISTORY_ENTRY_LEN],
+ pwhistory, (pwHistLen -1)*PW_HISTORY_ENTRY_LEN );
+ }
+ /* Create the new salt as the first part of the history entry. */
+ generate_random_buffer(pwhistory, PW_HISTORY_SALT_LEN);
+
+ /* Generate the md5 hash of the salt+new password as the second
+ part of the history entry. */
+
+ E_md5hash(pwhistory, new_nt_p16, &pwhistory[PW_HISTORY_SALT_LEN]);
+ pdb_set_pw_history(sampass, pwhistory, pwHistLen, PDB_CHANGED);
+ } else {
+ DEBUG (10,("pdb_get_set.c: pdb_set_plaintext_passwd: pwhistory was NULL!\n"));
+ }
+ } else {
+ /* Set the history length to zero. */
+ pdb_set_pw_history(sampass, NULL, 0, PDB_CHANGED);
+ }
+ }
+
+ return True;
+}
+
+/* check for any PDB_SET/CHANGED field and fill the appropriate mask bit */
+uint32 pdb_build_fields_present(struct samu *sampass)
+{
+ /* value set to all for testing */
+ return 0x00ffffff;
+}
diff --git a/source3/passdb/pdb_interface.c b/source3/passdb/pdb_interface.c
new file mode 100644
index 0000000000..2a1024cc56
--- /dev/null
+++ b/source3/passdb/pdb_interface.c
@@ -0,0 +1,2071 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Volker Lendecke 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static_decl_pdb;
+
+static struct pdb_init_function_entry *backends = NULL;
+
+static void lazy_initialize_passdb(void)
+{
+ static bool initialized = False;
+ if(initialized) {
+ return;
+ }
+ static_init_pdb;
+ initialized = True;
+}
+
+static bool lookup_global_sam_rid(TALLOC_CTX *mem_ctx, uint32 rid,
+ const char **name,
+ enum lsa_SidType *psid_name_use,
+ union unid_t *unix_id);
+
+NTSTATUS smb_register_passdb(int version, const char *name, pdb_init_function init)
+{
+ struct pdb_init_function_entry *entry = backends;
+
+ if(version != PASSDB_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register passdb backend!\n"
+ "You tried to register a passdb module with PASSDB_INTERFACE_VERSION %d, "
+ "while this version of samba uses version %d\n",
+ version,PASSDB_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register passdb backend %s\n", name));
+
+ /* Check for duplicates */
+ if (pdb_find_backend_entry(name)) {
+ DEBUG(0,("There already is a passdb backend registered with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = SMB_XMALLOC_P(struct pdb_init_function_entry);
+ entry->name = smb_xstrdup(name);
+ entry->init = init;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added passdb backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+struct pdb_init_function_entry *pdb_find_backend_entry(const char *name)
+{
+ struct pdb_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * The event context for the passdb backend. I know this is a bad hack and yet
+ * another static variable, but our pdb API is a global thing per
+ * definition. The first use for this is the LDAP idle function, more might be
+ * added later.
+ *
+ * I don't feel too bad about this static variable, it replaces the
+ * smb_idle_event_list that used to exist in lib/module.c. -- VL
+ */
+
+static struct event_context *pdb_event_ctx;
+
+struct event_context *pdb_get_event_context(void)
+{
+ return pdb_event_ctx;
+}
+
+/******************************************************************
+ Make a pdb_methods from scratch
+ *******************************************************************/
+
+NTSTATUS make_pdb_method_name(struct pdb_methods **methods, const char *selected)
+{
+ char *module_name = smb_xstrdup(selected);
+ char *module_location = NULL, *p;
+ struct pdb_init_function_entry *entry;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ lazy_initialize_passdb();
+
+ p = strchr(module_name, ':');
+
+ if (p) {
+ *p = 0;
+ module_location = p+1;
+ trim_char(module_location, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+
+ DEBUG(5,("Attempting to find a passdb backend to match %s (%s)\n", selected, module_name));
+
+ entry = pdb_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("pdb", module_name)) && !(entry = pdb_find_backend_entry(module_name))) {
+ DEBUG(0,("Plugin is available, but doesn't register passdb 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 pdb backend %s\n", module_name));
+
+ if ( !NT_STATUS_IS_OK( nt_status = entry->init(methods, module_location) ) ) {
+ DEBUG(0,("pdb backend %s did not correctly init (error was %s)\n",
+ selected, nt_errstr(nt_status)));
+ SAFE_FREE(module_name);
+ return nt_status;
+ }
+
+ SAFE_FREE(module_name);
+
+ DEBUG(5,("pdb backend %s has a valid init\n", selected));
+
+ return nt_status;
+}
+
+/******************************************************************
+ Return an already initialized pdb_methods structure
+*******************************************************************/
+
+static struct pdb_methods *pdb_get_methods_reload( bool reload )
+{
+ static struct pdb_methods *pdb = NULL;
+
+ if ( pdb && reload ) {
+ pdb->free_private_data( &(pdb->private_data) );
+ if ( !NT_STATUS_IS_OK( make_pdb_method_name( &pdb, lp_passdb_backend() ) ) ) {
+ char *msg = NULL;
+ asprintf(&msg, "pdb_get_methods_reload: "
+ "failed to get pdb methods for backend %s\n",
+ lp_passdb_backend());
+ smb_panic(msg);
+ }
+ }
+
+ if ( !pdb ) {
+ if ( !NT_STATUS_IS_OK( make_pdb_method_name( &pdb, lp_passdb_backend() ) ) ) {
+ char *msg = NULL;
+ asprintf(&msg, "pdb_get_methods_reload: "
+ "failed to get pdb methods for backend %s\n",
+ lp_passdb_backend());
+ smb_panic(msg);
+ }
+ }
+
+ return pdb;
+}
+
+static struct pdb_methods *pdb_get_methods(void)
+{
+ return pdb_get_methods_reload(False);
+}
+
+bool pdb_getsampwnam(struct samu *sam_acct, const char *username)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ struct samu *cache_copy;
+ const struct dom_sid *user_sid;
+
+ if (!NT_STATUS_IS_OK(pdb->getsampwnam(pdb, sam_acct, username))) {
+ return False;
+ }
+
+ cache_copy = samu_new(NULL);
+ if (cache_copy == NULL) {
+ return False;
+ }
+
+ if (!pdb_copy_sam_account(cache_copy, sam_acct)) {
+ TALLOC_FREE(cache_copy);
+ return False;
+ }
+
+ user_sid = pdb_get_user_sid(cache_copy);
+
+ memcache_add_talloc(NULL, PDB_GETPWSID_CACHE,
+ data_blob_const(user_sid, sizeof(*user_sid)),
+ cache_copy);
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+bool guest_user_info( struct samu *user )
+{
+ struct passwd *pwd;
+ NTSTATUS result;
+ const char *guestname = lp_guestaccount();
+
+ if ( !(pwd = getpwnam_alloc( NULL, guestname ) ) ) {
+ DEBUG(0,("guest_user_info: Unable to locate guest account [%s]!\n",
+ guestname));
+ return False;
+ }
+
+ result = samu_set_unix(user, pwd );
+
+ TALLOC_FREE( pwd );
+
+ return NT_STATUS_IS_OK( result );
+}
+
+/**********************************************************************
+**********************************************************************/
+
+bool pdb_getsampwsid(struct samu *sam_acct, const DOM_SID *sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ uint32 rid;
+ void *cache_data;
+
+ /* hard code the Guest RID of 501 */
+
+ if ( !sid_peek_check_rid( get_global_sam_sid(), sid, &rid ) )
+ return False;
+
+ if ( rid == DOMAIN_USER_RID_GUEST ) {
+ DEBUG(6,("pdb_getsampwsid: Building guest account\n"));
+ return guest_user_info( sam_acct );
+ }
+
+ /* check the cache first */
+
+ cache_data = memcache_lookup_talloc(
+ NULL, PDB_GETPWSID_CACHE, data_blob_const(sid, sizeof(*sid)));
+
+ if (cache_data != NULL) {
+ struct samu *cache_copy = talloc_get_type_abort(
+ cache_data, struct samu);
+
+ return pdb_copy_sam_account(sam_acct, cache_copy);
+ }
+
+ return NT_STATUS_IS_OK(pdb->getsampwsid(pdb, sam_acct, sid));
+}
+
+static NTSTATUS pdb_default_create_user(struct pdb_methods *methods,
+ TALLOC_CTX *tmp_ctx, const char *name,
+ uint32 acb_info, uint32 *rid)
+{
+ struct samu *sam_pass;
+ NTSTATUS status;
+ struct passwd *pwd;
+
+ if ((sam_pass = samu_new(tmp_ctx)) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !(pwd = Get_Pwnam_alloc(tmp_ctx, name)) ) {
+ char *add_script = NULL;
+ int add_ret;
+ fstring name2;
+
+ if ((acb_info & ACB_NORMAL) && name[strlen(name)-1] != '$') {
+ add_script = talloc_strdup(tmp_ctx,
+ lp_adduser_script());
+ } else {
+ add_script = talloc_strdup(tmp_ctx,
+ lp_addmachine_script());
+ }
+
+ if (!add_script || add_script[0] == '\0') {
+ DEBUG(3, ("Could not find user %s and no add script "
+ "defined\n", name));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* lowercase the username before creating the Unix account for
+ compatibility with previous Samba releases */
+ fstrcpy( name2, name );
+ strlower_m( name2 );
+ add_script = talloc_all_string_sub(tmp_ctx,
+ add_script,
+ "%u",
+ name2);
+ if (!add_script) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ add_ret = smbrun(add_script,NULL);
+ DEBUG(add_ret ? 0 : 3, ("_samr_create_user: Running the command `%s' gave %d\n",
+ add_script, add_ret));
+ if (add_ret == 0) {
+ smb_nscd_flush_user_cache();
+ }
+
+ flush_pwnam_cache();
+
+ pwd = Get_Pwnam_alloc(tmp_ctx, name);
+ }
+
+ /* we have a valid SID coming out of this call */
+
+ status = samu_alloc_rid_unix( sam_pass, pwd );
+
+ TALLOC_FREE( pwd );
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("pdb_default_create_user: failed to create a new user structure: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ if (!sid_peek_check_rid(get_global_sam_sid(),
+ pdb_get_user_sid(sam_pass), rid)) {
+ DEBUG(0, ("Could not get RID of fresh user\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* Use the username case specified in the original request */
+
+ pdb_set_username( sam_pass, name, PDB_SET );
+
+ /* Disable the account on creation, it does not have a reasonable password yet. */
+
+ acb_info |= ACB_DISABLED;
+
+ pdb_set_acct_ctrl(sam_pass, acb_info, PDB_CHANGED);
+
+ status = pdb_add_sam_account(sam_pass);
+
+ TALLOC_FREE(sam_pass);
+
+ return status;
+}
+
+NTSTATUS pdb_create_user(TALLOC_CTX *mem_ctx, const char *name, uint32 flags,
+ uint32 *rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->create_user(pdb, mem_ctx, name, flags, rid);
+}
+
+/****************************************************************************
+ Delete a UNIX user on demand.
+****************************************************************************/
+
+static int smb_delete_user(const char *unix_user)
+{
+ char *del_script = NULL;
+ int ret;
+
+ /* safety check */
+
+ if ( strequal( unix_user, "root" ) ) {
+ DEBUG(0,("smb_delete_user: Refusing to delete local system root account!\n"));
+ return -1;
+ }
+
+ del_script = talloc_strdup(talloc_tos(), lp_deluser_script());
+ if (!del_script || !*del_script) {
+ return -1;
+ }
+ del_script = talloc_all_string_sub(talloc_tos(),
+ del_script,
+ "%u",
+ unix_user);
+ if (!del_script) {
+ return -1;
+ }
+ ret = smbrun(del_script,NULL);
+ flush_pwnam_cache();
+ if (ret == 0) {
+ smb_nscd_flush_user_cache();
+ }
+ DEBUG(ret ? 0 : 3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret));
+
+ return ret;
+}
+
+static NTSTATUS pdb_default_delete_user(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ struct samu *sam_acct)
+{
+ NTSTATUS status;
+ fstring username;
+
+ status = pdb_delete_sam_account(sam_acct);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Now delete the unix side ....
+ * note: we don't check if the delete really happened as the script is
+ * not necessary present and maybe the sysadmin doesn't want to delete
+ * the unix side
+ */
+
+ /* always lower case the username before handing it off to
+ external scripts */
+
+ fstrcpy( username, pdb_get_username(sam_acct) );
+ strlower_m( username );
+
+ smb_delete_user( username );
+
+ return status;
+}
+
+NTSTATUS pdb_delete_user(TALLOC_CTX *mem_ctx, struct samu *sam_acct)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ uid_t uid = -1;
+
+ /* sanity check to make sure we don't delete root */
+
+ if ( !sid_to_uid( pdb_get_user_sid(sam_acct), &uid ) ) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if ( uid == 0 ) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return pdb->delete_user(pdb, mem_ctx, sam_acct);
+}
+
+NTSTATUS pdb_add_sam_account(struct samu *sam_acct)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->add_sam_account(pdb, sam_acct);
+}
+
+NTSTATUS pdb_update_sam_account(struct samu *sam_acct)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+
+ memcache_flush(NULL, PDB_GETPWSID_CACHE);
+
+ return pdb->update_sam_account(pdb, sam_acct);
+}
+
+NTSTATUS pdb_delete_sam_account(struct samu *sam_acct)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+
+ memcache_flush(NULL, PDB_GETPWSID_CACHE);
+
+ return pdb->delete_sam_account(pdb, sam_acct);
+}
+
+NTSTATUS pdb_rename_sam_account(struct samu *oldname, const char *newname)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ uid_t uid;
+ NTSTATUS status;
+
+ memcache_flush(NULL, PDB_GETPWSID_CACHE);
+
+ /* sanity check to make sure we don't rename root */
+
+ if ( !sid_to_uid( pdb_get_user_sid(oldname), &uid ) ) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if ( uid == 0 ) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pdb->rename_sam_account(pdb, oldname, newname);
+
+ /* always flush the cache here just to be safe */
+ flush_pwnam_cache();
+
+ return status;
+}
+
+NTSTATUS pdb_update_login_attempts(struct samu *sam_acct, bool success)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->update_login_attempts(pdb, sam_acct, success);
+}
+
+bool pdb_getgrsid(GROUP_MAP *map, DOM_SID sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return NT_STATUS_IS_OK(pdb->getgrsid(pdb, map, sid));
+}
+
+bool pdb_getgrgid(GROUP_MAP *map, gid_t gid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return NT_STATUS_IS_OK(pdb->getgrgid(pdb, map, gid));
+}
+
+bool pdb_getgrnam(GROUP_MAP *map, const char *name)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return NT_STATUS_IS_OK(pdb->getgrnam(pdb, map, name));
+}
+
+static NTSTATUS pdb_default_create_dom_group(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ uint32 *rid)
+{
+ DOM_SID group_sid;
+ struct group *grp;
+ fstring tmp;
+
+ grp = getgrnam(name);
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ if (smb_create_group(name, &gid) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ grp = getgrgid(gid);
+ }
+
+ if (grp == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pdb_rid_algorithm()) {
+ *rid = algorithmic_pdb_gid_to_group_rid( grp->gr_gid );
+ } else {
+ if (!pdb_new_rid(rid)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ sid_compose(&group_sid, get_global_sam_sid(), *rid);
+
+ return add_initial_entry(grp->gr_gid, sid_to_fstring(tmp, &group_sid),
+ SID_NAME_DOM_GRP, name, NULL);
+}
+
+NTSTATUS pdb_create_dom_group(TALLOC_CTX *mem_ctx, const char *name,
+ uint32 *rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->create_dom_group(pdb, mem_ctx, name, rid);
+}
+
+static NTSTATUS pdb_default_delete_dom_group(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ uint32 rid)
+{
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ NTSTATUS status;
+ struct group *grp;
+ const char *grp_name;
+
+ sid_compose(&group_sid, get_global_sam_sid(), rid);
+
+ if (!get_domain_group_from_sid(group_sid, &map)) {
+ DEBUG(10, ("Could not find group for rid %d\n", rid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /* We need the group name for the smb_delete_group later on */
+
+ if (map.gid == (gid_t)-1) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ grp = getgrgid(map.gid);
+ if (grp == NULL) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /* Copy the name, no idea what pdb_delete_group_mapping_entry does.. */
+
+ grp_name = talloc_strdup(mem_ctx, grp->gr_name);
+ if (grp_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pdb_delete_group_mapping_entry(group_sid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Don't check the result of smb_delete_group */
+
+ smb_delete_group(grp_name);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_delete_dom_group(TALLOC_CTX *mem_ctx, uint32 rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->delete_dom_group(pdb, mem_ctx, rid);
+}
+
+NTSTATUS pdb_add_group_mapping_entry(GROUP_MAP *map)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->add_group_mapping_entry(pdb, map);
+}
+
+NTSTATUS pdb_update_group_mapping_entry(GROUP_MAP *map)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->update_group_mapping_entry(pdb, map);
+}
+
+NTSTATUS pdb_delete_group_mapping_entry(DOM_SID sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->delete_group_mapping_entry(pdb, sid);
+}
+
+bool pdb_enum_group_mapping(const DOM_SID *sid, enum lsa_SidType sid_name_use, GROUP_MAP **pp_rmap,
+ size_t *p_num_entries, bool unix_only)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return NT_STATUS_IS_OK(pdb-> enum_group_mapping(pdb, sid, sid_name_use,
+ pp_rmap, p_num_entries, unix_only));
+}
+
+NTSTATUS pdb_enum_group_members(TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ uint32 **pp_member_rids,
+ size_t *p_num_members)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ NTSTATUS result;
+
+ result = pdb->enum_group_members(pdb, mem_ctx,
+ sid, pp_member_rids, p_num_members);
+
+ /* special check for rid 513 */
+
+ if ( !NT_STATUS_IS_OK( result ) ) {
+ uint32 rid;
+
+ sid_peek_rid( sid, &rid );
+
+ if ( rid == DOMAIN_GROUP_RID_USERS ) {
+ *p_num_members = 0;
+ *pp_member_rids = NULL;
+
+ return NT_STATUS_OK;
+ }
+ }
+
+ return result;
+}
+
+NTSTATUS pdb_enum_group_memberships(TALLOC_CTX *mem_ctx, struct samu *user,
+ DOM_SID **pp_sids, gid_t **pp_gids,
+ size_t *p_num_groups)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->enum_group_memberships(
+ pdb, mem_ctx, user,
+ pp_sids, pp_gids, p_num_groups);
+}
+
+static NTSTATUS pdb_default_set_unix_primary_group(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ struct samu *sampass)
+{
+ struct group *grp;
+ gid_t gid;
+
+ if (!sid_to_gid(pdb_get_group_sid(sampass), &gid) ||
+ (grp = getgrgid(gid)) == NULL) {
+ return NT_STATUS_INVALID_PRIMARY_GROUP;
+ }
+
+ if (smb_set_primary_group(grp->gr_name,
+ pdb_get_username(sampass)) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_set_unix_primary_group(TALLOC_CTX *mem_ctx, struct samu *user)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->set_unix_primary_group(pdb, mem_ctx, user);
+}
+
+/*
+ * Helper function to see whether a user is in a group. We can't use
+ * user_in_group_sid here because this creates dependencies only smbd can
+ * fulfil.
+ */
+
+static bool pdb_user_in_group(TALLOC_CTX *mem_ctx, struct samu *account,
+ const DOM_SID *group_sid)
+{
+ DOM_SID *sids;
+ gid_t *gids;
+ size_t i, num_groups;
+
+ if (!NT_STATUS_IS_OK(pdb_enum_group_memberships(mem_ctx, account,
+ &sids, &gids,
+ &num_groups))) {
+ return False;
+ }
+
+ for (i=0; i<num_groups; i++) {
+ if (sid_equal(group_sid, &sids[i])) {
+ return True;
+ }
+ }
+ return False;
+}
+
+static NTSTATUS pdb_default_add_groupmem(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ uint32 group_rid,
+ uint32 member_rid)
+{
+ DOM_SID group_sid, member_sid;
+ struct samu *account = NULL;
+ GROUP_MAP map;
+ struct group *grp;
+ struct passwd *pwd;
+ const char *group_name;
+ uid_t uid;
+
+ sid_compose(&group_sid, get_global_sam_sid(), group_rid);
+ sid_compose(&member_sid, get_global_sam_sid(), member_rid);
+
+ if (!get_domain_group_from_sid(group_sid, &map) ||
+ (map.gid == (gid_t)-1) ||
+ ((grp = getgrgid(map.gid)) == NULL)) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ group_name = talloc_strdup(mem_ctx, grp->gr_name);
+ if (group_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !(account = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_getsampwsid(account, &member_sid) ||
+ !sid_to_uid(&member_sid, &uid) ||
+ ((pwd = getpwuid_alloc(mem_ctx, uid)) == NULL)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (pdb_user_in_group(mem_ctx, account, &group_sid)) {
+ return NT_STATUS_MEMBER_IN_GROUP;
+ }
+
+ /*
+ * ok, the group exist, the user exist, the user is not in the group,
+ * we can (finally) add it to the group !
+ */
+
+ smb_add_user_group(group_name, pwd->pw_name);
+
+ if (!pdb_user_in_group(mem_ctx, account, &group_sid)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_add_groupmem(TALLOC_CTX *mem_ctx, uint32 group_rid,
+ uint32 member_rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->add_groupmem(pdb, mem_ctx, group_rid, member_rid);
+}
+
+static NTSTATUS pdb_default_del_groupmem(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ uint32 group_rid,
+ uint32 member_rid)
+{
+ DOM_SID group_sid, member_sid;
+ struct samu *account = NULL;
+ GROUP_MAP map;
+ struct group *grp;
+ struct passwd *pwd;
+ const char *group_name;
+ uid_t uid;
+
+ sid_compose(&group_sid, get_global_sam_sid(), group_rid);
+ sid_compose(&member_sid, get_global_sam_sid(), member_rid);
+
+ if (!get_domain_group_from_sid(group_sid, &map) ||
+ (map.gid == (gid_t)-1) ||
+ ((grp = getgrgid(map.gid)) == NULL)) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ group_name = talloc_strdup(mem_ctx, grp->gr_name);
+ if (group_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !(account = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_getsampwsid(account, &member_sid) ||
+ !sid_to_uid(&member_sid, &uid) ||
+ ((pwd = getpwuid_alloc(mem_ctx, uid)) == NULL)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (!pdb_user_in_group(mem_ctx, account, &group_sid)) {
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ }
+
+ /*
+ * ok, the group exist, the user exist, the user is in the group,
+ * we can (finally) delete it from the group!
+ */
+
+ smb_delete_user_group(group_name, pwd->pw_name);
+
+ if (pdb_user_in_group(mem_ctx, account, &group_sid)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_del_groupmem(TALLOC_CTX *mem_ctx, uint32 group_rid,
+ uint32 member_rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->del_groupmem(pdb, mem_ctx, group_rid, member_rid);
+}
+
+NTSTATUS pdb_create_alias(const char *name, uint32 *rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->create_alias(pdb, name, rid);
+}
+
+NTSTATUS pdb_delete_alias(const DOM_SID *sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->delete_alias(pdb, sid);
+}
+
+NTSTATUS pdb_get_aliasinfo(const DOM_SID *sid, struct acct_info *info)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->get_aliasinfo(pdb, sid, info);
+}
+
+NTSTATUS pdb_set_aliasinfo(const DOM_SID *sid, struct acct_info *info)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->set_aliasinfo(pdb, sid, info);
+}
+
+NTSTATUS pdb_add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->add_aliasmem(pdb, alias, member);
+}
+
+NTSTATUS pdb_del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->del_aliasmem(pdb, alias, member);
+}
+
+NTSTATUS pdb_enum_aliasmem(const DOM_SID *alias,
+ DOM_SID **pp_members, size_t *p_num_members)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->enum_aliasmem(pdb, alias, pp_members, p_num_members);
+}
+
+NTSTATUS pdb_enum_alias_memberships(TALLOC_CTX *mem_ctx,
+ const DOM_SID *domain_sid,
+ const DOM_SID *members, size_t num_members,
+ uint32 **pp_alias_rids,
+ size_t *p_num_alias_rids)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->enum_alias_memberships(pdb, mem_ctx,
+ domain_sid,
+ members, num_members,
+ pp_alias_rids,
+ p_num_alias_rids);
+}
+
+NTSTATUS pdb_lookup_rids(const DOM_SID *domain_sid,
+ int num_rids,
+ uint32 *rids,
+ const char **names,
+ enum lsa_SidType *attrs)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->lookup_rids(pdb, domain_sid, num_rids, rids, names, attrs);
+}
+
+/*
+ * NOTE: pdb_lookup_names is currently (2007-01-12) not used anywhere
+ * in the samba code.
+ * Unlike _lsa_lookup_sids and _samr_lookup_rids, which eventually
+ * also ask pdb_lookup_rids, thus looking up a bunch of rids at a time,
+ * the pdb_ calls _lsa_lookup_names and _samr_lookup_names come
+ * down to are pdb_getsampwnam and pdb_getgrnam instead of
+ * pdb_lookup_names.
+ * But in principle, it the call belongs to the API and might get
+ * used in this context some day.
+ */
+#if 0
+NTSTATUS pdb_lookup_names(const DOM_SID *domain_sid,
+ int num_names,
+ const char **names,
+ uint32 *rids,
+ enum lsa_SidType *attrs)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->lookup_names(pdb, domain_sid, num_names, names, rids, attrs);
+}
+#endif
+
+bool pdb_get_account_policy(int policy_index, uint32 *value)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ NTSTATUS status;
+
+ become_root();
+ status = pdb->get_account_policy(pdb, policy_index, value);
+ unbecome_root();
+
+ return NT_STATUS_IS_OK(status);
+}
+
+bool pdb_set_account_policy(int policy_index, uint32 value)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ NTSTATUS status;
+
+ become_root();
+ status = pdb->set_account_policy(pdb, policy_index, value);
+ unbecome_root();
+
+ return NT_STATUS_IS_OK(status);
+}
+
+bool pdb_get_seq_num(time_t *seq_num)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return NT_STATUS_IS_OK(pdb->get_seq_num(pdb, seq_num));
+}
+
+bool pdb_uid_to_rid(uid_t uid, uint32 *rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->uid_to_rid(pdb, uid, rid);
+}
+
+bool pdb_uid_to_sid(uid_t uid, DOM_SID *sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->uid_to_sid(pdb, uid, sid);
+}
+
+bool pdb_gid_to_sid(gid_t gid, DOM_SID *sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->gid_to_sid(pdb, gid, sid);
+}
+
+bool pdb_sid_to_id(const DOM_SID *sid, union unid_t *id,
+ enum lsa_SidType *type)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->sid_to_id(pdb, sid, id, type);
+}
+
+bool pdb_rid_algorithm(void)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->rid_algorithm(pdb);
+}
+
+/********************************************************************
+ Allocate a new RID from the passdb backend. Verify that it is free
+ by calling lookup_global_sam_rid() to verify that the RID is not
+ in use. This handles servers that have existing users or groups
+ with add RIDs (assigned from previous algorithmic mappings)
+********************************************************************/
+
+bool pdb_new_rid(uint32 *rid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ const char *name = NULL;
+ enum lsa_SidType type;
+ uint32 allocated_rid = 0;
+ int i;
+ TALLOC_CTX *ctx;
+
+ if (pdb_rid_algorithm()) {
+ DEBUG(0, ("Trying to allocate a RID when algorithmic RIDs "
+ "are active\n"));
+ return False;
+ }
+
+ if (algorithmic_rid_base() != BASE_RID) {
+ DEBUG(0, ("'algorithmic rid base' is set but a passdb backend "
+ "without algorithmic RIDs is chosen.\n"));
+ DEBUGADD(0, ("Please map all used groups using 'net groupmap "
+ "add', set the maximum used RID using\n"));
+ DEBUGADD(0, ("'net setmaxrid' and remove the parameter\n"));
+ return False;
+ }
+
+ if ( (ctx = talloc_init("pdb_new_rid")) == NULL ) {
+ DEBUG(0,("pdb_new_rid: Talloc initialization failure\n"));
+ return False;
+ }
+
+ /* Attempt to get an unused RID (max tires is 250...yes that it is
+ and arbitrary number I pulkled out of my head). -- jerry */
+
+ for ( i=0; allocated_rid==0 && i<250; i++ ) {
+ /* get a new RID */
+
+ if ( !pdb->new_rid(pdb, &allocated_rid) ) {
+ return False;
+ }
+
+ /* validate that the RID is not in use */
+
+ if ( lookup_global_sam_rid( ctx, allocated_rid, &name, &type, NULL ) ) {
+ allocated_rid = 0;
+ }
+ }
+
+ TALLOC_FREE( ctx );
+
+ if ( allocated_rid == 0 ) {
+ DEBUG(0,("pdb_new_rid: Failed to find unused RID\n"));
+ return False;
+ }
+
+ *rid = allocated_rid;
+
+ return True;
+}
+
+/***************************************************************
+ Initialize the static context (at smbd startup etc).
+
+ If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+bool initialize_password_db(bool reload, struct event_context *event_ctx)
+{
+ pdb_event_ctx = event_ctx;
+ return (pdb_get_methods_reload(reload) != NULL);
+}
+
+
+/***************************************************************************
+ Default implementations of some functions.
+ ****************************************************************************/
+
+static NTSTATUS pdb_default_getsampwnam (struct pdb_methods *methods, struct samu *user, const char *sname)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_getsampwsid(struct pdb_methods *my_methods, struct samu * user, const DOM_SID *sid)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_add_sam_account (struct pdb_methods *methods, struct samu *newpwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_sam_account (struct pdb_methods *methods, struct samu *newpwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_delete_sam_account (struct pdb_methods *methods, struct samu *pwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_rename_sam_account (struct pdb_methods *methods, struct samu *pwd, const char *newname)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_login_attempts (struct pdb_methods *methods, struct samu *newpwd, bool success)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_get_account_policy(struct pdb_methods *methods, int policy_index, uint32 *value)
+{
+ return account_policy_get(policy_index, value) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS pdb_default_set_account_policy(struct pdb_methods *methods, int policy_index, uint32 value)
+{
+ return account_policy_set(policy_index, value) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS pdb_default_get_seq_num(struct pdb_methods *methods, time_t *seq_num)
+{
+ *seq_num = time(NULL);
+ return NT_STATUS_OK;
+}
+
+static bool pdb_default_uid_to_sid(struct pdb_methods *methods, uid_t uid,
+ DOM_SID *sid)
+{
+ struct samu *sampw = NULL;
+ struct passwd *unix_pw;
+ bool ret;
+
+ unix_pw = sys_getpwuid( uid );
+
+ if ( !unix_pw ) {
+ DEBUG(4,("pdb_default_uid_to_rid: host has no idea of uid "
+ "%lu\n", (unsigned long)uid));
+ return False;
+ }
+
+ if ( !(sampw = samu_new( NULL )) ) {
+ DEBUG(0,("pdb_default_uid_to_rid: samu_new() failed!\n"));
+ return False;
+ }
+
+ become_root();
+ ret = NT_STATUS_IS_OK(
+ methods->getsampwnam(methods, sampw, unix_pw->pw_name ));
+ unbecome_root();
+
+ if (!ret) {
+ DEBUG(5, ("pdb_default_uid_to_rid: Did not find user "
+ "%s (%d)\n", unix_pw->pw_name, uid));
+ TALLOC_FREE(sampw);
+ return False;
+ }
+
+ sid_copy(sid, pdb_get_user_sid(sampw));
+
+ TALLOC_FREE(sampw);
+
+ return True;
+}
+
+static bool pdb_default_uid_to_rid(struct pdb_methods *methods, uid_t uid,
+ uint32 *rid)
+{
+ DOM_SID sid;
+ bool ret;
+
+ ret = pdb_default_uid_to_sid(methods, uid, &sid);
+ if (!ret) {
+ return ret;
+ }
+
+ ret = sid_peek_check_rid(get_global_sam_sid(), &sid, rid);
+
+ if (!ret) {
+ DEBUG(1, ("Could not peek rid out of sid %s\n",
+ sid_string_dbg(&sid)));
+ }
+
+ return ret;
+}
+
+static bool pdb_default_gid_to_sid(struct pdb_methods *methods, gid_t gid,
+ DOM_SID *sid)
+{
+ GROUP_MAP map;
+
+ if (!NT_STATUS_IS_OK(methods->getgrgid(methods, &map, gid))) {
+ return False;
+ }
+
+ sid_copy(sid, &map.sid);
+ return True;
+}
+
+static bool pdb_default_sid_to_id(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ union unid_t *id, enum lsa_SidType *type)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = False;
+ const char *name;
+ uint32 rid;
+
+ mem_ctx = talloc_new(NULL);
+
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return False;
+ }
+
+ if (sid_peek_check_rid(get_global_sam_sid(), sid, &rid)) {
+ /* Here we might have users as well as groups and aliases */
+ ret = lookup_global_sam_rid(mem_ctx, rid, &name, type, id);
+ goto done;
+ }
+
+ /* check for "Unix User" */
+
+ if ( sid_peek_check_rid(&global_sid_Unix_Users, sid, &rid) ) {
+ id->uid = rid;
+ *type = SID_NAME_USER;
+ ret = True;
+ goto done;
+ }
+
+ /* check for "Unix Group" */
+
+ if ( sid_peek_check_rid(&global_sid_Unix_Groups, sid, &rid) ) {
+ id->gid = rid;
+ *type = SID_NAME_ALIAS;
+ ret = True;
+ goto done;
+ }
+
+ /* BUILTIN */
+
+ if (sid_check_is_in_builtin(sid) ||
+ sid_check_is_in_wellknown_domain(sid)) {
+ /* Here we only have aliases */
+ GROUP_MAP map;
+ if (!NT_STATUS_IS_OK(methods->getgrsid(methods, &map, *sid))) {
+ DEBUG(10, ("Could not find map for sid %s\n",
+ sid_string_dbg(sid)));
+ goto done;
+ }
+ if ((map.sid_name_use != SID_NAME_ALIAS) &&
+ (map.sid_name_use != SID_NAME_WKN_GRP)) {
+ DEBUG(10, ("Map for sid %s is a %s, expected an "
+ "alias\n", sid_string_dbg(sid),
+ sid_type_lookup(map.sid_name_use)));
+ goto done;
+ }
+
+ id->gid = map.gid;
+ *type = SID_NAME_ALIAS;
+ ret = True;
+ goto done;
+ }
+
+ DEBUG(5, ("Sid %s is neither ours, a Unix SID, nor builtin\n",
+ sid_string_dbg(sid)));
+
+ done:
+
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static bool add_uid_to_array_unique(TALLOC_CTX *mem_ctx,
+ uid_t uid, uid_t **pp_uids, size_t *p_num)
+{
+ size_t i;
+
+ for (i=0; i<*p_num; i++) {
+ if ((*pp_uids)[i] == uid)
+ return True;
+ }
+
+ *pp_uids = TALLOC_REALLOC_ARRAY(mem_ctx, *pp_uids, uid_t, *p_num+1);
+
+ if (*pp_uids == NULL)
+ return False;
+
+ (*pp_uids)[*p_num] = uid;
+ *p_num += 1;
+ return True;
+}
+
+static bool get_memberuids(TALLOC_CTX *mem_ctx, gid_t gid, uid_t **pp_uids, size_t *p_num)
+{
+ struct group *grp;
+ char **gr;
+ struct passwd *pwd;
+ bool winbind_env;
+ bool ret = False;
+
+ *pp_uids = NULL;
+ *p_num = 0;
+
+ /* We only look at our own sam, so don't care about imported stuff */
+ winbind_env = winbind_env_set();
+ (void)winbind_off();
+
+ if ((grp = getgrgid(gid)) == NULL) {
+ /* allow winbindd lookups, but only if they weren't already disabled */
+ goto done;
+ }
+
+ /* Primary group members */
+ setpwent();
+ while ((pwd = getpwent()) != NULL) {
+ if (pwd->pw_gid == gid) {
+ if (!add_uid_to_array_unique(mem_ctx, pwd->pw_uid,
+ pp_uids, p_num)) {
+ goto done;
+ }
+ }
+ }
+ endpwent();
+
+ /* Secondary group members */
+ for (gr = grp->gr_mem; (*gr != NULL) && ((*gr)[0] != '\0'); gr += 1) {
+ struct passwd *pw = getpwnam(*gr);
+
+ if (pw == NULL)
+ continue;
+ if (!add_uid_to_array_unique(mem_ctx, pw->pw_uid, pp_uids, p_num)) {
+ goto done;
+ }
+ }
+
+ ret = True;
+
+ done:
+
+ /* allow winbindd lookups, but only if they weren't already disabled */
+ if (!winbind_env) {
+ (void)winbind_on();
+ }
+
+ return ret;
+}
+
+static NTSTATUS pdb_default_enum_group_members(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group,
+ uint32 **pp_member_rids,
+ size_t *p_num_members)
+{
+ gid_t gid;
+ uid_t *uids;
+ size_t i, num_uids;
+
+ *pp_member_rids = NULL;
+ *p_num_members = 0;
+
+ if (!sid_to_gid(group, &gid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if(!get_memberuids(mem_ctx, gid, &uids, &num_uids))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if (num_uids == 0)
+ return NT_STATUS_OK;
+
+ *pp_member_rids = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_uids);
+
+ for (i=0; i<num_uids; i++) {
+ DOM_SID sid;
+
+ uid_to_sid(&sid, uids[i]);
+
+ if (!sid_check_is_in_our_domain(&sid)) {
+ DEBUG(5, ("Inconsistent SAM -- group member uid not "
+ "in our domain\n"));
+ continue;
+ }
+
+ sid_peek_rid(&sid, &(*pp_member_rids)[*p_num_members]);
+ *p_num_members += 1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pdb_default_enum_group_memberships(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ struct samu *user,
+ DOM_SID **pp_sids,
+ gid_t **pp_gids,
+ size_t *p_num_groups)
+{
+ size_t i;
+ gid_t gid;
+ struct passwd *pw;
+ const char *username = pdb_get_username(user);
+
+
+ /* Ignore the primary group SID. Honor the real Unix primary group.
+ The primary group SID is only of real use to Windows clients */
+
+ if ( !(pw = getpwnam_alloc(mem_ctx, username)) ) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ gid = pw->pw_gid;
+
+ TALLOC_FREE( pw );
+
+ if (!getgroups_unix_user(mem_ctx, username, gid, pp_gids, p_num_groups)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (*p_num_groups == 0) {
+ smb_panic("primary group missing");
+ }
+
+ *pp_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, *p_num_groups);
+
+ if (*pp_sids == NULL) {
+ TALLOC_FREE(*pp_gids);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<*p_num_groups; i++) {
+ gid_to_sid(&(*pp_sids)[i], (*pp_gids)[i]);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Look up a rid in the SAM we're responsible for (i.e. passdb)
+ ********************************************************************/
+
+static bool lookup_global_sam_rid(TALLOC_CTX *mem_ctx, uint32 rid,
+ const char **name,
+ enum lsa_SidType *psid_name_use,
+ union unid_t *unix_id)
+{
+ struct samu *sam_account = NULL;
+ GROUP_MAP map;
+ bool ret;
+ DOM_SID sid;
+
+ *psid_name_use = SID_NAME_UNKNOWN;
+
+ DEBUG(5,("lookup_global_sam_rid: looking up RID %u.\n",
+ (unsigned int)rid));
+
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+
+ /* see if the passdb can help us with the name of the user */
+
+ if ( !(sam_account = samu_new( NULL )) ) {
+ return False;
+ }
+
+ /* BEING ROOT BLLOCK */
+ become_root();
+ if (pdb_getsampwsid(sam_account, &sid)) {
+ struct passwd *pw;
+
+ unbecome_root(); /* -----> EXIT BECOME_ROOT() */
+ *name = talloc_strdup(mem_ctx, pdb_get_username(sam_account));
+ if (!*name) {
+ TALLOC_FREE(sam_account);
+ return False;
+ }
+
+ *psid_name_use = SID_NAME_USER;
+
+ TALLOC_FREE(sam_account);
+
+ if (unix_id == NULL) {
+ return True;
+ }
+
+ pw = Get_Pwnam_alloc(talloc_tos(), *name);
+ if (pw == NULL) {
+ return False;
+ }
+ unix_id->uid = pw->pw_uid;
+ TALLOC_FREE(pw);
+ return True;
+ }
+ TALLOC_FREE(sam_account);
+
+ ret = pdb_getgrsid(&map, sid);
+ unbecome_root();
+ /* END BECOME_ROOT BLOCK */
+
+ /* do not resolve SIDs to a name unless there is a valid
+ gid associated with it */
+
+ if ( ret && (map.gid != (gid_t)-1) ) {
+ *name = talloc_strdup(mem_ctx, map.nt_name);
+ *psid_name_use = map.sid_name_use;
+
+ if ( unix_id ) {
+ unix_id->gid = map.gid;
+ }
+
+ return True;
+ }
+
+ /* Windows will always map RID 513 to something. On a non-domain
+ controller, this gets mapped to SERVER\None. */
+
+ if ( unix_id ) {
+ DEBUG(5, ("Can't find a unix id for an unmapped group\n"));
+ return False;
+ }
+
+ if ( rid == DOMAIN_GROUP_RID_USERS ) {
+ *name = talloc_strdup(mem_ctx, "None" );
+ *psid_name_use = SID_NAME_DOM_GRP;
+
+ return True;
+ }
+
+ return False;
+}
+
+static NTSTATUS pdb_default_lookup_rids(struct pdb_methods *methods,
+ const DOM_SID *domain_sid,
+ int num_rids,
+ uint32 *rids,
+ const char **names,
+ enum lsa_SidType *attrs)
+{
+ int i;
+ NTSTATUS result;
+ bool have_mapped = False;
+ bool have_unmapped = False;
+
+ if (sid_check_is_builtin(domain_sid)) {
+
+ for (i=0; i<num_rids; i++) {
+ const char *name;
+
+ if (lookup_builtin_rid(names, rids[i], &name)) {
+ attrs[i] = SID_NAME_ALIAS;
+ names[i] = name;
+ DEBUG(5,("lookup_rids: %s:%d\n",
+ names[i], attrs[i]));
+ have_mapped = True;
+ } else {
+ have_unmapped = True;
+ attrs[i] = SID_NAME_UNKNOWN;
+ }
+ }
+ goto done;
+ }
+
+ /* Should not happen, but better check once too many */
+ if (!sid_check_is_domain(domain_sid)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ for (i = 0; i < num_rids; i++) {
+ const char *name;
+
+ if (lookup_global_sam_rid(names, rids[i], &name, &attrs[i],
+ NULL)) {
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ names[i] = name;
+ DEBUG(5,("lookup_rids: %s:%d\n", names[i], attrs[i]));
+ have_mapped = True;
+ } else {
+ have_unmapped = True;
+ attrs[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ done:
+
+ result = NT_STATUS_NONE_MAPPED;
+
+ if (have_mapped)
+ result = have_unmapped ? STATUS_SOME_UNMAPPED : NT_STATUS_OK;
+
+ return result;
+}
+
+#if 0
+static NTSTATUS pdb_default_lookup_names(struct pdb_methods *methods,
+ const DOM_SID *domain_sid,
+ int num_names,
+ const char **names,
+ uint32 *rids,
+ enum lsa_SidType *attrs)
+{
+ int i;
+ NTSTATUS result;
+ bool have_mapped = False;
+ bool have_unmapped = False;
+
+ if (sid_check_is_builtin(domain_sid)) {
+
+ for (i=0; i<num_names; i++) {
+ uint32 rid;
+
+ if (lookup_builtin_name(names[i], &rid)) {
+ attrs[i] = SID_NAME_ALIAS;
+ rids[i] = rid;
+ DEBUG(5,("lookup_rids: %s:%d\n",
+ names[i], attrs[i]));
+ have_mapped = True;
+ } else {
+ have_unmapped = True;
+ attrs[i] = SID_NAME_UNKNOWN;
+ }
+ }
+ goto done;
+ }
+
+ /* Should not happen, but better check once too many */
+ if (!sid_check_is_domain(domain_sid)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ if (lookup_global_sam_name(names[i], 0, &rids[i], &attrs[i])) {
+ DEBUG(5,("lookup_names: %s-> %d:%d\n", names[i],
+ rids[i], attrs[i]));
+ have_mapped = True;
+ } else {
+ have_unmapped = True;
+ attrs[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ done:
+
+ result = NT_STATUS_NONE_MAPPED;
+
+ if (have_mapped)
+ result = have_unmapped ? STATUS_SOME_UNMAPPED : NT_STATUS_OK;
+
+ return result;
+}
+#endif
+
+struct pdb_search *pdb_search_init(enum pdb_search_type type)
+{
+ TALLOC_CTX *mem_ctx;
+ struct pdb_search *result;
+
+ mem_ctx = talloc_init("pdb_search");
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_init failed\n"));
+ return NULL;
+ }
+
+ result = TALLOC_P(mem_ctx, struct pdb_search);
+ if (result == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return NULL;
+ }
+
+ result->mem_ctx = mem_ctx;
+ result->type = type;
+ result->cache = NULL;
+ result->num_entries = 0;
+ result->cache_size = 0;
+ result->search_ended = False;
+
+ /* Segfault appropriately if not initialized */
+ result->next_entry = NULL;
+ result->search_end = NULL;
+
+ return result;
+}
+
+static void fill_displayentry(TALLOC_CTX *mem_ctx, uint32 rid,
+ uint16 acct_flags,
+ const char *account_name,
+ const char *fullname,
+ const char *description,
+ struct samr_displayentry *entry)
+{
+ entry->rid = rid;
+ entry->acct_flags = acct_flags;
+
+ if (account_name != NULL)
+ entry->account_name = talloc_strdup(mem_ctx, account_name);
+ else
+ entry->account_name = "";
+
+ if (fullname != NULL)
+ entry->fullname = talloc_strdup(mem_ctx, fullname);
+ else
+ entry->fullname = "";
+
+ if (description != NULL)
+ entry->description = talloc_strdup(mem_ctx, description);
+ else
+ entry->description = "";
+}
+
+struct group_search {
+ GROUP_MAP *groups;
+ size_t num_groups, current_group;
+};
+
+static bool next_entry_groups(struct pdb_search *s,
+ struct samr_displayentry *entry)
+{
+ struct group_search *state = (struct group_search *)s->private_data;
+ uint32 rid;
+ GROUP_MAP *map = &state->groups[state->current_group];
+
+ if (state->current_group == state->num_groups)
+ return False;
+
+ sid_peek_rid(&map->sid, &rid);
+
+ fill_displayentry(s->mem_ctx, rid, 0, map->nt_name, NULL, map->comment,
+ entry);
+
+ state->current_group += 1;
+ return True;
+}
+
+static void search_end_groups(struct pdb_search *search)
+{
+ struct group_search *state =
+ (struct group_search *)search->private_data;
+ SAFE_FREE(state->groups);
+}
+
+static bool pdb_search_grouptype(struct pdb_search *search,
+ const DOM_SID *sid, enum lsa_SidType type)
+{
+ struct group_search *state;
+
+ state = TALLOC_P(search->mem_ctx, struct group_search);
+ if (state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ if (!pdb_enum_group_mapping(sid, type, &state->groups, &state->num_groups,
+ True)) {
+ DEBUG(0, ("Could not enum groups\n"));
+ return False;
+ }
+
+ state->current_group = 0;
+ search->private_data = state;
+ search->next_entry = next_entry_groups;
+ search->search_end = search_end_groups;
+ return True;
+}
+
+static bool pdb_default_search_groups(struct pdb_methods *methods,
+ struct pdb_search *search)
+{
+ return pdb_search_grouptype(search, get_global_sam_sid(), SID_NAME_DOM_GRP);
+}
+
+static bool pdb_default_search_aliases(struct pdb_methods *methods,
+ struct pdb_search *search,
+ const DOM_SID *sid)
+{
+
+ return pdb_search_grouptype(search, sid, SID_NAME_ALIAS);
+}
+
+static struct samr_displayentry *pdb_search_getentry(struct pdb_search *search,
+ uint32 idx)
+{
+ if (idx < search->num_entries)
+ return &search->cache[idx];
+
+ if (search->search_ended)
+ return NULL;
+
+ while (idx >= search->num_entries) {
+ struct samr_displayentry entry;
+
+ if (!search->next_entry(search, &entry)) {
+ search->search_end(search);
+ search->search_ended = True;
+ break;
+ }
+
+ ADD_TO_LARGE_ARRAY(search->mem_ctx, struct samr_displayentry,
+ entry, &search->cache, &search->num_entries,
+ &search->cache_size);
+ }
+
+ return (search->num_entries > idx) ? &search->cache[idx] : NULL;
+}
+
+struct pdb_search *pdb_search_users(uint32 acct_flags)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ struct pdb_search *result;
+
+ result = pdb_search_init(PDB_USER_SEARCH);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (!pdb->search_users(pdb, result, acct_flags)) {
+ talloc_destroy(result->mem_ctx);
+ return NULL;
+ }
+ return result;
+}
+
+struct pdb_search *pdb_search_groups(void)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ struct pdb_search *result;
+
+ result = pdb_search_init(PDB_GROUP_SEARCH);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (!pdb->search_groups(pdb, result)) {
+ talloc_destroy(result->mem_ctx);
+ return NULL;
+ }
+ return result;
+}
+
+struct pdb_search *pdb_search_aliases(const DOM_SID *sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ struct pdb_search *result;
+
+ if (pdb == NULL) return NULL;
+
+ result = pdb_search_init(PDB_ALIAS_SEARCH);
+ if (result == NULL) return NULL;
+
+ if (!pdb->search_aliases(pdb, result, sid)) {
+ talloc_destroy(result->mem_ctx);
+ return NULL;
+ }
+ return result;
+}
+
+uint32 pdb_search_entries(struct pdb_search *search,
+ uint32 start_idx, uint32 max_entries,
+ struct samr_displayentry **result)
+{
+ struct samr_displayentry *end_entry;
+ uint32 end_idx = start_idx+max_entries-1;
+
+ /* The first entry needs to be searched after the last. Otherwise the
+ * first entry might have moved due to a realloc during the search for
+ * the last entry. */
+
+ end_entry = pdb_search_getentry(search, end_idx);
+ *result = pdb_search_getentry(search, start_idx);
+
+ if (end_entry != NULL)
+ return max_entries;
+
+ if (start_idx >= search->num_entries)
+ return 0;
+
+ return search->num_entries - start_idx;
+}
+
+void pdb_search_destroy(struct pdb_search *search)
+{
+ if (search == NULL)
+ return;
+
+ if (!search->search_ended)
+ search->search_end(search);
+
+ talloc_destroy(search->mem_ctx);
+}
+
+/*******************************************************************
+ trustodm methods
+ *******************************************************************/
+
+bool pdb_get_trusteddom_pw(const char *domain, char** pwd, DOM_SID *sid,
+ time_t *pass_last_set_time)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->get_trusteddom_pw(pdb, domain, pwd, sid,
+ pass_last_set_time);
+}
+
+bool pdb_set_trusteddom_pw(const char* domain, const char* pwd,
+ const DOM_SID *sid)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->set_trusteddom_pw(pdb, domain, pwd, sid);
+}
+
+bool pdb_del_trusteddom_pw(const char *domain)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->del_trusteddom_pw(pdb, domain);
+}
+
+NTSTATUS pdb_enum_trusteddoms(TALLOC_CTX *mem_ctx, uint32 *num_domains,
+ struct trustdom_info ***domains)
+{
+ struct pdb_methods *pdb = pdb_get_methods();
+ return pdb->enum_trusteddoms(pdb, mem_ctx, num_domains, domains);
+}
+
+/*******************************************************************
+ the defaults for trustdom methods:
+ these simply call the original passdb/secrets.c actions,
+ to be replaced by pdb_ldap.
+ *******************************************************************/
+
+static bool pdb_default_get_trusteddom_pw(struct pdb_methods *methods,
+ const char *domain,
+ char** pwd,
+ DOM_SID *sid,
+ time_t *pass_last_set_time)
+{
+ return secrets_fetch_trusted_domain_password(domain, pwd,
+ sid, pass_last_set_time);
+
+}
+
+static bool pdb_default_set_trusteddom_pw(struct pdb_methods *methods,
+ const char* domain,
+ const char* pwd,
+ const DOM_SID *sid)
+{
+ return secrets_store_trusted_domain_password(domain, pwd, sid);
+}
+
+static bool pdb_default_del_trusteddom_pw(struct pdb_methods *methods,
+ const char *domain)
+{
+ return trusted_domain_password_delete(domain);
+}
+
+static NTSTATUS pdb_default_enum_trusteddoms(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ struct trustdom_info ***domains)
+{
+ return secrets_trusted_domains(mem_ctx, num_domains, domains);
+}
+
+/*******************************************************************
+ Create a pdb_methods structure and initialize it with the default
+ operations. In this way a passdb module can simply implement
+ the functionality it cares about. However, normally this is done
+ in groups of related functions.
+*******************************************************************/
+
+NTSTATUS make_pdb_method( struct pdb_methods **methods )
+{
+ /* allocate memory for the structure as its own talloc CTX */
+
+ if ( !(*methods = TALLOC_ZERO_P(NULL, struct pdb_methods) ) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*methods)->getsampwnam = pdb_default_getsampwnam;
+ (*methods)->getsampwsid = pdb_default_getsampwsid;
+ (*methods)->create_user = pdb_default_create_user;
+ (*methods)->delete_user = pdb_default_delete_user;
+ (*methods)->add_sam_account = pdb_default_add_sam_account;
+ (*methods)->update_sam_account = pdb_default_update_sam_account;
+ (*methods)->delete_sam_account = pdb_default_delete_sam_account;
+ (*methods)->rename_sam_account = pdb_default_rename_sam_account;
+ (*methods)->update_login_attempts = pdb_default_update_login_attempts;
+
+ (*methods)->getgrsid = pdb_default_getgrsid;
+ (*methods)->getgrgid = pdb_default_getgrgid;
+ (*methods)->getgrnam = pdb_default_getgrnam;
+ (*methods)->create_dom_group = pdb_default_create_dom_group;
+ (*methods)->delete_dom_group = pdb_default_delete_dom_group;
+ (*methods)->add_group_mapping_entry = pdb_default_add_group_mapping_entry;
+ (*methods)->update_group_mapping_entry = pdb_default_update_group_mapping_entry;
+ (*methods)->delete_group_mapping_entry = pdb_default_delete_group_mapping_entry;
+ (*methods)->enum_group_mapping = pdb_default_enum_group_mapping;
+ (*methods)->enum_group_members = pdb_default_enum_group_members;
+ (*methods)->enum_group_memberships = pdb_default_enum_group_memberships;
+ (*methods)->set_unix_primary_group = pdb_default_set_unix_primary_group;
+ (*methods)->add_groupmem = pdb_default_add_groupmem;
+ (*methods)->del_groupmem = pdb_default_del_groupmem;
+ (*methods)->create_alias = pdb_default_create_alias;
+ (*methods)->delete_alias = pdb_default_delete_alias;
+ (*methods)->get_aliasinfo = pdb_default_get_aliasinfo;
+ (*methods)->set_aliasinfo = pdb_default_set_aliasinfo;
+ (*methods)->add_aliasmem = pdb_default_add_aliasmem;
+ (*methods)->del_aliasmem = pdb_default_del_aliasmem;
+ (*methods)->enum_aliasmem = pdb_default_enum_aliasmem;
+ (*methods)->enum_alias_memberships = pdb_default_alias_memberships;
+ (*methods)->lookup_rids = pdb_default_lookup_rids;
+ (*methods)->get_account_policy = pdb_default_get_account_policy;
+ (*methods)->set_account_policy = pdb_default_set_account_policy;
+ (*methods)->get_seq_num = pdb_default_get_seq_num;
+ (*methods)->uid_to_rid = pdb_default_uid_to_rid;
+ (*methods)->uid_to_sid = pdb_default_uid_to_sid;
+ (*methods)->gid_to_sid = pdb_default_gid_to_sid;
+ (*methods)->sid_to_id = pdb_default_sid_to_id;
+
+ (*methods)->search_groups = pdb_default_search_groups;
+ (*methods)->search_aliases = pdb_default_search_aliases;
+
+ (*methods)->get_trusteddom_pw = pdb_default_get_trusteddom_pw;
+ (*methods)->set_trusteddom_pw = pdb_default_set_trusteddom_pw;
+ (*methods)->del_trusteddom_pw = pdb_default_del_trusteddom_pw;
+ (*methods)->enum_trusteddoms = pdb_default_enum_trusteddoms;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c
new file mode 100644
index 0000000000..ddbb53a9b9
--- /dev/null
+++ b/source3/passdb/pdb_ldap.c
@@ -0,0 +1,6389 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 2002-2003
+ Copyright (C) Simo Sorce 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/* TODO:
+* persistent connections: if using NSS LDAP, many connections are made
+* however, using only one within Samba would be nice
+*
+* Clean up SSL stuff, compile on OpenLDAP 1.x, 2.x, and Netscape SDK
+*
+* Other LDAP based login attributes: accountExpires, etc.
+* (should be the domain of Samba proper, but the sam_password/struct samu
+* structures don't have fields for some of these attributes)
+*
+* SSL is done, but can't get the certificate based authentication to work
+* against on my test platform (Linux 2.4, OpenLDAP 2.x)
+*/
+
+/* NOTE: this will NOT work against an Active Directory server
+* due to the fact that the two password fields cannot be retrieved
+* from a server; recommend using security = domain in this situation
+* and/or winbind
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#include <lber.h>
+#include <ldap.h>
+
+/*
+ * Work around versions of the LDAP client libs that don't have the OIDs
+ * defined, or have them defined under the old name.
+ * This functionality is really a factor of the server, not the client
+ *
+ */
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD) && !defined(LDAP_EXOP_MODIFY_PASSWD)
+#define LDAP_EXOP_MODIFY_PASSWD LDAP_EXOP_X_MODIFY_PASSWD
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD)
+#define LDAP_EXOP_MODIFY_PASSWD "1.3.6.1.4.1.4203.1.11.1"
+#endif
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD_ID) && !defined(LDAP_EXOP_MODIFY_PASSWD_ID)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID LDAP_EXOP_X_MODIFY_PASSWD_ID
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD_ID)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID ((ber_tag_t) 0x80U)
+#endif
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD_NEW) && !defined(LDAP_EXOP_MODIFY_PASSWD_NEW)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW LDAP_EXOP_X_MODIFY_PASSWD_NEW
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD_NEW)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ((ber_tag_t) 0x82U)
+#endif
+
+
+#include "smbldap.h"
+
+/**********************************************************************
+ Simple helper function to make stuff better readable
+ **********************************************************************/
+
+static LDAP *priv2ld(struct ldapsam_privates *priv)
+{
+ return priv->smbldap_state->ldap_struct;
+}
+
+/**********************************************************************
+ Get the attribute name given a user schame version.
+ **********************************************************************/
+
+static const char* get_userattr_key2string( int schema_ver, int key )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_key2string( attrib_map_v22, key );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_key2string( attrib_map_v30, key );
+
+ default:
+ DEBUG(0,("get_userattr_key2string: unknown schema version specified\n"));
+ break;
+ }
+ return NULL;
+}
+
+/**********************************************************************
+ Return the list of attribute names given a user schema version.
+**********************************************************************/
+
+const char** get_userattr_list( TALLOC_CTX *mem_ctx, int schema_ver )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_list( mem_ctx, attrib_map_v22 );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_list( mem_ctx, attrib_map_v30 );
+ default:
+ DEBUG(0,("get_userattr_list: unknown schema version specified!\n"));
+ break;
+ }
+
+ return NULL;
+}
+
+/**************************************************************************
+ Return the list of attribute names to delete given a user schema version.
+**************************************************************************/
+
+static const char** get_userattr_delete_list( TALLOC_CTX *mem_ctx,
+ int schema_ver )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_list( mem_ctx,
+ attrib_map_to_delete_v22 );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_list( mem_ctx,
+ attrib_map_to_delete_v30 );
+ default:
+ DEBUG(0,("get_userattr_delete_list: unknown schema version specified!\n"));
+ break;
+ }
+
+ return NULL;
+}
+
+
+/*******************************************************************
+ Generate the LDAP search filter for the objectclass based on the
+ version of the schema we are using.
+******************************************************************/
+
+static const char* get_objclass_filter( int schema_ver )
+{
+ fstring objclass_filter;
+ char *result;
+
+ switch( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ fstr_sprintf( objclass_filter, "(objectclass=%s)", LDAP_OBJ_SAMBAACCOUNT );
+ break;
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ fstr_sprintf( objclass_filter, "(objectclass=%s)", LDAP_OBJ_SAMBASAMACCOUNT );
+ break;
+ default:
+ DEBUG(0,("get_objclass_filter: Invalid schema version specified!\n"));
+ objclass_filter[0] = '\0';
+ break;
+ }
+
+ result = talloc_strdup(talloc_tos(), objclass_filter);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*****************************************************************
+ Scan a sequence number off OpenLDAP's syncrepl contextCSN
+******************************************************************/
+
+static NTSTATUS ldapsam_get_seq_num(struct pdb_methods *my_methods, time_t *seq_num)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ntstatus = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *msg = NULL;
+ LDAPMessage *entry = NULL;
+ TALLOC_CTX *mem_ctx;
+ char **values = NULL;
+ int rc, num_result, num_values, rid;
+ char *suffix = NULL;
+ char *tok;
+ const char *p;
+ const char **attrs;
+
+ /* Unfortunatly there is no proper way to detect syncrepl-support in
+ * smbldap_connect_system(). The syncrepl OIDs are submitted for publication
+ * but do not show up in the root-DSE yet. Neither we can query the
+ * subschema-context for the syncProviderSubentry or syncConsumerSubentry
+ * objectclass. Currently we require lp_ldap_suffix() to show up as
+ * namingContext. - Guenther
+ */
+
+ if (!lp_parm_bool(-1, "ldapsam", "syncrepl_seqnum", False)) {
+ return ntstatus;
+ }
+
+ if (!seq_num) {
+ DEBUG(3,("ldapsam_get_seq_num: no sequence_number\n"));
+ return ntstatus;
+ }
+
+ if (!smbldap_has_naming_context(ldap_state->smbldap_state->ldap_struct, lp_ldap_suffix())) {
+ DEBUG(3,("ldapsam_get_seq_num: DIT not configured to hold %s "
+ "as top-level namingContext\n", lp_ldap_suffix()));
+ return ntstatus;
+ }
+
+ mem_ctx = talloc_init("ldapsam_get_seq_num");
+
+ if (mem_ctx == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((attrs = TALLOC_ARRAY(mem_ctx, const char *, 2)) == NULL) {
+ ntstatus = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* if we got a syncrepl-rid (up to three digits long) we speak with a consumer */
+ rid = lp_parm_int(-1, "ldapsam", "syncrepl_rid", -1);
+ if (rid > 0) {
+
+ /* consumer syncreplCookie: */
+ /* csn=20050126161620Z#0000001#00#00000 */
+ attrs[0] = talloc_strdup(mem_ctx, "syncreplCookie");
+ attrs[1] = NULL;
+ suffix = talloc_asprintf(mem_ctx,
+ "cn=syncrepl%d,%s", rid, lp_ldap_suffix());
+ if (!suffix) {
+ ntstatus = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ } else {
+
+ /* provider contextCSN */
+ /* 20050126161620Z#000009#00#000000 */
+ attrs[0] = talloc_strdup(mem_ctx, "contextCSN");
+ attrs[1] = NULL;
+ suffix = talloc_asprintf(mem_ctx,
+ "cn=ldapsync,%s", lp_ldap_suffix());
+
+ if (!suffix) {
+ ntstatus = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ rc = smbldap_search(ldap_state->smbldap_state, suffix,
+ LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0, &msg);
+
+ if (rc != LDAP_SUCCESS) {
+ goto done;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, msg);
+ if (num_result != 1) {
+ DEBUG(3,("ldapsam_get_seq_num: Expected one entry, got %d\n", num_result));
+ goto done;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, msg);
+ if (entry == NULL) {
+ DEBUG(3,("ldapsam_get_seq_num: Could not retrieve entry\n"));
+ goto done;
+ }
+
+ values = ldap_get_values(ldap_state->smbldap_state->ldap_struct, entry, attrs[0]);
+ if (values == NULL) {
+ DEBUG(3,("ldapsam_get_seq_num: no values\n"));
+ goto done;
+ }
+
+ num_values = ldap_count_values(values);
+ if (num_values == 0) {
+ DEBUG(3,("ldapsam_get_seq_num: not a single value\n"));
+ goto done;
+ }
+
+ p = values[0];
+ if (!next_token_talloc(mem_ctx, &p, &tok, "#")) {
+ DEBUG(0,("ldapsam_get_seq_num: failed to parse sequence number\n"));
+ goto done;
+ }
+
+ p = tok;
+ if (!strncmp(p, "csn=", strlen("csn=")))
+ p += strlen("csn=");
+
+ DEBUG(10,("ldapsam_get_seq_num: got %s: %s\n", attrs[0], p));
+
+ *seq_num = generalized_to_unix_time(p);
+
+ /* very basic sanity check */
+ if (*seq_num <= 0) {
+ DEBUG(3,("ldapsam_get_seq_num: invalid sequence number: %d\n",
+ (int)*seq_num));
+ goto done;
+ }
+
+ ntstatus = NT_STATUS_OK;
+
+ done:
+ if (values != NULL)
+ ldap_value_free(values);
+ if (msg != NULL)
+ ldap_msgfree(msg);
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return ntstatus;
+}
+
+/*******************************************************************
+ Run the search by name.
+******************************************************************/
+
+int ldapsam_search_suffix_by_name(struct ldapsam_privates *ldap_state,
+ const char *user,
+ LDAPMessage ** result,
+ const char **attr)
+{
+ char *filter = NULL;
+ char *escape_user = escape_ldap_string_alloc(user);
+ int ret = -1;
+
+ if (!escape_user) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /*
+ * in the filter expression, replace %u with the real name
+ * so in ldap filter, %u MUST exist :-)
+ */
+ filter = talloc_asprintf(talloc_tos(), "(&%s%s)", "(uid=%u)",
+ get_objclass_filter(ldap_state->schema_ver));
+ if (!filter) {
+ SAFE_FREE(escape_user);
+ return LDAP_NO_MEMORY;
+ }
+ /*
+ * have to use this here because $ is filtered out
+ * in string_sub
+ */
+
+ filter = talloc_all_string_sub(talloc_tos(),
+ filter, "%u", escape_user);
+ SAFE_FREE(escape_user);
+ if (!filter) {
+ return LDAP_NO_MEMORY;
+ }
+
+ ret = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr, result);
+ TALLOC_FREE(filter);
+ return ret;
+}
+
+/*******************************************************************
+ Run the search by rid.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_rid (struct ldapsam_privates *ldap_state,
+ uint32 rid, LDAPMessage ** result,
+ const char **attr)
+{
+ char *filter = NULL;
+ int rc;
+
+ filter = talloc_asprintf(talloc_tos(), "(&(rid=%i)%s)", rid,
+ get_objclass_filter(ldap_state->schema_ver));
+ if (!filter) {
+ return LDAP_NO_MEMORY;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr, result);
+ TALLOC_FREE(filter);
+ return rc;
+}
+
+/*******************************************************************
+ Run the search by SID.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_sid (struct ldapsam_privates *ldap_state,
+ const DOM_SID *sid, LDAPMessage ** result,
+ const char **attr)
+{
+ char *filter = NULL;
+ int rc;
+ fstring sid_string;
+
+ filter = talloc_asprintf(talloc_tos(), "(&(%s=%s)%s)",
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_SID),
+ sid_to_fstring(sid_string, sid),
+ get_objclass_filter(ldap_state->schema_ver));
+ if (!filter) {
+ return LDAP_NO_MEMORY;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr, result);
+
+ TALLOC_FREE(filter);
+ return rc;
+}
+
+/*******************************************************************
+ Delete complete object or objectclass and attrs from
+ object found in search_result depending on lp_ldap_delete_dn
+******************************************************************/
+
+static int ldapsam_delete_entry(struct ldapsam_privates *priv,
+ TALLOC_CTX *mem_ctx,
+ LDAPMessage *entry,
+ const char *objectclass,
+ const char **attrs)
+{
+ LDAPMod **mods = NULL;
+ char *name;
+ const char *dn;
+ BerElement *ptr = NULL;
+
+ dn = smbldap_talloc_dn(mem_ctx, priv2ld(priv), entry);
+ if (dn == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ if (lp_ldap_delete_dn()) {
+ return smbldap_delete(priv->smbldap_state, dn);
+ }
+
+ /* Ok, delete only the SAM attributes */
+
+ for (name = ldap_first_attribute(priv2ld(priv), entry, &ptr);
+ name != NULL;
+ name = ldap_next_attribute(priv2ld(priv), entry, ptr)) {
+ const char **attrib;
+
+ /* We are only allowed to delete the attributes that
+ really exist. */
+
+ for (attrib = attrs; *attrib != NULL; attrib++) {
+ if (strequal(*attrib, name)) {
+ DEBUG(10, ("ldapsam_delete_entry: deleting "
+ "attribute %s\n", name));
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, name,
+ NULL);
+ }
+ }
+ ldap_memfree(name);
+ }
+
+ if (ptr != NULL) {
+ ber_free(ptr, 0);
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, "objectClass", objectclass);
+ talloc_autofree_ldapmod(mem_ctx, mods);
+
+ return smbldap_modify(priv->smbldap_state, dn, mods);
+}
+
+static time_t ldapsam_get_entry_timestamp( struct ldapsam_privates *ldap_state, LDAPMessage * entry)
+{
+ char *temp;
+ struct tm tm;
+
+ temp = smbldap_talloc_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver,LDAP_ATTR_MOD_TIMESTAMP),
+ talloc_tos());
+ if (!temp) {
+ return (time_t) 0;
+ }
+
+ if ( !strptime(temp, "%Y%m%d%H%M%SZ", &tm)) {
+ DEBUG(2,("ldapsam_get_entry_timestamp: strptime failed on: %s\n",
+ (char*)temp));
+ TALLOC_FREE(temp);
+ return (time_t) 0;
+ }
+ TALLOC_FREE(temp);
+ tzset();
+ return timegm(&tm);
+}
+
+/**********************************************************************
+ Initialize struct samu from an LDAP query.
+ (Based on init_sam_from_buffer in pdb_tdb.c)
+*********************************************************************/
+
+static bool init_sam_from_ldap(struct ldapsam_privates *ldap_state,
+ struct samu * sampass,
+ LDAPMessage * entry)
+{
+ time_t logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ ldap_entry_time,
+ bad_password_time;
+ char *username = NULL,
+ *domain = NULL,
+ *nt_username = NULL,
+ *fullname = NULL,
+ *homedir = NULL,
+ *dir_drive = NULL,
+ *logon_script = NULL,
+ *profile_path = NULL,
+ *acct_desc = NULL,
+ *workstations = NULL,
+ *munged_dial = NULL;
+ uint32 user_rid;
+ uint8 smblmpwd[LM_HASH_LEN],
+ smbntpwd[NT_HASH_LEN];
+ bool use_samba_attrs = True;
+ uint32 acct_ctrl = 0;
+ uint16 logon_divs;
+ uint16 bad_password_count = 0,
+ logon_count = 0;
+ uint32 hours_len;
+ uint8 hours[MAX_HOURS_LEN];
+ char *temp = NULL;
+ LOGIN_CACHE *cache_entry = NULL;
+ uint32 pwHistLen;
+ bool expand_explicit = lp_passdb_expand_explicit();
+ bool ret = false;
+ TALLOC_CTX *ctx = talloc_init("init_sam_from_ldap");
+
+ if (!ctx) {
+ return false;
+ }
+ if (sampass == NULL || ldap_state == NULL || entry == NULL) {
+ DEBUG(0, ("init_sam_from_ldap: NULL parameters found!\n"));
+ goto fn_exit;
+ }
+
+ if (priv2ld(ldap_state) == NULL) {
+ DEBUG(0, ("init_sam_from_ldap: ldap_state->smbldap_state->"
+ "ldap_struct is NULL!\n"));
+ goto fn_exit;
+ }
+
+ if (!(username = smbldap_talloc_single_attribute(priv2ld(ldap_state),
+ entry,
+ "uid",
+ ctx))) {
+ DEBUG(1, ("init_sam_from_ldap: No uid attribute found for "
+ "this user!\n"));
+ goto fn_exit;
+ }
+
+ DEBUG(2, ("init_sam_from_ldap: Entry found for user: %s\n", username));
+
+ nt_username = talloc_strdup(ctx, username);
+ if (!nt_username) {
+ goto fn_exit;
+ }
+
+ domain = talloc_strdup(ctx, ldap_state->domain_name);
+ if (!domain) {
+ goto fn_exit;
+ }
+
+ pdb_set_username(sampass, username, PDB_SET);
+
+ pdb_set_domain(sampass, domain, PDB_DEFAULT);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+
+ /* deal with different attributes between the schema first */
+
+ if ( ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT ) {
+ if ((temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_SID),
+ ctx))!=NULL) {
+ pdb_set_user_sid_from_string(sampass, temp, PDB_SET);
+ }
+ } else {
+ if ((temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_RID),
+ ctx))!=NULL) {
+ user_rid = (uint32)atol(temp);
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ }
+ }
+
+ if (pdb_get_init_flags(sampass,PDB_USERSID) == PDB_DEFAULT) {
+ DEBUG(1, ("init_sam_from_ldap: no %s or %s attribute found for this user %s\n",
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_SID),
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_RID),
+ username));
+ return False;
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PWD_LAST_SET),
+ ctx);
+ if (temp) {
+ pass_last_set_time = (time_t) atol(temp);
+ pdb_set_pass_last_set_time(sampass,
+ pass_last_set_time, PDB_SET);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LOGON_TIME),
+ ctx);
+ if (temp) {
+ logon_time = (time_t) atol(temp);
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LOGOFF_TIME),
+ ctx);
+ if (temp) {
+ logoff_time = (time_t) atol(temp);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_KICKOFF_TIME),
+ ctx);
+ if (temp) {
+ kickoff_time = (time_t) atol(temp);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PWD_CAN_CHANGE),
+ ctx);
+ if (temp) {
+ pass_can_change_time = (time_t) atol(temp);
+ pdb_set_pass_can_change_time(sampass,
+ pass_can_change_time, PDB_SET);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PWD_MUST_CHANGE),
+ ctx);
+ if (temp) {
+ pass_must_change_time = (time_t) atol(temp);
+ pdb_set_pass_must_change_time(sampass,
+ pass_must_change_time, PDB_SET);
+ }
+
+ /* recommend that 'gecos' and 'displayName' should refer to the same
+ * attribute OID. userFullName depreciated, only used by Samba
+ * primary rules of LDAP: don't make a new attribute when one is already defined
+ * that fits your needs; using cn then displayName rather than 'userFullName'
+ */
+
+ fullname = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_DISPLAY_NAME),
+ ctx);
+ if (fullname) {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ } else {
+ fullname = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_CN),
+ ctx);
+ if (fullname) {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ }
+ }
+
+ dir_drive = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_HOME_DRIVE),
+ ctx);
+ if (dir_drive) {
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ } else {
+ pdb_set_dir_drive( sampass, lp_logon_drive(), PDB_DEFAULT );
+ }
+
+ homedir = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_HOME_PATH),
+ ctx);
+ if (homedir) {
+ if (expand_explicit) {
+ homedir = talloc_sub_basic(ctx,
+ username,
+ domain,
+ homedir);
+ if (!homedir) {
+ goto fn_exit;
+ }
+ }
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ } else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(ctx, username, domain,
+ lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ logon_script = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LOGON_SCRIPT),
+ ctx);
+ if (logon_script) {
+ if (expand_explicit) {
+ logon_script = talloc_sub_basic(ctx,
+ username,
+ domain,
+ logon_script);
+ if (!logon_script) {
+ goto fn_exit;
+ }
+ }
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ } else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(ctx, username, domain,
+ lp_logon_script()),
+ PDB_DEFAULT );
+ }
+
+ profile_path = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PROFILE_PATH),
+ ctx);
+ if (profile_path) {
+ if (expand_explicit) {
+ profile_path = talloc_sub_basic(ctx,
+ username,
+ domain,
+ profile_path);
+ if (!profile_path) {
+ goto fn_exit;
+ }
+ }
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(ctx, username, domain,
+ lp_logon_path()),
+ PDB_DEFAULT );
+ }
+
+ acct_desc = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_DESC),
+ ctx);
+ if (acct_desc) {
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ }
+
+ workstations = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_WKS),
+ ctx);
+ if (workstations) {
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ }
+
+ munged_dial = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_MUNGED_DIAL),
+ ctx);
+ if (munged_dial) {
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+ }
+
+ /* FIXME: hours stuff should be cleaner */
+
+ logon_divs = 168;
+ hours_len = 21;
+ memset(hours, 0xff, hours_len);
+
+ if (ldap_state->is_nds_ldap) {
+ char *user_dn;
+ size_t pwd_len;
+ char clear_text_pw[512];
+
+ /* Make call to Novell eDirectory ldap extension to get clear text password.
+ NOTE: This will only work if we have an SSL connection to eDirectory. */
+ user_dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (user_dn != NULL) {
+ DEBUG(3, ("init_sam_from_ldap: smbldap_get_dn(%s) returned '%s'\n", username, user_dn));
+
+ pwd_len = sizeof(clear_text_pw);
+ if (pdb_nds_get_password(ldap_state->smbldap_state, user_dn, &pwd_len, clear_text_pw) == LDAP_SUCCESS) {
+ nt_lm_owf_gen(clear_text_pw, smbntpwd, smblmpwd);
+ if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET)) {
+ SAFE_FREE(user_dn);
+ return False;
+ }
+ ZERO_STRUCT(smblmpwd);
+ if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET)) {
+ SAFE_FREE(user_dn);
+ return False;
+ }
+ ZERO_STRUCT(smbntpwd);
+ use_samba_attrs = False;
+ }
+
+ SAFE_FREE(user_dn);
+
+ } else {
+ DEBUG(0, ("init_sam_from_ldap: failed to get user_dn for '%s'\n", username));
+ }
+ }
+
+ if (use_samba_attrs) {
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LMPW),
+ ctx);
+ if (temp) {
+ pdb_gethexpwd(temp, smblmpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET)) {
+ goto fn_exit;
+ }
+ ZERO_STRUCT(smblmpwd);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_NTPW),
+ ctx);
+ if (temp) {
+ pdb_gethexpwd(temp, smbntpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET)) {
+ goto fn_exit;
+ }
+ ZERO_STRUCT(smbntpwd);
+ }
+ }
+
+ pwHistLen = 0;
+
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHistLen);
+ if (pwHistLen > 0){
+ uint8 *pwhist = NULL;
+ int i;
+ char *history_string = TALLOC_ARRAY(ctx, char,
+ MAX_PW_HISTORY_LEN*64);
+
+ if (!history_string) {
+ goto fn_exit;
+ }
+
+ pwHistLen = MIN(pwHistLen, MAX_PW_HISTORY_LEN);
+
+ if ((pwhist = TALLOC_ARRAY(ctx, uint8,
+ pwHistLen * PW_HISTORY_ENTRY_LEN)) ==
+ NULL){
+ DEBUG(0, ("init_sam_from_ldap: talloc failed!\n"));
+ goto fn_exit;
+ }
+ memset(pwhist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
+
+ if (smbldap_get_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PWD_HISTORY),
+ history_string,
+ MAX_PW_HISTORY_LEN*64)) {
+ bool hex_failed = false;
+ for (i = 0; i < pwHistLen; i++){
+ /* Get the 16 byte salt. */
+ if (!pdb_gethexpwd(&history_string[i*64],
+ &pwhist[i*PW_HISTORY_ENTRY_LEN])) {
+ hex_failed = true;
+ break;
+ }
+ /* Get the 16 byte MD5 hash of salt+passwd. */
+ if (!pdb_gethexpwd(&history_string[(i*64)+32],
+ &pwhist[(i*PW_HISTORY_ENTRY_LEN)+
+ PW_HISTORY_SALT_LEN])) {
+ hex_failed = True;
+ break;
+ }
+ }
+ if (hex_failed) {
+ DEBUG(2,("init_sam_from_ldap: Failed to get password history for user %s\n",
+ username));
+ memset(pwhist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
+ }
+ }
+ if (!pdb_set_pw_history(sampass, pwhist, pwHistLen, PDB_SET)){
+ goto fn_exit;
+ }
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_ACB_INFO),
+ ctx);
+ if (temp) {
+ acct_ctrl = pdb_decode_acct_ctrl(temp);
+
+ if (acct_ctrl == 0) {
+ acct_ctrl |= ACB_NORMAL;
+ }
+
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ } else {
+ acct_ctrl |= ACB_NORMAL;
+ }
+
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_COUNT),
+ ctx);
+ if (temp) {
+ bad_password_count = (uint32) atol(temp);
+ pdb_set_bad_password_count(sampass,
+ bad_password_count, PDB_SET);
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_TIME),
+ ctx);
+ if (temp) {
+ bad_password_time = (time_t) atol(temp);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ }
+
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LOGON_COUNT),
+ ctx);
+ if (temp) {
+ logon_count = (uint32) atol(temp);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ }
+
+ /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LOGON_HOURS),
+ ctx);
+ if (temp) {
+ pdb_gethexhours(temp, hours);
+ memset((char *)temp, '\0', strlen(temp) +1);
+ pdb_set_hours(sampass, hours, PDB_SET);
+ ZERO_STRUCT(hours);
+ }
+
+ if (lp_parm_bool(-1, "ldapsam", "trusted", False)) {
+ temp = smbldap_talloc_single_attribute(
+ priv2ld(ldap_state),
+ entry,
+ "uidNumber",
+ ctx);
+ if (temp) {
+ /* We've got a uid, feed the cache */
+ uid_t uid = strtoul(temp, NULL, 10);
+ store_uid_sid_cache(pdb_get_user_sid(sampass), uid);
+ }
+ }
+
+ /* check the timestamp of the cache vs ldap entry */
+ if (!(ldap_entry_time = ldapsam_get_entry_timestamp(ldap_state,
+ entry))) {
+ ret = true;
+ goto fn_exit;
+ }
+
+ /* see if we have newer updates */
+ if (!(cache_entry = login_cache_read(sampass))) {
+ DEBUG (9, ("No cache entry, bad count = %u, bad time = %u\n",
+ (unsigned int)pdb_get_bad_password_count(sampass),
+ (unsigned int)pdb_get_bad_password_time(sampass)));
+ ret = true;
+ goto fn_exit;
+ }
+
+ DEBUG(7, ("ldap time is %u, cache time is %u, bad time = %u\n",
+ (unsigned int)ldap_entry_time,
+ (unsigned int)cache_entry->entry_timestamp,
+ (unsigned int)cache_entry->bad_password_time));
+
+ if (ldap_entry_time > cache_entry->entry_timestamp) {
+ /* cache is older than directory , so
+ we need to delete the entry but allow the
+ fields to be written out */
+ login_cache_delentry(sampass);
+ } else {
+ /* read cache in */
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) |
+ (cache_entry->acct_ctrl & ACB_AUTOLOCK),
+ PDB_SET);
+ pdb_set_bad_password_count(sampass,
+ cache_entry->bad_password_count,
+ PDB_SET);
+ pdb_set_bad_password_time(sampass,
+ cache_entry->bad_password_time,
+ PDB_SET);
+ }
+
+ ret = true;
+
+ fn_exit:
+
+ TALLOC_FREE(ctx);
+ SAFE_FREE(cache_entry);
+ return ret;
+}
+
+/**********************************************************************
+ Initialize the ldap db from a struct samu. Called on update.
+ (Based on init_buffer_from_sam in pdb_tdb.c)
+*********************************************************************/
+
+static bool init_ldap_from_sam (struct ldapsam_privates *ldap_state,
+ LDAPMessage *existing,
+ LDAPMod *** mods, struct samu * sampass,
+ bool (*need_update)(const struct samu *,
+ enum pdb_elements))
+{
+ char *temp = NULL;
+ uint32 rid;
+
+ if (mods == NULL || sampass == NULL) {
+ DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
+ return False;
+ }
+
+ *mods = NULL;
+
+ /*
+ * took out adding "objectclass: sambaAccount"
+ * do this on a per-mod basis
+ */
+ if (need_update(sampass, PDB_USERNAME)) {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ "uid", pdb_get_username(sampass));
+ if (ldap_state->is_nds_ldap) {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ "cn", pdb_get_username(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ "sn", pdb_get_username(sampass));
+ }
+ }
+
+ DEBUG(2, ("init_ldap_from_sam: Setting entry for user: %s\n", pdb_get_username(sampass)));
+
+ /* only update the RID if we actually need to */
+ if (need_update(sampass, PDB_USERSID)) {
+ fstring sid_string;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, user_sid, &rid)) {
+ DEBUG(1, ("init_ldap_from_sam: User's SID (%s) is not for this domain (%s), cannot add to LDAP!\n",
+ sid_string_dbg(user_sid),
+ sid_string_dbg(
+ &ldap_state->domain_sid)));
+ return False;
+ }
+ if (asprintf(&temp, "%i", rid) < 0) {
+ return false;
+ }
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID),
+ temp);
+ SAFE_FREE(temp);
+ break;
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_fstring(sid_string, user_sid));
+ break;
+
+ default:
+ DEBUG(0,("init_ldap_from_sam: unknown schema version specified\n"));
+ break;
+ }
+ }
+
+ /* we don't need to store the primary group RID - so leaving it
+ 'free' to hang off the unix primary group makes life easier */
+
+ if (need_update(sampass, PDB_GROUPSID)) {
+ fstring sid_string;
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, group_sid, &rid)) {
+ DEBUG(1, ("init_ldap_from_sam: User's Primary Group SID (%s) is not for this domain (%s), cannot add to LDAP!\n",
+ sid_string_dbg(group_sid),
+ sid_string_dbg(
+ &ldap_state->domain_sid)));
+ return False;
+ }
+
+ if (asprintf(&temp, "%i", rid) < 0) {
+ return false;
+ }
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PRIMARY_GROUP_RID), temp);
+ SAFE_FREE(temp);
+ break;
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PRIMARY_GROUP_SID), sid_to_fstring(sid_string, group_sid));
+ break;
+
+ default:
+ DEBUG(0,("init_ldap_from_sam: unknown schema version specified\n"));
+ break;
+ }
+
+ }
+
+ /* displayName, cn, and gecos should all be the same
+ * most easily accomplished by giving them the same OID
+ * gecos isn't set here b/c it should be handled by the
+ * add-user script
+ * We change displayName only and fall back to cn if
+ * it does not exist.
+ */
+
+ if (need_update(sampass, PDB_FULLNAME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DISPLAY_NAME),
+ pdb_get_fullname(sampass));
+
+ if (need_update(sampass, PDB_ACCTDESC))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DESC),
+ pdb_get_acct_desc(sampass));
+
+ if (need_update(sampass, PDB_WORKSTATIONS))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_WKS),
+ pdb_get_workstations(sampass));
+
+ if (need_update(sampass, PDB_MUNGEDDIAL))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL),
+ pdb_get_munged_dial(sampass));
+
+ if (need_update(sampass, PDB_SMBHOME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH),
+ pdb_get_homedir(sampass));
+
+ if (need_update(sampass, PDB_DRIVE))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_DRIVE),
+ pdb_get_dir_drive(sampass));
+
+ if (need_update(sampass, PDB_LOGONSCRIPT))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_SCRIPT),
+ pdb_get_logon_script(sampass));
+
+ if (need_update(sampass, PDB_PROFILE))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PROFILE_PATH),
+ pdb_get_profile_path(sampass));
+
+ if (asprintf(&temp, "%li", pdb_get_logon_time(sampass)) < 0) {
+ return false;
+ }
+ if (need_update(sampass, PDB_LOGONTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_TIME), temp);
+ SAFE_FREE(temp);
+
+ if (asprintf(&temp, "%li", pdb_get_logoff_time(sampass)) < 0) {
+ return false;
+ }
+ if (need_update(sampass, PDB_LOGOFFTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGOFF_TIME), temp);
+ SAFE_FREE(temp);
+
+ if (asprintf(&temp, "%li", pdb_get_kickoff_time(sampass)) < 0) {
+ return false;
+ }
+ if (need_update(sampass, PDB_KICKOFFTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp);
+ SAFE_FREE(temp);
+
+ if (asprintf(&temp, "%li", pdb_get_pass_can_change_time_noncalc(sampass)) < 0) {
+ return false;
+ }
+ if (need_update(sampass, PDB_CANCHANGETIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp);
+ SAFE_FREE(temp);
+
+ if (asprintf(&temp, "%li", pdb_get_pass_must_change_time(sampass)) < 0) {
+ return false;
+ }
+ if (need_update(sampass, PDB_MUSTCHANGETIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp);
+ SAFE_FREE(temp);
+
+ if ((pdb_get_acct_ctrl(sampass)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))
+ || (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) {
+
+ if (need_update(sampass, PDB_LMPASSWD)) {
+ const uchar *lm_pw = pdb_get_lanman_passwd(sampass);
+ if (lm_pw) {
+ char pwstr[34];
+ pdb_sethexpwd(pwstr, lm_pw,
+ pdb_get_acct_ctrl(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW),
+ pwstr);
+ } else {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW),
+ NULL);
+ }
+ }
+ if (need_update(sampass, PDB_NTPASSWD)) {
+ const uchar *nt_pw = pdb_get_nt_passwd(sampass);
+ if (nt_pw) {
+ char pwstr[34];
+ pdb_sethexpwd(pwstr, nt_pw,
+ pdb_get_acct_ctrl(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW),
+ pwstr);
+ } else {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW),
+ NULL);
+ }
+ }
+
+ if (need_update(sampass, PDB_PWHISTORY)) {
+ char *pwstr = NULL;
+ uint32 pwHistLen = 0;
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHistLen);
+
+ pwstr = SMB_MALLOC_ARRAY(char, 1024);
+ if (!pwstr) {
+ return false;
+ }
+ if (pwHistLen == 0) {
+ /* Remove any password history from the LDAP store. */
+ memset(pwstr, '0', 64); /* NOTE !!!! '0' *NOT '\0' */
+ pwstr[64] = '\0';
+ } else {
+ int i;
+ uint32 currHistLen = 0;
+ const uint8 *pwhist = pdb_get_pw_history(sampass, &currHistLen);
+ if (pwhist != NULL) {
+ /* We can only store (1024-1/64 password history entries. */
+ pwHistLen = MIN(pwHistLen, ((1024-1)/64));
+ for (i=0; i< pwHistLen && i < currHistLen; i++) {
+ /* Store the salt. */
+ pdb_sethexpwd(&pwstr[i*64], &pwhist[i*PW_HISTORY_ENTRY_LEN], 0);
+ /* Followed by the md5 hash of salt + md4 hash */
+ pdb_sethexpwd(&pwstr[(i*64)+32],
+ &pwhist[(i*PW_HISTORY_ENTRY_LEN)+PW_HISTORY_SALT_LEN], 0);
+ DEBUG(100, ("pwstr=%s\n", pwstr));
+ }
+ }
+ }
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_HISTORY),
+ pwstr);
+ SAFE_FREE(pwstr);
+ }
+
+ if (need_update(sampass, PDB_PASSLASTSET)) {
+ if (asprintf(&temp, "%li",
+ pdb_get_pass_last_set_time(sampass)) < 0) {
+ return false;
+ }
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET),
+ temp);
+ SAFE_FREE(temp);
+ }
+ }
+
+ if (need_update(sampass, PDB_HOURS)) {
+ const uint8 *hours = pdb_get_hours(sampass);
+ if (hours) {
+ char hourstr[44];
+ pdb_sethexhours(hourstr, hours);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct,
+ existing,
+ mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_LOGON_HOURS),
+ hourstr);
+ }
+ }
+
+ if (need_update(sampass, PDB_ACCTCTRL))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO),
+ pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ /* password lockout cache:
+ - If we are now autolocking or clearing, we write to ldap
+ - If we are clearing, we delete the cache entry
+ - If the count is > 0, we update the cache
+
+ This even means when autolocking, we cache, just in case the
+ update doesn't work, and we have to cache the autolock flag */
+
+ if (need_update(sampass, PDB_BAD_PASSWORD_COUNT)) /* &&
+ need_update(sampass, PDB_BAD_PASSWORD_TIME)) */ {
+ uint16 badcount = pdb_get_bad_password_count(sampass);
+ time_t badtime = pdb_get_bad_password_time(sampass);
+ uint32 pol;
+ pdb_get_account_policy(AP_BAD_ATTEMPT_LOCKOUT, &pol);
+
+ DEBUG(3, ("updating bad password fields, policy=%u, count=%u, time=%u\n",
+ (unsigned int)pol, (unsigned int)badcount, (unsigned int)badtime));
+
+ if ((badcount >= pol) || (badcount == 0)) {
+ DEBUG(7, ("making mods to update ldap, count=%u, time=%u\n",
+ (unsigned int)badcount, (unsigned int)badtime));
+ if (asprintf(&temp, "%li", (long)badcount) < 0) {
+ return false;
+ }
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_COUNT),
+ temp);
+ SAFE_FREE(temp);
+
+ if (asprintf(&temp, "%li", badtime) < 0) {
+ return false;
+ }
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_TIME),
+ temp);
+ SAFE_FREE(temp);
+ }
+ if (badcount == 0) {
+ DEBUG(7, ("bad password count is reset, deleting login cache entry for %s\n", pdb_get_nt_username(sampass)));
+ login_cache_delentry(sampass);
+ } else {
+ LOGIN_CACHE cache_entry;
+
+ cache_entry.entry_timestamp = time(NULL);
+ cache_entry.acct_ctrl = pdb_get_acct_ctrl(sampass);
+ cache_entry.bad_password_count = badcount;
+ cache_entry.bad_password_time = badtime;
+
+ DEBUG(7, ("Updating bad password count and time in login cache\n"));
+ login_cache_write(sampass, cache_entry);
+ }
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ End enumeration of the LDAP password list.
+*********************************************************************/
+
+static void ldapsam_endsampwent(struct pdb_methods *my_methods)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ if (ldap_state->result) {
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ }
+}
+
+static void append_attr(TALLOC_CTX *mem_ctx, const char ***attr_list,
+ const char *new_attr)
+{
+ int i;
+
+ if (new_attr == NULL) {
+ return;
+ }
+
+ for (i=0; (*attr_list)[i] != NULL; i++) {
+ ;
+ }
+
+ (*attr_list) = TALLOC_REALLOC_ARRAY(mem_ctx, (*attr_list),
+ const char *, i+2);
+ SMB_ASSERT((*attr_list) != NULL);
+ (*attr_list)[i] = talloc_strdup((*attr_list), new_attr);
+ (*attr_list)[i+1] = NULL;
+}
+
+/**********************************************************************
+Get struct samu entry from LDAP by username.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *my_methods, struct samu *user, const char *sname)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ const char ** attr_list;
+ int rc;
+
+ attr_list = get_userattr_list( user, ldap_state->schema_ver );
+ append_attr(user, &attr_list,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_MOD_TIMESTAMP));
+ append_attr(user, &attr_list, "uidNumber");
+ rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result,
+ attr_list);
+ TALLOC_FREE( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return NT_STATUS_NO_SUCH_USER;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getsampwnam: Unable to locate user [%s] count=%d\n", sname, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (count > 1) {
+ DEBUG(1, ("ldapsam_getsampwnam: Duplicate entries for this user [%s] Failing. count=%d\n", sname, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (entry) {
+ if (!init_sam_from_ldap(ldap_state, user, entry)) {
+ DEBUG(1,("ldapsam_getsampwnam: init_sam_from_ldap failed for user '%s'!\n", sname));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ pdb_set_backend_private_data(user, result, NULL,
+ my_methods, PDB_CHANGED);
+ talloc_autofree_ldapmsg(user, result);
+ ret = NT_STATUS_OK;
+ } else {
+ ldap_msgfree(result);
+ }
+ return ret;
+}
+
+static int ldapsam_get_ldap_user_by_sid(struct ldapsam_privates *ldap_state,
+ const DOM_SID *sid, LDAPMessage **result)
+{
+ int rc = -1;
+ const char ** attr_list;
+ uint32 rid;
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBASAMACCOUNT: {
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ attr_list = get_userattr_list(tmp_ctx,
+ ldap_state->schema_ver);
+ append_attr(tmp_ctx, &attr_list,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_MOD_TIMESTAMP));
+ append_attr(tmp_ctx, &attr_list, "uidNumber");
+ rc = ldapsam_search_suffix_by_sid(ldap_state, sid,
+ result, attr_list);
+ TALLOC_FREE(tmp_ctx);
+
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ break;
+ }
+
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, sid, &rid)) {
+ return rc;
+ }
+
+ attr_list = get_userattr_list(NULL,
+ ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_rid(ldap_state, rid, result, attr_list );
+ TALLOC_FREE( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ break;
+ }
+ return rc;
+}
+
+/**********************************************************************
+ Get struct samu entry from LDAP by SID.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, struct samu * user, const DOM_SID *sid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ int rc;
+
+ rc = ldapsam_get_ldap_user_by_sid(ldap_state,
+ sid, &result);
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_USER;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getsampwsid: Unable to locate SID [%s] "
+ "count=%d\n", sid_string_dbg(sid), count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (count > 1) {
+ DEBUG(1, ("ldapsam_getsampwsid: More than one user with SID "
+ "[%s]. Failing. count=%d\n", sid_string_dbg(sid),
+ count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (!init_sam_from_ldap(ldap_state, user, entry)) {
+ DEBUG(1,("ldapsam_getsampwsid: init_sam_from_ldap failed!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_set_backend_private_data(user, result, NULL,
+ my_methods, PDB_CHANGED);
+ talloc_autofree_ldapmsg(user, result);
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Do the actual modification - also change a plaintext passord if
+ it it set.
+**********************************************************************/
+
+static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods,
+ struct samu *newpwd, char *dn,
+ LDAPMod **mods, int ldap_op,
+ bool (*need_update)(const struct samu *, enum pdb_elements))
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+
+ if (!newpwd || !dn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!mods) {
+ DEBUG(5,("ldapsam_modify_entry: mods is empty: nothing to modify\n"));
+ /* may be password change below however */
+ } else {
+ switch(ldap_op) {
+ case LDAP_MOD_ADD:
+ if (ldap_state->is_nds_ldap) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ "objectclass",
+ "inetOrgPerson");
+ } else {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ "objectclass",
+ LDAP_OBJ_ACCOUNT);
+ }
+ rc = smbldap_add(ldap_state->smbldap_state,
+ dn, mods);
+ break;
+ case LDAP_MOD_REPLACE:
+ rc = smbldap_modify(ldap_state->smbldap_state,
+ dn ,mods);
+ break;
+ default:
+ DEBUG(0,("ldapsam_modify_entry: Wrong LDAP operation type: %d!\n",
+ ldap_op));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (rc!=LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (!(pdb_get_acct_ctrl(newpwd)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST)) &&
+ (lp_ldap_passwd_sync() != LDAP_PASSWD_SYNC_OFF) &&
+ need_update(newpwd, PDB_PLAINTEXT_PW) &&
+ (pdb_get_plaintext_passwd(newpwd)!=NULL)) {
+ BerElement *ber;
+ struct berval *bv;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ char *utf8_password;
+ char *utf8_dn;
+ size_t converted_size;
+
+ if (!ldap_state->is_nds_ldap) {
+
+ if (!smbldap_has_extension(ldap_state->smbldap_state->ldap_struct,
+ LDAP_EXOP_MODIFY_PASSWD)) {
+ DEBUG(2, ("ldap password change requested, but LDAP "
+ "server does not support it -- ignoring\n"));
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!push_utf8_allocate(&utf8_password,
+ pdb_get_plaintext_passwd(newpwd),
+ &converted_size))
+ {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!push_utf8_allocate(&utf8_dn, dn, &converted_size)) {
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ((ber = ber_alloc_t(LBER_USE_DER))==NULL) {
+ DEBUG(0,("ber_alloc_t returns NULL\n"));
+ SAFE_FREE(utf8_password);
+ SAFE_FREE(utf8_dn);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ((ber_printf (ber, "{") < 0) ||
+ (ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, utf8_dn) < 0) ||
+ (ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, utf8_password) < 0) ||
+ (ber_printf (ber, "n}") < 0)) {
+ DEBUG(0,("ldapsam_modify_entry: ber_printf returns a value <0\n"));
+ ber_free(ber,1);
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ((rc = ber_flatten (ber, &bv))<0) {
+ DEBUG(0,("ldapsam_modify_entry: ber_flatten returns a value <0\n"));
+ ber_free(ber,1);
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ ber_free(ber, 1);
+
+ if (!ldap_state->is_nds_ldap) {
+ rc = smbldap_extended_operation(ldap_state->smbldap_state,
+ LDAP_EXOP_MODIFY_PASSWD,
+ bv, NULL, NULL, &retoid,
+ &retdata);
+ } else {
+ rc = pdb_nds_set_password(ldap_state->smbldap_state, dn,
+ pdb_get_plaintext_passwd(newpwd));
+ }
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+
+ if (rc == LDAP_OBJECT_CLASS_VIOLATION) {
+ DEBUG(3, ("Could not set userPassword "
+ "attribute due to an objectClass "
+ "violation -- ignoring\n"));
+ ber_bvfree(bv);
+ return NT_STATUS_OK;
+ }
+
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldapsam_modify_entry: LDAP Password could not be changed for user %s: %s\n\t%s\n",
+ pdb_get_username(newpwd), ldap_err2string(rc), ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ ber_bvfree(bv);
+#if defined(LDAP_CONSTRAINT_VIOLATION)
+ if (rc == LDAP_CONSTRAINT_VIOLATION)
+ return NT_STATUS_PASSWORD_RESTRICTION;
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ } else {
+ DEBUG(3,("ldapsam_modify_entry: LDAP Password changed for user %s\n",pdb_get_username(newpwd)));
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("ldapsam_modify_entry: LDAP Password changed to %s\n",pdb_get_plaintext_passwd(newpwd)));
+#endif
+ if (retdata)
+ ber_bvfree(retdata);
+ if (retoid)
+ ldap_memfree(retoid);
+ }
+ ber_bvfree(bv);
+ }
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Delete entry from LDAP for username.
+*********************************************************************/
+
+static NTSTATUS ldapsam_delete_sam_account(struct pdb_methods *my_methods,
+ struct samu * sam_acct)
+{
+ struct ldapsam_privates *priv =
+ (struct ldapsam_privates *)my_methods->private_data;
+ const char *sname;
+ int rc;
+ LDAPMessage *msg, *entry;
+ NTSTATUS result = NT_STATUS_NO_MEMORY;
+ const char **attr_list;
+ TALLOC_CTX *mem_ctx;
+
+ if (!sam_acct) {
+ DEBUG(0, ("ldapsam_delete_sam_account: sam_acct was NULL!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sname = pdb_get_username(sam_acct);
+
+ DEBUG(3, ("ldapsam_delete_sam_account: Deleting user %s from "
+ "LDAP.\n", sname));
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ goto done;
+ }
+
+ attr_list = get_userattr_delete_list(mem_ctx, priv->schema_ver );
+ if (attr_list == NULL) {
+ goto done;
+ }
+
+ rc = ldapsam_search_suffix_by_name(priv, sname, &msg, attr_list);
+
+ if ((rc != LDAP_SUCCESS) ||
+ (ldap_count_entries(priv2ld(priv), msg) != 1) ||
+ ((entry = ldap_first_entry(priv2ld(priv), msg)) == NULL)) {
+ DEBUG(5, ("Could not find user %s\n", sname));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ rc = ldapsam_delete_entry(
+ priv, mem_ctx, entry,
+ priv->schema_ver == SCHEMAVER_SAMBASAMACCOUNT ?
+ LDAP_OBJ_SAMBASAMACCOUNT : LDAP_OBJ_SAMBAACCOUNT,
+ attr_list);
+
+ result = (rc == LDAP_SUCCESS) ?
+ NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+
+ done:
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+/**********************************************************************
+ Helper function to determine for update_sam_account whether
+ we need LDAP modification.
+*********************************************************************/
+
+static bool element_is_changed(const struct samu *sampass,
+ enum pdb_elements element)
+{
+ return IS_SAM_CHANGED(sampass, element);
+}
+
+/**********************************************************************
+ Update struct samu.
+*********************************************************************/
+
+static NTSTATUS ldapsam_update_sam_account(struct pdb_methods *my_methods, struct samu * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc = 0;
+ char *dn;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ const char **attr_list;
+
+ result = (LDAPMessage *)pdb_get_backend_private_data(newpwd, my_methods);
+ if (!result) {
+ attr_list = get_userattr_list(NULL, ldap_state->schema_ver);
+ if (pdb_get_username(newpwd) == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ rc = ldapsam_search_suffix_by_name(ldap_state, pdb_get_username(newpwd), &result, attr_list );
+ TALLOC_FREE( attr_list );
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ pdb_set_backend_private_data(newpwd, result, NULL,
+ my_methods, PDB_CHANGED);
+ talloc_autofree_ldapmsg(newpwd, result);
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
+ DEBUG(0, ("ldapsam_update_sam_account: No user to modify!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(4, ("ldapsam_update_sam_account: user %s to be modified has dn: %s\n", pdb_get_username(newpwd), dn));
+
+ if (!init_ldap_from_sam(ldap_state, entry, &mods, newpwd,
+ element_is_changed)) {
+ DEBUG(0, ("ldapsam_update_sam_account: init_ldap_from_sam failed!\n"));
+ SAFE_FREE(dn);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ((lp_ldap_passwd_sync() != LDAP_PASSWD_SYNC_ONLY)
+ && (mods == NULL)) {
+ DEBUG(4,("ldapsam_update_sam_account: mods is empty: nothing to update for user: %s\n",
+ pdb_get_username(newpwd)));
+ SAFE_FREE(dn);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,LDAP_MOD_REPLACE, element_is_changed);
+
+ if (mods != NULL) {
+ ldap_mods_free(mods,True);
+ }
+
+ SAFE_FREE(dn);
+
+ /*
+ * We need to set the backend private data to NULL here. For example
+ * setuserinfo level 25 does a pdb_update_sam_account twice on the
+ * same one, and with the explicit delete / add logic for attribute
+ * values the second time we would use the wrong "old" value which
+ * does not exist in LDAP anymore. Thus the LDAP server would refuse
+ * the update.
+ * The existing LDAPMessage is still being auto-freed by the
+ * destructor.
+ */
+ pdb_set_backend_private_data(newpwd, NULL, NULL, my_methods,
+ PDB_CHANGED);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+
+ DEBUG(2, ("ldapsam_update_sam_account: successfully modified uid = %s in the LDAP database\n",
+ pdb_get_username(newpwd)));
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Renames a struct samu
+ - The "rename user script" has full responsibility for changing everything
+***************************************************************************/
+
+static NTSTATUS ldapsam_rename_sam_account(struct pdb_methods *my_methods,
+ struct samu *old_acct,
+ const char *newname)
+{
+ const char *oldname;
+ int rc;
+ char *rename_script = NULL;
+ fstring oldname_lower, newname_lower;
+
+ if (!old_acct) {
+ DEBUG(0, ("ldapsam_rename_sam_account: old_acct was NULL!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (!newname) {
+ DEBUG(0, ("ldapsam_rename_sam_account: newname was NULL!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ oldname = pdb_get_username(old_acct);
+
+ /* rename the posix user */
+ rename_script = SMB_STRDUP(lp_renameuser_script());
+ if (rename_script == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(*rename_script)) {
+ SAFE_FREE(rename_script);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG (3, ("ldapsam_rename_sam_account: Renaming user %s to %s.\n",
+ oldname, newname));
+
+ /* We have to allow the account name to end with a '$'.
+ Also, follow the semantics in _samr_create_user() and lower case the
+ posix name but preserve the case in passdb */
+
+ fstrcpy( oldname_lower, oldname );
+ strlower_m( oldname_lower );
+ fstrcpy( newname_lower, newname );
+ strlower_m( newname_lower );
+ rename_script = realloc_string_sub2(rename_script,
+ "%unew",
+ newname_lower,
+ true,
+ true);
+ if (rename_script) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ rename_script = realloc_string_sub2(rename_script,
+ "%uold",
+ oldname_lower,
+ true,
+ true);
+ rc = smbrun(rename_script, NULL);
+
+ DEBUG(rc ? 0 : 3,("Running the command `%s' gave %d\n",
+ rename_script, rc));
+
+ SAFE_FREE(rename_script);
+
+ if (rc == 0) {
+ smb_nscd_flush_user_cache();
+ }
+
+ if (rc)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Helper function to determine for update_sam_account whether
+ we need LDAP modification.
+ *********************************************************************/
+
+static bool element_is_set_or_changed(const struct samu *sampass,
+ enum pdb_elements element)
+{
+ return (IS_SAM_SET(sampass, element) ||
+ IS_SAM_CHANGED(sampass, element));
+}
+
+/**********************************************************************
+ Add struct samu to LDAP.
+*********************************************************************/
+
+static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, struct samu * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ int ldap_op = LDAP_MOD_REPLACE;
+ uint32 num_result;
+ const char **attr_list;
+ char *escape_user = NULL;
+ const char *username = pdb_get_username(newpwd);
+ const DOM_SID *sid = pdb_get_user_sid(newpwd);
+ char *filter = NULL;
+ char *dn = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *ctx = talloc_init("ldapsam_add_sam_account");
+
+ if (!ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!username || !*username) {
+ DEBUG(0, ("ldapsam_add_sam_account: Cannot add user without a username!\n"));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fn_exit;
+ }
+
+ /* free this list after the second search or in case we exit on failure */
+ attr_list = get_userattr_list(ctx, ldap_state->schema_ver);
+
+ rc = ldapsam_search_suffix_by_name (ldap_state, username, &result, attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ goto fn_exit;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) != 0) {
+ DEBUG(0,("ldapsam_add_sam_account: User '%s' already in the base, with samba attributes\n",
+ username));
+ goto fn_exit;
+ }
+ ldap_msgfree(result);
+ result = NULL;
+
+ if (element_is_set_or_changed(newpwd, PDB_USERSID)) {
+ rc = ldapsam_get_ldap_user_by_sid(ldap_state,
+ sid, &result);
+ if (rc == LDAP_SUCCESS) {
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) != 0) {
+ DEBUG(0,("ldapsam_add_sam_account: SID '%s' "
+ "already in the base, with samba "
+ "attributes\n", sid_string_dbg(sid)));
+ goto fn_exit;
+ }
+ ldap_msgfree(result);
+ result = NULL;
+ }
+ }
+
+ /* does the entry already exist but without a samba attributes?
+ we need to return the samba attributes here */
+
+ escape_user = escape_ldap_string_alloc( username );
+ filter = talloc_strdup(attr_list, "(uid=%u)");
+ if (!filter) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+ filter = talloc_all_string_sub(attr_list, filter, "%u", escape_user);
+ if (!filter) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+ SAFE_FREE(escape_user);
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr_list, &result);
+ if ( rc != LDAP_SUCCESS ) {
+ goto fn_exit;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_add_sam_account: More than one user with that uid exists: bailing out!\n"));
+ goto fn_exit;
+ }
+
+ /* Check if we need to update an existing entry */
+ if (num_result == 1) {
+ char *tmp;
+
+ DEBUG(3,("ldapsam_add_sam_account: User exists without samba attributes: adding them\n"));
+ ldap_op = LDAP_MOD_REPLACE;
+ entry = ldap_first_entry (ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ goto fn_exit;
+ }
+ dn = talloc_asprintf(ctx, "%s", tmp);
+ SAFE_FREE(tmp);
+ if (!dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+
+ } else if (ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT) {
+
+ /* There might be a SID for this account already - say an idmap entry */
+
+ filter = talloc_asprintf(ctx,
+ "(&(%s=%s)(|(objectClass=%s)(objectClass=%s)))",
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_SID),
+ sid_string_talloc(ctx, sid),
+ LDAP_OBJ_IDMAP_ENTRY,
+ LDAP_OBJ_SID_ENTRY);
+ if (!filter) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+
+ /* free old result before doing a new search */
+ if (result != NULL) {
+ ldap_msgfree(result);
+ result = NULL;
+ }
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr_list, &result);
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto fn_exit;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_add_sam_account: More than one user with specified Sid exists: bailing out!\n"));
+ goto fn_exit;
+ }
+
+ /* Check if we need to update an existing entry */
+ if (num_result == 1) {
+ char *tmp;
+
+ DEBUG(3,("ldapsam_add_sam_account: User exists without samba attributes: adding them\n"));
+ ldap_op = LDAP_MOD_REPLACE;
+ entry = ldap_first_entry (ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn (ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ goto fn_exit;
+ }
+ dn = talloc_asprintf(ctx, "%s", tmp);
+ SAFE_FREE(tmp);
+ if (!dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+ }
+ }
+
+ if (num_result == 0) {
+ char *escape_username;
+ /* Check if we need to add an entry */
+ DEBUG(3,("ldapsam_add_sam_account: Adding new user\n"));
+ ldap_op = LDAP_MOD_ADD;
+
+ escape_username = escape_rdn_val_string_alloc(username);
+ if (!escape_username) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+
+ if (username[strlen(username)-1] == '$') {
+ dn = talloc_asprintf(ctx,
+ "uid=%s,%s",
+ escape_username,
+ lp_ldap_machine_suffix());
+ } else {
+ dn = talloc_asprintf(ctx,
+ "uid=%s,%s",
+ escape_username,
+ lp_ldap_user_suffix());
+ }
+
+ SAFE_FREE(escape_username);
+ if (!dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fn_exit;
+ }
+ }
+
+ if (!init_ldap_from_sam(ldap_state, entry, &mods, newpwd,
+ element_is_set_or_changed)) {
+ DEBUG(0, ("ldapsam_add_sam_account: init_ldap_from_sam failed!\n"));
+ if (mods != NULL) {
+ ldap_mods_free(mods, true);
+ }
+ goto fn_exit;
+ }
+
+ if (mods == NULL) {
+ DEBUG(0,("ldapsam_add_sam_account: mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd)));
+ goto fn_exit;
+ }
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_SAMBAACCOUNT);
+ break;
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_SAMBASAMACCOUNT);
+ break;
+ default:
+ DEBUG(0,("ldapsam_add_sam_account: invalid schema version specified\n"));
+ break;
+ }
+
+ ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,ldap_op, element_is_set_or_changed);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("ldapsam_add_sam_account: failed to modify/add user with uid = %s (dn = %s)\n",
+ pdb_get_username(newpwd),dn));
+ ldap_mods_free(mods, true);
+ goto fn_exit;
+ }
+
+ DEBUG(2,("ldapsam_add_sam_account: added: uid == %s in the LDAP database\n", pdb_get_username(newpwd)));
+ ldap_mods_free(mods, true);
+
+ status = NT_STATUS_OK;
+
+ fn_exit:
+
+ TALLOC_FREE(ctx);
+ SAFE_FREE(escape_user);
+ if (result) {
+ ldap_msgfree(result);
+ }
+ return status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int ldapsam_search_one_group (struct ldapsam_privates *ldap_state,
+ const char *filter,
+ LDAPMessage ** result)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ int rc;
+ const char **attr_list;
+
+ attr_list = get_attr_list(NULL, groupmap_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state,
+ lp_ldap_group_suffix (), scope,
+ filter, attr_list, 0, result);
+ TALLOC_FREE(attr_list);
+
+ return rc;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool init_group_from_ldap(struct ldapsam_privates *ldap_state,
+ GROUP_MAP *map, LDAPMessage *entry)
+{
+ char *temp = NULL;
+ TALLOC_CTX *ctx = talloc_init("init_group_from_ldap");
+
+ if (ldap_state == NULL || map == NULL || entry == NULL ||
+ ldap_state->smbldap_state->ldap_struct == NULL) {
+ DEBUG(0, ("init_group_from_ldap: NULL parameters found!\n"));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GIDNUMBER),
+ ctx);
+ if (!temp) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GIDNUMBER)));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ DEBUG(2, ("init_group_from_ldap: Entry found for group: %s\n", temp));
+
+ map->gid = (gid_t)atol(temp);
+
+ TALLOC_FREE(temp);
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GROUP_SID),
+ ctx);
+ if (!temp) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID)));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ if (!string_to_sid(&map->sid, temp)) {
+ DEBUG(1, ("SID string [%s] could not be read as a valid SID\n", temp));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ TALLOC_FREE(temp);
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GROUP_TYPE),
+ ctx);
+ if (!temp) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE)));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ map->sid_name_use = (enum lsa_SidType)atol(temp);
+
+ if ((map->sid_name_use < SID_NAME_USER) ||
+ (map->sid_name_use > SID_NAME_UNKNOWN)) {
+ DEBUG(0, ("init_group_from_ldap: Unknown Group type: %d\n", map->sid_name_use));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ TALLOC_FREE(temp);
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_DISPLAY_NAME),
+ ctx);
+ if (!temp) {
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_CN),
+ ctx);
+ if (!temp) {
+ DEBUG(0, ("init_group_from_ldap: Attributes cn not found either \
+for gidNumber(%lu)\n",(unsigned long)map->gid));
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ }
+ fstrcpy(map->nt_name, temp);
+
+ TALLOC_FREE(temp);
+ temp = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_DESC),
+ ctx);
+ if (!temp) {
+ temp = talloc_strdup(ctx, "");
+ if (!temp) {
+ TALLOC_FREE(ctx);
+ return false;
+ }
+ }
+ fstrcpy(map->comment, temp);
+
+ if (lp_parm_bool(-1, "ldapsam", "trusted", false)) {
+ store_gid_sid_cache(&map->sid, map->gid);
+ }
+
+ TALLOC_FREE(ctx);
+ return true;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgroup(struct pdb_methods *methods,
+ const char *filter,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+
+ if (ldapsam_search_one_group(ldap_state, filter, &result)
+ != LDAP_SUCCESS) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ count = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getgroup: Did not find group, filter was "
+ "%s\n", filter));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: "
+ "count=%d\n", filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!init_group_from_ldap(ldap_state, map, entry)) {
+ DEBUG(1, ("ldapsam_getgroup: init_group_from_ldap failed for "
+ "group filter %s\n", filter));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ char *filter = NULL;
+ NTSTATUS status;
+ fstring tmp;
+
+ if (asprintf(&filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_SID),
+ sid_to_fstring(tmp, &sid)) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ldapsam_getgroup(methods, filter, map);
+ SAFE_FREE(filter);
+ return status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ char *filter = NULL;
+ NTSTATUS status;
+
+ if (asprintf(&filter, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER),
+ (unsigned long)gid) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ldapsam_getgroup(methods, filter, map);
+ SAFE_FREE(filter);
+ return status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ char *filter = NULL;
+ char *escape_name = escape_ldap_string_alloc(name);
+ NTSTATUS status;
+
+ if (!escape_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (asprintf(&filter, "(&(objectClass=%s)(|(%s=%s)(%s=%s)))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), escape_name,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_CN),
+ escape_name) < 0) {
+ SAFE_FREE(escape_name);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SAFE_FREE(escape_name);
+ status = ldapsam_getgroup(methods, filter, map);
+ SAFE_FREE(filter);
+ return status;
+}
+
+static bool ldapsam_extract_rid_from_entry(LDAP *ldap_struct,
+ LDAPMessage *entry,
+ const DOM_SID *domain_sid,
+ uint32 *rid)
+{
+ fstring str;
+ DOM_SID sid;
+
+ if (!smbldap_get_single_attribute(ldap_struct, entry, "sambaSID",
+ str, sizeof(str)-1)) {
+ DEBUG(10, ("Could not find sambaSID attribute\n"));
+ return False;
+ }
+
+ if (!string_to_sid(&sid, str)) {
+ DEBUG(10, ("Could not convert string %s to sid\n", str));
+ return False;
+ }
+
+ if (sid_compare_domain(&sid, domain_sid) != 0) {
+ DEBUG(10, ("SID %s is not in expected domain %s\n",
+ str, sid_string_dbg(domain_sid)));
+ return False;
+ }
+
+ if (!sid_peek_rid(&sid, rid)) {
+ DEBUG(10, ("Could not peek into RID\n"));
+ return False;
+ }
+
+ return True;
+}
+
+static NTSTATUS ldapsam_enum_group_members(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group,
+ uint32 **pp_member_rids,
+ size_t *p_num_members)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ struct smbldap_state *conn = ldap_state->smbldap_state;
+ const char *id_attrs[] = { "memberUid", "gidNumber", NULL };
+ const char *sid_attrs[] = { "sambaSID", NULL };
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry;
+ char *filter;
+ char **values = NULL;
+ char **memberuid;
+ char *gidstr;
+ int rc, count;
+
+ *pp_member_rids = NULL;
+ *p_num_members = 0;
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectClass=%s)"
+ "(objectClass=%s)"
+ "(sambaSID=%s))",
+ LDAP_OBJ_POSIXGROUP,
+ LDAP_OBJ_GROUPMAP,
+ sid_string_talloc(mem_ctx, group));
+ if (filter == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_search(conn, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, id_attrs, 0,
+ &result);
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ count = ldap_count_entries(conn->ldap_struct, result);
+
+ if (count > 1) {
+ DEBUG(1, ("Found more than one groupmap entry for %s\n",
+ sid_string_dbg(group)));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ if (count == 0) {
+ ret = NT_STATUS_NO_SUCH_GROUP;
+ goto done;
+ }
+
+ entry = ldap_first_entry(conn->ldap_struct, result);
+ if (entry == NULL)
+ goto done;
+
+ gidstr = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "gidNumber", mem_ctx);
+ if (!gidstr) {
+ DEBUG (0, ("ldapsam_enum_group_members: Unable to find the group's gid!\n"));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ values = ldap_get_values(conn->ldap_struct, entry, "memberUid");
+
+ if (values) {
+
+ filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(|", LDAP_OBJ_SAMBASAMACCOUNT);
+ if (filter == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (memberuid = values; *memberuid != NULL; memberuid += 1) {
+ char *escape_memberuid;
+
+ escape_memberuid = escape_ldap_string_alloc(*memberuid);
+ if (escape_memberuid == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ filter = talloc_asprintf_append_buffer(filter, "(uid=%s)", escape_memberuid);
+ if (filter == NULL) {
+ SAFE_FREE(escape_memberuid);
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ SAFE_FREE(escape_memberuid);
+ }
+
+ filter = talloc_asprintf_append_buffer(filter, "))");
+ if (filter == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_search(conn, lp_ldap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, sid_attrs, 0,
+ &result);
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ count = ldap_count_entries(conn->ldap_struct, result);
+ DEBUG(10,("ldapsam_enum_group_members: found %d accounts\n", count));
+
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ for (entry = ldap_first_entry(conn->ldap_struct, result);
+ entry != NULL;
+ entry = ldap_next_entry(conn->ldap_struct, entry))
+ {
+ char *sidstr;
+ DOM_SID sid;
+ uint32 rid;
+
+ sidstr = smbldap_talloc_single_attribute(conn->ldap_struct,
+ entry, "sambaSID",
+ mem_ctx);
+ if (!sidstr) {
+ DEBUG(0, ("Severe DB error, %s can't miss the sambaSID"
+ "attribute\n", LDAP_OBJ_SAMBASAMACCOUNT));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ if (!string_to_sid(&sid, sidstr))
+ goto done;
+
+ if (!sid_check_is_in_our_domain(&sid)) {
+ DEBUG(0, ("Inconsistent SAM -- group member uid not "
+ "in our domain\n"));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ sid_peek_rid(&sid, &rid);
+
+ if (!add_rid_to_array_unique(mem_ctx, rid, pp_member_rids,
+ p_num_members)) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectClass=%s)"
+ "(gidNumber=%s))",
+ LDAP_OBJ_SAMBASAMACCOUNT,
+ gidstr);
+
+ rc = smbldap_search(conn, lp_ldap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, sid_attrs, 0,
+ &result);
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ for (entry = ldap_first_entry(conn->ldap_struct, result);
+ entry != NULL;
+ entry = ldap_next_entry(conn->ldap_struct, entry))
+ {
+ uint32 rid;
+
+ if (!ldapsam_extract_rid_from_entry(conn->ldap_struct,
+ entry,
+ get_global_sam_sid(),
+ &rid)) {
+ DEBUG(0, ("Severe DB error, %s can't miss the samba SID" "attribute\n", LDAP_OBJ_SAMBASAMACCOUNT));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ if (!add_rid_to_array_unique(mem_ctx, rid, pp_member_rids,
+ p_num_members)) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ ret = NT_STATUS_OK;
+
+ done:
+
+ if (values)
+ ldap_value_free(values);
+
+ return ret;
+}
+
+static NTSTATUS ldapsam_enum_group_memberships(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ struct samu *user,
+ DOM_SID **pp_sids,
+ gid_t **pp_gids,
+ size_t *p_num_groups)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ struct smbldap_state *conn = ldap_state->smbldap_state;
+ char *filter;
+ const char *attrs[] = { "gidNumber", "sambaSID", NULL };
+ char *escape_name;
+ int rc, count;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ size_t num_sids, num_gids;
+ char *gidstr;
+ gid_t primary_gid = -1;
+
+ *pp_sids = NULL;
+ num_sids = 0;
+
+ if (pdb_get_username(user) == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ escape_name = escape_ldap_string_alloc(pdb_get_username(user));
+ if (escape_name == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* retrieve the users primary gid */
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectClass=%s)(uid=%s))",
+ LDAP_OBJ_SAMBASAMACCOUNT,
+ escape_name);
+ if (filter == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_search(conn, lp_ldap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ count = ldap_count_entries(priv2ld(ldap_state), result);
+
+ switch (count) {
+ case 0:
+ DEBUG(1, ("User account [%s] not found!\n", pdb_get_username(user)));
+ ret = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ case 1:
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+
+ gidstr = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "gidNumber", mem_ctx);
+ if (!gidstr) {
+ DEBUG (1, ("Unable to find the member's gid!\n"));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+ primary_gid = strtoul(gidstr, NULL, 10);
+ break;
+ default:
+ DEBUG(1, ("found more than one account with the same user name ?!\n"));
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectClass=%s)(|(memberUid=%s)(gidNumber=%d)))",
+ LDAP_OBJ_POSIXGROUP, escape_name, primary_gid);
+ if (filter == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_search(conn, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ num_gids = 0;
+ *pp_gids = NULL;
+
+ num_sids = 0;
+ *pp_sids = NULL;
+
+ /* We need to add the primary group as the first gid/sid */
+
+ if (!add_gid_to_array_unique(mem_ctx, primary_gid, pp_gids, &num_gids)) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* This sid will be replaced later */
+
+ ret = add_sid_to_array_unique(mem_ctx, &global_sid_NULL, pp_sids,
+ &num_sids);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto done;
+ }
+
+ for (entry = ldap_first_entry(conn->ldap_struct, result);
+ entry != NULL;
+ entry = ldap_next_entry(conn->ldap_struct, entry))
+ {
+ fstring str;
+ DOM_SID sid;
+ gid_t gid;
+ char *end;
+
+ if (!smbldap_get_single_attribute(conn->ldap_struct,
+ entry, "sambaSID",
+ str, sizeof(str)-1))
+ continue;
+
+ if (!string_to_sid(&sid, str))
+ goto done;
+
+ if (!smbldap_get_single_attribute(conn->ldap_struct,
+ entry, "gidNumber",
+ str, sizeof(str)-1))
+ continue;
+
+ gid = strtoul(str, &end, 10);
+
+ if (PTR_DIFF(end, str) != strlen(str))
+ goto done;
+
+ if (gid == primary_gid) {
+ sid_copy(&(*pp_sids)[0], &sid);
+ } else {
+ if (!add_gid_to_array_unique(mem_ctx, gid, pp_gids,
+ &num_gids)) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ ret = add_sid_to_array_unique(mem_ctx, &sid, pp_sids,
+ &num_sids);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto done;
+ }
+ }
+ }
+
+ if (sid_compare(&global_sid_NULL, &(*pp_sids)[0]) == 0) {
+ DEBUG(3, ("primary group of [%s] not found\n",
+ pdb_get_username(user)));
+ goto done;
+ }
+
+ *p_num_groups = num_sids;
+
+ ret = NT_STATUS_OK;
+
+ done:
+
+ SAFE_FREE(escape_name);
+ return ret;
+}
+
+/**********************************************************************
+ * Augment a posixGroup object with a sambaGroupMapping domgroup
+ *********************************************************************/
+
+static NTSTATUS ldapsam_map_posixgroup(TALLOC_CTX *mem_ctx,
+ struct ldapsam_privates *ldap_state,
+ GROUP_MAP *map)
+{
+ const char *filter, *dn;
+ LDAPMessage *msg, *entry;
+ LDAPMod **mods;
+ int rc;
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectClass=%s)(gidNumber=%u))",
+ LDAP_OBJ_POSIXGROUP, map->gid);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter,
+ get_attr_list(mem_ctx, groupmap_attr_list),
+ &msg);
+ talloc_autofree_ldapmsg(mem_ctx, msg);
+
+ if ((rc != LDAP_SUCCESS) ||
+ (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, msg) != 1) ||
+ ((entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, msg)) == NULL)) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ dn = smbldap_talloc_dn(mem_ctx, ldap_state->smbldap_state->ldap_struct, entry);
+ if (dn == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mods = NULL;
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass",
+ LDAP_OBJ_GROUPMAP);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "sambaSid",
+ sid_string_talloc(mem_ctx, &map->sid));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "sambaGroupType",
+ talloc_asprintf(mem_ctx, "%d", map->sid_name_use));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "displayName",
+ map->nt_name);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "description",
+ map->comment);
+ talloc_autofree_ldapmod(mem_ctx, mods);
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *msg = NULL;
+ LDAPMod **mods = NULL;
+ const char *attrs[] = { NULL };
+ char *filter;
+
+ char *dn;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS result;
+
+ DOM_SID sid;
+
+ int rc;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(sambaSid=%s)",
+ sid_string_talloc(mem_ctx, &map->sid));
+ if (filter == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attrs, True, &msg);
+ talloc_autofree_ldapmsg(mem_ctx, msg);
+
+ if ((rc == LDAP_SUCCESS) &&
+ (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, msg) > 0)) {
+
+ DEBUG(3, ("SID %s already present in LDAP, refusing to add "
+ "group mapping entry\n", sid_string_dbg(&map->sid)));
+ result = NT_STATUS_GROUP_EXISTS;
+ goto done;
+ }
+
+ switch (map->sid_name_use) {
+
+ case SID_NAME_DOM_GRP:
+ /* To map a domain group we need to have a posix group
+ to attach to. */
+ result = ldapsam_map_posixgroup(mem_ctx, ldap_state, map);
+ goto done;
+ break;
+
+ case SID_NAME_ALIAS:
+ if (!sid_check_is_in_our_domain(&map->sid)
+ && !sid_check_is_in_builtin(&map->sid) )
+ {
+ DEBUG(3, ("Refusing to map sid %s as an alias, not in our domain\n",
+ sid_string_dbg(&map->sid)));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ break;
+
+ default:
+ DEBUG(3, ("Got invalid use '%s' for mapping\n",
+ sid_type_lookup(map->sid_name_use)));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Domain groups have been mapped in a separate routine, we have to
+ * create an alias now */
+
+ if (map->gid == -1) {
+ DEBUG(10, ("Refusing to map gid==-1\n"));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (pdb_gid_to_sid(map->gid, &sid)) {
+ DEBUG(3, ("Gid %d is already mapped to SID %s, refusing to "
+ "add\n", map->gid, sid_string_dbg(&sid)));
+ result = NT_STATUS_GROUP_EXISTS;
+ goto done;
+ }
+
+ /* Ok, enough checks done. It's still racy to go ahead now, but that's
+ * the best we can get out of LDAP. */
+
+ dn = talloc_asprintf(mem_ctx, "sambaSid=%s,%s",
+ sid_string_talloc(mem_ctx, &map->sid),
+ lp_ldap_group_suffix());
+ if (dn == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ mods = NULL;
+
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "objectClass",
+ LDAP_OBJ_SID_ENTRY);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "objectClass",
+ LDAP_OBJ_GROUPMAP);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "sambaSid",
+ sid_string_talloc(mem_ctx, &map->sid));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "sambaGroupType",
+ talloc_asprintf(mem_ctx, "%d", map->sid_name_use));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "displayName",
+ map->nt_name);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "description",
+ map->comment);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, NULL, &mods, "gidNumber",
+ talloc_asprintf(mem_ctx, "%u", map->gid));
+ talloc_autofree_ldapmod(mem_ctx, mods);
+
+ rc = smbldap_add(ldap_state->smbldap_state, dn, mods);
+
+ result = (rc == LDAP_SUCCESS) ?
+ NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+
+ done:
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+/**********************************************************************
+ * Update a group mapping entry. We're quite strict about what can be changed:
+ * Only the description and displayname may be changed. It simply does not
+ * make any sense to change the SID, gid or the type in a mapping.
+ *********************************************************************/
+
+static NTSTATUS ldapsam_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ int rc;
+ const char *filter, *dn;
+ LDAPMessage *msg = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS result;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Make 100% sure that sid, gid and type are not changed by looking up
+ * exactly the values we're given in LDAP. */
+
+ filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)"
+ "(sambaSid=%s)(gidNumber=%u)"
+ "(sambaGroupType=%d))",
+ LDAP_OBJ_GROUPMAP,
+ sid_string_talloc(mem_ctx, &map->sid),
+ map->gid, map->sid_name_use);
+ if (filter == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter,
+ get_attr_list(mem_ctx, groupmap_attr_list),
+ &msg);
+ talloc_autofree_ldapmsg(mem_ctx, msg);
+
+ if ((rc != LDAP_SUCCESS) ||
+ (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, msg) != 1) ||
+ ((entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, msg)) == NULL)) {
+ result = NT_STATUS_NO_SUCH_GROUP;
+ goto done;
+ }
+
+ dn = smbldap_talloc_dn(mem_ctx, ldap_state->smbldap_state->ldap_struct, entry);
+
+ if (dn == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ mods = NULL;
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "displayName",
+ map->nt_name);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "description",
+ map->comment);
+ talloc_autofree_ldapmod(mem_ctx, mods);
+
+ if (mods == NULL) {
+ DEBUG(4, ("ldapsam_update_group_mapping_entry: mods is empty: "
+ "nothing to do\n"));
+ result = NT_STATUS_OK;
+ goto done;
+ }
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ DEBUG(2, ("ldapsam_update_group_mapping_entry: successfully modified "
+ "group %lu in LDAP\n", (unsigned long)map->gid));
+
+ result = NT_STATUS_OK;
+
+ done:
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ struct ldapsam_privates *priv =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *msg, *entry;
+ int rc;
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+ char *filter;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_ATTRIBUTE_SID,
+ sid_string_talloc(mem_ctx, &sid));
+ if (filter == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ rc = smbldap_search_suffix(priv->smbldap_state, filter,
+ get_attr_list(mem_ctx, groupmap_attr_list),
+ &msg);
+ talloc_autofree_ldapmsg(mem_ctx, msg);
+
+ if ((rc != LDAP_SUCCESS) ||
+ (ldap_count_entries(priv2ld(priv), msg) != 1) ||
+ ((entry = ldap_first_entry(priv2ld(priv), msg)) == NULL)) {
+ result = NT_STATUS_NO_SUCH_GROUP;
+ goto done;
+ }
+
+ rc = ldapsam_delete_entry(priv, mem_ctx, entry, LDAP_OBJ_GROUPMAP,
+ get_attr_list(mem_ctx,
+ groupmap_attr_list_to_delete));
+
+ if ((rc == LDAP_NAMING_VIOLATION) ||
+ (rc == LDAP_OBJECT_CLASS_VIOLATION)) {
+ const char *attrs[] = { "sambaGroupType", "description",
+ "displayName", "sambaSIDList",
+ NULL };
+
+ /* Second try. Don't delete the sambaSID attribute, this is
+ for "old" entries that are tacked on a winbind
+ sambaIdmapEntry. */
+
+ rc = ldapsam_delete_entry(priv, mem_ctx, entry,
+ LDAP_OBJ_GROUPMAP, attrs);
+ }
+
+ if ((rc == LDAP_NAMING_VIOLATION) ||
+ (rc == LDAP_OBJECT_CLASS_VIOLATION)) {
+ const char *attrs[] = { "sambaGroupType", "description",
+ "displayName", "sambaSIDList",
+ "gidNumber", NULL };
+
+ /* Third try. This is a post-3.0.21 alias (containing only
+ * sambaSidEntry and sambaGroupMapping classes), we also have
+ * to delete the gidNumber attribute, only the sambaSidEntry
+ * remains */
+
+ rc = ldapsam_delete_entry(priv, mem_ctx, entry,
+ LDAP_OBJ_GROUPMAP, attrs);
+ }
+
+ result = (rc == LDAP_SUCCESS) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ done:
+ TALLOC_FREE(mem_ctx);
+ return result;
+ }
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_setsamgrent(struct pdb_methods *my_methods,
+ bool update)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)my_methods->private_data;
+ char *filter = NULL;
+ int rc;
+ const char **attr_list;
+
+ filter = talloc_asprintf(NULL, "(objectclass=%s)", LDAP_OBJ_GROUPMAP);
+ if (!filter) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ attr_list = get_attr_list( NULL, groupmap_attr_list );
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ TALLOC_FREE(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_setsamgrent: LDAP search failed: %s\n",
+ ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_setsamgrent: Query was: %s, %s\n",
+ lp_ldap_group_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ TALLOC_FREE(filter);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ TALLOC_FREE(filter);
+
+ DEBUG(2, ("ldapsam_setsamgrent: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result)));
+
+ ldap_state->entry =
+ ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result);
+ ldap_state->index = 0;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static void ldapsam_endsamgrent(struct pdb_methods *my_methods)
+{
+ ldapsam_endsampwent(my_methods);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getsamgrent(struct pdb_methods *my_methods,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)my_methods->private_data;
+ bool bret = False;
+
+ while (!bret) {
+ if (!ldap_state->entry)
+ return ret;
+
+ ldap_state->index++;
+ bret = init_group_from_ldap(ldap_state, map,
+ ldap_state->entry);
+
+ ldap_state->entry =
+ ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->entry);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_enum_group_mapping(struct pdb_methods *methods,
+ const DOM_SID *domsid, enum lsa_SidType sid_name_use,
+ GROUP_MAP **pp_rmap,
+ size_t *p_num_entries,
+ bool unix_only)
+{
+ GROUP_MAP map;
+ size_t entries = 0;
+
+ *p_num_entries = 0;
+ *pp_rmap = NULL;
+
+ if (!NT_STATUS_IS_OK(ldapsam_setsamgrent(methods, False))) {
+ DEBUG(0, ("ldapsam_enum_group_mapping: Unable to open "
+ "passdb\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ while (NT_STATUS_IS_OK(ldapsam_getsamgrent(methods, &map))) {
+ if (sid_name_use != SID_NAME_UNKNOWN &&
+ sid_name_use != map.sid_name_use) {
+ DEBUG(11,("ldapsam_enum_group_mapping: group %s is "
+ "not of the requested type\n", map.nt_name));
+ continue;
+ }
+ if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+ DEBUG(11,("ldapsam_enum_group_mapping: group %s is "
+ "non mapped\n", map.nt_name));
+ continue;
+ }
+
+ (*pp_rmap)=SMB_REALLOC_ARRAY((*pp_rmap), GROUP_MAP, entries+1);
+ if (!(*pp_rmap)) {
+ DEBUG(0,("ldapsam_enum_group_mapping: Unable to "
+ "enlarge group map!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*pp_rmap)[entries] = map;
+
+ entries += 1;
+
+ }
+ ldapsam_endsamgrent(methods);
+
+ *p_num_entries = entries;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_modify_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member,
+ int modop)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ char *dn = NULL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ LDAPMod **mods = NULL;
+ int rc;
+ enum lsa_SidType type = SID_NAME_USE_NONE;
+ fstring tmp;
+
+ char *filter = NULL;
+
+ if (sid_check_is_in_builtin(alias)) {
+ type = SID_NAME_ALIAS;
+ }
+
+ if (sid_check_is_in_our_domain(alias)) {
+ type = SID_NAME_ALIAS;
+ }
+
+ if (type == SID_NAME_USE_NONE) {
+ DEBUG(5, ("SID %s is neither in builtin nor in our domain!\n",
+ sid_string_dbg(alias)));
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (asprintf(&filter,
+ "(&(objectClass=%s)(sambaSid=%s)(sambaGroupType=%d))",
+ LDAP_OBJ_GROUPMAP, sid_to_fstring(tmp, alias),
+ type) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldapsam_search_one_group(ldap_state, filter,
+ &result) != LDAP_SUCCESS) {
+ SAFE_FREE(filter);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_modify_aliasmem: Did not find alias\n"));
+ ldap_msgfree(result);
+ SAFE_FREE(filter);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_modify_aliasmem: Duplicate entries for "
+ "filter %s: count=%d\n", filter, count));
+ ldap_msgfree(result);
+ SAFE_FREE(filter);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ SAFE_FREE(filter);
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ smbldap_set_mod(&mods, modop,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST),
+ sid_to_fstring(tmp, member));
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+ SAFE_FREE(dn);
+
+ if (rc == LDAP_TYPE_OR_VALUE_EXISTS) {
+ return NT_STATUS_MEMBER_IN_ALIAS;
+ }
+
+ if (rc == LDAP_NO_SUCH_ATTRIBUTE) {
+ return NT_STATUS_MEMBER_NOT_IN_ALIAS;
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_add_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ return ldapsam_modify_aliasmem(methods, alias, member, LDAP_MOD_ADD);
+}
+
+static NTSTATUS ldapsam_del_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ return ldapsam_modify_aliasmem(methods, alias, member,
+ LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_enum_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ DOM_SID **pp_members,
+ size_t *p_num_members)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ char **values = NULL;
+ int i;
+ char *filter = NULL;
+ size_t num_members = 0;
+ enum lsa_SidType type = SID_NAME_USE_NONE;
+ fstring tmp;
+
+ *pp_members = NULL;
+ *p_num_members = 0;
+
+ if (sid_check_is_in_builtin(alias)) {
+ type = SID_NAME_ALIAS;
+ }
+
+ if (sid_check_is_in_our_domain(alias)) {
+ type = SID_NAME_ALIAS;
+ }
+
+ if (type == SID_NAME_USE_NONE) {
+ DEBUG(5, ("SID %s is neither in builtin nor in our domain!\n",
+ sid_string_dbg(alias)));
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (asprintf(&filter,
+ "(&(objectClass=%s)(sambaSid=%s)(sambaGroupType=%d))",
+ LDAP_OBJ_GROUPMAP, sid_to_fstring(tmp, alias),
+ type) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldapsam_search_one_group(ldap_state, filter,
+ &result) != LDAP_SUCCESS) {
+ SAFE_FREE(filter);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_enum_aliasmem: Did not find alias\n"));
+ ldap_msgfree(result);
+ SAFE_FREE(filter);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_enum_aliasmem: Duplicate entries for "
+ "filter %s: count=%d\n", filter, count));
+ ldap_msgfree(result);
+ SAFE_FREE(filter);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ SAFE_FREE(filter);
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ values = ldap_get_values(ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST));
+
+ if (values == NULL) {
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+ count = ldap_count_values(values);
+
+ for (i=0; i<count; i++) {
+ DOM_SID member;
+ NTSTATUS status;
+
+ if (!string_to_sid(&member, values[i]))
+ continue;
+
+ status = add_sid_to_array(NULL, &member, pp_members,
+ &num_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldap_value_free(values);
+ ldap_msgfree(result);
+ return status;
+ }
+ }
+
+ *p_num_members = num_members;
+ ldap_value_free(values);
+ ldap_msgfree(result);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_alias_memberships(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *domain_sid,
+ const DOM_SID *members,
+ size_t num_members,
+ uint32 **pp_alias_rids,
+ size_t *p_num_alias_rids)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAP *ldap_struct;
+
+ const char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL };
+
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int i;
+ int rc;
+ char *filter;
+ enum lsa_SidType type = SID_NAME_USE_NONE;
+
+ if (sid_check_is_builtin(domain_sid)) {
+ type = SID_NAME_ALIAS;
+ }
+
+ if (sid_check_is_domain(domain_sid)) {
+ type = SID_NAME_ALIAS;
+ }
+
+ if (type == SID_NAME_USE_NONE) {
+ DEBUG(5, ("SID %s is neither builtin nor domain!\n",
+ sid_string_dbg(domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(|(objectclass=%s)(sambaGroupType=%d))(|",
+ LDAP_OBJ_GROUPMAP, type);
+
+ for (i=0; i<num_members; i++)
+ filter = talloc_asprintf(mem_ctx, "%s(sambaSIDList=%s)",
+ filter,
+ sid_string_talloc(mem_ctx,
+ &members[i]));
+
+ filter = talloc_asprintf(mem_ctx, "%s))", filter);
+
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
+
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ ldap_struct = ldap_state->smbldap_state->ldap_struct;
+
+ for (entry = ldap_first_entry(ldap_struct, result);
+ entry != NULL;
+ entry = ldap_next_entry(ldap_struct, entry))
+ {
+ fstring sid_str;
+ DOM_SID sid;
+ uint32 rid;
+
+ if (!smbldap_get_single_attribute(ldap_struct, entry,
+ LDAP_ATTRIBUTE_SID,
+ sid_str,
+ sizeof(sid_str)-1))
+ continue;
+
+ if (!string_to_sid(&sid, sid_str))
+ continue;
+
+ if (!sid_peek_check_rid(domain_sid, &sid, &rid))
+ continue;
+
+ if (!add_rid_to_array_unique(mem_ctx, rid, pp_alias_rids,
+ p_num_alias_rids)) {
+ ldap_msgfree(result);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_set_account_policy_in_ldap(struct pdb_methods *methods,
+ int policy_index,
+ uint32 value)
+{
+ NTSTATUS ntstatus = NT_STATUS_UNSUCCESSFUL;
+ int rc;
+ LDAPMod **mods = NULL;
+ fstring value_string;
+ const char *policy_attr = NULL;
+
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+
+ DEBUG(10,("ldapsam_set_account_policy_in_ldap\n"));
+
+ if (!ldap_state->domain_dn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ policy_attr = get_account_policy_attr(policy_index);
+ if (policy_attr == NULL) {
+ DEBUG(0,("ldapsam_set_account_policy_in_ldap: invalid "
+ "policy\n"));
+ return ntstatus;
+ }
+
+ slprintf(value_string, sizeof(value_string) - 1, "%i", value);
+
+ smbldap_set_mod(&mods, LDAP_MOD_REPLACE, policy_attr, value_string);
+
+ rc = smbldap_modify(ldap_state->smbldap_state, ldap_state->domain_dn,
+ mods);
+
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ return ntstatus;
+ }
+
+ if (!cache_account_policy_set(policy_index, value)) {
+ DEBUG(0,("ldapsam_set_account_policy_in_ldap: failed to "
+ "update local tdb cache\n"));
+ return ntstatus;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_set_account_policy(struct pdb_methods *methods,
+ int policy_index, uint32 value)
+{
+ return ldapsam_set_account_policy_in_ldap(methods, policy_index,
+ value);
+}
+
+static NTSTATUS ldapsam_get_account_policy_from_ldap(struct pdb_methods *methods,
+ int policy_index,
+ uint32 *value)
+{
+ NTSTATUS ntstatus = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ int rc;
+ char **vals = NULL;
+ const char *policy_attr = NULL;
+
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+
+ const char *attrs[2];
+
+ DEBUG(10,("ldapsam_get_account_policy_from_ldap\n"));
+
+ if (!ldap_state->domain_dn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ policy_attr = get_account_policy_attr(policy_index);
+ if (!policy_attr) {
+ DEBUG(0,("ldapsam_get_account_policy_from_ldap: invalid "
+ "policy index: %d\n", policy_index));
+ return ntstatus;
+ }
+
+ attrs[0] = policy_attr;
+ attrs[1] = NULL;
+
+ rc = smbldap_search(ldap_state->smbldap_state, ldap_state->domain_dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0,
+ &result);
+
+ if (rc != LDAP_SUCCESS) {
+ return ntstatus;
+ }
+
+ count = ldap_count_entries(priv2ld(ldap_state), result);
+ if (count < 1) {
+ goto out;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (entry == NULL) {
+ goto out;
+ }
+
+ vals = ldap_get_values(priv2ld(ldap_state), entry, policy_attr);
+ if (vals == NULL) {
+ goto out;
+ }
+
+ *value = (uint32)atol(vals[0]);
+
+ ntstatus = NT_STATUS_OK;
+
+out:
+ if (vals)
+ ldap_value_free(vals);
+ ldap_msgfree(result);
+
+ return ntstatus;
+}
+
+/* wrapper around ldapsam_get_account_policy_from_ldap(), handles tdb as cache
+
+ - if user hasn't decided to use account policies inside LDAP just reuse the
+ old tdb values
+
+ - if there is a valid cache entry, return that
+ - if there is an LDAP entry, update cache and return
+ - otherwise set to default, update cache and return
+
+ Guenther
+*/
+static NTSTATUS ldapsam_get_account_policy(struct pdb_methods *methods,
+ int policy_index, uint32 *value)
+{
+ NTSTATUS ntstatus = NT_STATUS_UNSUCCESSFUL;
+
+ if (cache_account_policy_get(policy_index, value)) {
+ DEBUG(11,("ldapsam_get_account_policy: got valid value from "
+ "cache\n"));
+ return NT_STATUS_OK;
+ }
+
+ ntstatus = ldapsam_get_account_policy_from_ldap(methods, policy_index,
+ value);
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ goto update_cache;
+ }
+
+ DEBUG(10,("ldapsam_get_account_policy: failed to retrieve from "
+ "ldap\n"));
+
+#if 0
+ /* should we automagically migrate old tdb value here ? */
+ if (account_policy_get(policy_index, value))
+ goto update_ldap;
+
+ DEBUG(10,("ldapsam_get_account_policy: no tdb for %d, trying "
+ "default\n", policy_index));
+#endif
+
+ if (!account_policy_get_default(policy_index, value)) {
+ return ntstatus;
+ }
+
+/* update_ldap: */
+
+ ntstatus = ldapsam_set_account_policy(methods, policy_index, *value);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ntstatus;
+ }
+
+ update_cache:
+
+ if (!cache_account_policy_set(policy_index, *value)) {
+ DEBUG(0,("ldapsam_get_account_policy: failed to update local "
+ "tdb as a cache\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_lookup_rids(struct pdb_methods *methods,
+ const DOM_SID *domain_sid,
+ int num_rids,
+ uint32 *rids,
+ const char **names,
+ enum lsa_SidType *attrs)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *msg = NULL;
+ LDAPMessage *entry;
+ char *allsids = NULL;
+ int i, rc, num_mapped;
+ NTSTATUS result = NT_STATUS_NO_MEMORY;
+ TALLOC_CTX *mem_ctx;
+ LDAP *ld;
+ bool is_builtin;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ goto done;
+ }
+
+ if (!sid_check_is_builtin(domain_sid) &&
+ !sid_check_is_domain(domain_sid)) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ for (i=0; i<num_rids; i++)
+ attrs[i] = SID_NAME_UNKNOWN;
+
+ allsids = talloc_strdup(mem_ctx, "");
+ if (allsids == NULL) {
+ goto done;
+ }
+
+ for (i=0; i<num_rids; i++) {
+ DOM_SID sid;
+ sid_compose(&sid, domain_sid, rids[i]);
+ allsids = talloc_asprintf_append_buffer(
+ allsids, "(sambaSid=%s)",
+ sid_string_talloc(mem_ctx, &sid));
+ if (allsids == NULL) {
+ goto done;
+ }
+ }
+
+ /* First look for users */
+
+ {
+ char *filter;
+ const char *ldap_attrs[] = { "uid", "sambaSid", NULL };
+
+ filter = talloc_asprintf(
+ mem_ctx, ("(&(objectClass=%s)(|%s))"),
+ LDAP_OBJ_SAMBASAMACCOUNT, allsids);
+
+ if (filter == NULL) {
+ goto done;
+ }
+
+ rc = smbldap_search(ldap_state->smbldap_state,
+ lp_ldap_user_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, ldap_attrs, 0,
+ &msg);
+ talloc_autofree_ldapmsg(mem_ctx, msg);
+ }
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ ld = ldap_state->smbldap_state->ldap_struct;
+ num_mapped = 0;
+
+ for (entry = ldap_first_entry(ld, msg);
+ entry != NULL;
+ entry = ldap_next_entry(ld, entry)) {
+ uint32 rid;
+ int rid_index;
+ const char *name;
+
+ if (!ldapsam_extract_rid_from_entry(ld, entry, domain_sid,
+ &rid)) {
+ DEBUG(2, ("Could not find sid from ldap entry\n"));
+ continue;
+ }
+
+ name = smbldap_talloc_single_attribute(ld, entry, "uid",
+ names);
+ if (name == NULL) {
+ DEBUG(2, ("Could not retrieve uid attribute\n"));
+ continue;
+ }
+
+ for (rid_index = 0; rid_index < num_rids; rid_index++) {
+ if (rid == rids[rid_index])
+ break;
+ }
+
+ if (rid_index == num_rids) {
+ DEBUG(2, ("Got a RID not asked for: %d\n", rid));
+ continue;
+ }
+
+ attrs[rid_index] = SID_NAME_USER;
+ names[rid_index] = name;
+ num_mapped += 1;
+ }
+
+ if (num_mapped == num_rids) {
+ /* No need to look for groups anymore -- we're done */
+ result = NT_STATUS_OK;
+ goto done;
+ }
+
+ /* Same game for groups */
+
+ {
+ char *filter;
+ const char *ldap_attrs[] = { "cn", "displayName", "sambaSid",
+ "sambaGroupType", NULL };
+
+ filter = talloc_asprintf(
+ mem_ctx, "(&(objectClass=%s)(|%s))",
+ LDAP_OBJ_GROUPMAP, allsids);
+ if (filter == NULL) {
+ goto done;
+ }
+
+ rc = smbldap_search(ldap_state->smbldap_state,
+ lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, ldap_attrs, 0,
+ &msg);
+ talloc_autofree_ldapmsg(mem_ctx, msg);
+ }
+
+ if (rc != LDAP_SUCCESS)
+ goto done;
+
+ /* ldap_struct might have changed due to a reconnect */
+
+ ld = ldap_state->smbldap_state->ldap_struct;
+
+ /* For consistency checks, we already checked we're only domain or builtin */
+
+ is_builtin = sid_check_is_builtin(domain_sid);
+
+ for (entry = ldap_first_entry(ld, msg);
+ entry != NULL;
+ entry = ldap_next_entry(ld, entry))
+ {
+ uint32 rid;
+ int rid_index;
+ const char *attr;
+ enum lsa_SidType type;
+ const char *dn = smbldap_talloc_dn(mem_ctx, ld, entry);
+
+ attr = smbldap_talloc_single_attribute(ld, entry, "sambaGroupType",
+ mem_ctx);
+ if (attr == NULL) {
+ DEBUG(2, ("Could not extract type from ldap entry %s\n",
+ dn));
+ continue;
+ }
+
+ type = (enum lsa_SidType)atol(attr);
+
+ /* Consistency checks */
+ if ((is_builtin && (type != SID_NAME_ALIAS)) ||
+ (!is_builtin && ((type != SID_NAME_ALIAS) &&
+ (type != SID_NAME_DOM_GRP)))) {
+ DEBUG(2, ("Rejecting invalid group mapping entry %s\n", dn));
+ }
+
+ if (!ldapsam_extract_rid_from_entry(ld, entry, domain_sid,
+ &rid)) {
+ DEBUG(2, ("Could not find sid from ldap entry %s\n", dn));
+ continue;
+ }
+
+ attr = smbldap_talloc_single_attribute(ld, entry, "displayName", names);
+
+ if (attr == NULL) {
+ DEBUG(10, ("Could not retrieve 'displayName' attribute from %s\n",
+ dn));
+ attr = smbldap_talloc_single_attribute(ld, entry, "cn", names);
+ }
+
+ if (attr == NULL) {
+ DEBUG(2, ("Could not retrieve naming attribute from %s\n",
+ dn));
+ continue;
+ }
+
+ for (rid_index = 0; rid_index < num_rids; rid_index++) {
+ if (rid == rids[rid_index])
+ break;
+ }
+
+ if (rid_index == num_rids) {
+ DEBUG(2, ("Got a RID not asked for: %d\n", rid));
+ continue;
+ }
+
+ attrs[rid_index] = type;
+ names[rid_index] = attr;
+ num_mapped += 1;
+ }
+
+ result = NT_STATUS_NONE_MAPPED;
+
+ if (num_mapped > 0)
+ result = (num_mapped == num_rids) ?
+ NT_STATUS_OK : STATUS_SOME_UNMAPPED;
+ done:
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+static char *get_ldap_filter(TALLOC_CTX *mem_ctx, const char *username)
+{
+ char *filter = NULL;
+ char *escaped = NULL;
+ char *result = NULL;
+
+ asprintf(&filter, "(&%s(objectclass=%s))",
+ "(uid=%u)", LDAP_OBJ_SAMBASAMACCOUNT);
+ if (filter == NULL) goto done;
+
+ escaped = escape_ldap_string_alloc(username);
+ if (escaped == NULL) goto done;
+
+ result = talloc_string_sub(mem_ctx, filter, "%u", username);
+
+ done:
+ SAFE_FREE(filter);
+ SAFE_FREE(escaped);
+
+ return result;
+}
+
+const char **talloc_attrs(TALLOC_CTX *mem_ctx, ...)
+{
+ int i, num = 0;
+ va_list ap;
+ const char **result;
+
+ va_start(ap, mem_ctx);
+ while (va_arg(ap, const char *) != NULL)
+ num += 1;
+ va_end(ap);
+
+ if ((result = TALLOC_ARRAY(mem_ctx, const char *, num+1)) == NULL) {
+ return NULL;
+ }
+
+ va_start(ap, mem_ctx);
+ for (i=0; i<num; i++) {
+ result[i] = talloc_strdup(result, va_arg(ap, const char*));
+ if (result[i] == NULL) {
+ talloc_free(result);
+ return NULL;
+ }
+ }
+ va_end(ap);
+
+ result[num] = NULL;
+ return result;
+}
+
+struct ldap_search_state {
+ struct smbldap_state *connection;
+
+ uint32 acct_flags;
+ uint16 group_type;
+
+ const char *base;
+ int scope;
+ const char *filter;
+ const char **attrs;
+ int attrsonly;
+ void *pagedresults_cookie;
+
+ LDAPMessage *entries, *current_entry;
+ bool (*ldap2displayentry)(struct ldap_search_state *state,
+ TALLOC_CTX *mem_ctx,
+ LDAP *ld, LDAPMessage *entry,
+ struct samr_displayentry *result);
+};
+
+static bool ldapsam_search_firstpage(struct pdb_search *search)
+{
+ struct ldap_search_state *state =
+ (struct ldap_search_state *)search->private_data;
+ LDAP *ld;
+ int rc = LDAP_OPERATIONS_ERROR;
+
+ state->entries = NULL;
+
+ if (state->connection->paged_results) {
+ rc = smbldap_search_paged(state->connection, state->base,
+ state->scope, state->filter,
+ state->attrs, state->attrsonly,
+ lp_ldap_page_size(), &state->entries,
+ &state->pagedresults_cookie);
+ }
+
+ if ((rc != LDAP_SUCCESS) || (state->entries == NULL)) {
+
+ if (state->entries != NULL) {
+ /* Left over from unsuccessful paged attempt */
+ ldap_msgfree(state->entries);
+ state->entries = NULL;
+ }
+
+ rc = smbldap_search(state->connection, state->base,
+ state->scope, state->filter, state->attrs,
+ state->attrsonly, &state->entries);
+
+ if ((rc != LDAP_SUCCESS) || (state->entries == NULL))
+ return False;
+
+ /* Ok, the server was lying. It told us it could do paged
+ * searches when it could not. */
+ state->connection->paged_results = False;
+ }
+
+ ld = state->connection->ldap_struct;
+ if ( ld == NULL) {
+ DEBUG(5, ("Don't have an LDAP connection right after a "
+ "search\n"));
+ return False;
+ }
+ state->current_entry = ldap_first_entry(ld, state->entries);
+
+ if (state->current_entry == NULL) {
+ ldap_msgfree(state->entries);
+ state->entries = NULL;
+ }
+
+ return True;
+}
+
+static bool ldapsam_search_nextpage(struct pdb_search *search)
+{
+ struct ldap_search_state *state =
+ (struct ldap_search_state *)search->private_data;
+ int rc;
+
+ if (!state->connection->paged_results) {
+ /* There is no next page when there are no paged results */
+ return False;
+ }
+
+ rc = smbldap_search_paged(state->connection, state->base,
+ state->scope, state->filter, state->attrs,
+ state->attrsonly, lp_ldap_page_size(),
+ &state->entries,
+ &state->pagedresults_cookie);
+
+ if ((rc != LDAP_SUCCESS) || (state->entries == NULL))
+ return False;
+
+ state->current_entry = ldap_first_entry(state->connection->ldap_struct, state->entries);
+
+ if (state->current_entry == NULL) {
+ ldap_msgfree(state->entries);
+ state->entries = NULL;
+ }
+
+ return True;
+}
+
+static bool ldapsam_search_next_entry(struct pdb_search *search,
+ struct samr_displayentry *entry)
+{
+ struct ldap_search_state *state =
+ (struct ldap_search_state *)search->private_data;
+ bool result;
+
+ retry:
+ if ((state->entries == NULL) && (state->pagedresults_cookie == NULL))
+ return False;
+
+ if ((state->entries == NULL) &&
+ !ldapsam_search_nextpage(search))
+ return False;
+
+ result = state->ldap2displayentry(state, search->mem_ctx, state->connection->ldap_struct,
+ state->current_entry, entry);
+
+ if (!result) {
+ char *dn;
+ dn = ldap_get_dn(state->connection->ldap_struct, state->current_entry);
+ DEBUG(5, ("Skipping entry %s\n", dn != NULL ? dn : "<NULL>"));
+ if (dn != NULL) ldap_memfree(dn);
+ }
+
+ state->current_entry = ldap_next_entry(state->connection->ldap_struct, state->current_entry);
+
+ if (state->current_entry == NULL) {
+ ldap_msgfree(state->entries);
+ state->entries = NULL;
+ }
+
+ if (!result) goto retry;
+
+ return True;
+}
+
+static void ldapsam_search_end(struct pdb_search *search)
+{
+ struct ldap_search_state *state =
+ (struct ldap_search_state *)search->private_data;
+ int rc;
+
+ if (state->pagedresults_cookie == NULL)
+ return;
+
+ if (state->entries != NULL)
+ ldap_msgfree(state->entries);
+
+ state->entries = NULL;
+ state->current_entry = NULL;
+
+ if (!state->connection->paged_results)
+ return;
+
+ /* Tell the LDAP server we're not interested in the rest anymore. */
+
+ rc = smbldap_search_paged(state->connection, state->base, state->scope,
+ state->filter, state->attrs,
+ state->attrsonly, 0, &state->entries,
+ &state->pagedresults_cookie);
+
+ if (rc != LDAP_SUCCESS)
+ DEBUG(5, ("Could not end search properly\n"));
+
+ return;
+}
+
+static bool ldapuser2displayentry(struct ldap_search_state *state,
+ TALLOC_CTX *mem_ctx,
+ LDAP *ld, LDAPMessage *entry,
+ struct samr_displayentry *result)
+{
+ char **vals;
+ size_t converted_size;
+ DOM_SID sid;
+ uint32 acct_flags;
+
+ vals = ldap_get_values(ld, entry, "sambaAcctFlags");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(5, ("\"sambaAcctFlags\" not found\n"));
+ return False;
+ }
+ acct_flags = pdb_decode_acct_ctrl(vals[0]);
+ ldap_value_free(vals);
+
+ if ((state->acct_flags != 0) &&
+ ((state->acct_flags & acct_flags) == 0))
+ return False;
+
+ result->acct_flags = acct_flags;
+ result->account_name = "";
+ result->fullname = "";
+ result->description = "";
+
+ vals = ldap_get_values(ld, entry, "uid");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(5, ("\"uid\" not found\n"));
+ return False;
+ }
+ if (!pull_utf8_talloc(mem_ctx,
+ CONST_DISCARD(char **, &result->account_name),
+ vals[0], &converted_size))
+ {
+ DEBUG(0,("ldapuser2displayentry: pull_utf8_talloc failed: %s",
+ strerror(errno)));
+ }
+
+ ldap_value_free(vals);
+
+ vals = ldap_get_values(ld, entry, "displayName");
+ if ((vals == NULL) || (vals[0] == NULL))
+ DEBUG(8, ("\"displayName\" not found\n"));
+ else if (!pull_utf8_talloc(mem_ctx,
+ CONST_DISCARD(char **, &result->fullname),
+ vals[0], &converted_size))
+ {
+ DEBUG(0,("ldapuser2displayentry: pull_utf8_talloc failed: %s",
+ strerror(errno)));
+ }
+
+ ldap_value_free(vals);
+
+ vals = ldap_get_values(ld, entry, "description");
+ if ((vals == NULL) || (vals[0] == NULL))
+ DEBUG(8, ("\"description\" not found\n"));
+ else if (!pull_utf8_talloc(mem_ctx,
+ CONST_DISCARD(char **, &result->description),
+ vals[0], &converted_size))
+ {
+ DEBUG(0,("ldapuser2displayentry: pull_utf8_talloc failed: %s",
+ strerror(errno)));
+ }
+
+ ldap_value_free(vals);
+
+ if ((result->account_name == NULL) ||
+ (result->fullname == NULL) ||
+ (result->description == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ vals = ldap_get_values(ld, entry, "sambaSid");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(0, ("\"objectSid\" not found\n"));
+ return False;
+ }
+
+ if (!string_to_sid(&sid, vals[0])) {
+ DEBUG(0, ("Could not convert %s to SID\n", vals[0]));
+ ldap_value_free(vals);
+ return False;
+ }
+ ldap_value_free(vals);
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &sid, &result->rid)) {
+ DEBUG(0, ("sid %s does not belong to our domain\n",
+ sid_string_dbg(&sid)));
+ return False;
+ }
+
+ return True;
+}
+
+
+static bool ldapsam_search_users(struct pdb_methods *methods,
+ struct pdb_search *search,
+ uint32 acct_flags)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ struct ldap_search_state *state;
+
+ state = TALLOC_P(search->mem_ctx, struct ldap_search_state);
+ if (state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ state->connection = ldap_state->smbldap_state;
+
+ if ((acct_flags != 0) && ((acct_flags & ACB_NORMAL) != 0))
+ state->base = lp_ldap_user_suffix();
+ else if ((acct_flags != 0) &&
+ ((acct_flags & (ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST)) != 0))
+ state->base = lp_ldap_machine_suffix();
+ else
+ state->base = lp_ldap_suffix();
+
+ state->acct_flags = acct_flags;
+ state->base = talloc_strdup(search->mem_ctx, state->base);
+ state->scope = LDAP_SCOPE_SUBTREE;
+ state->filter = get_ldap_filter(search->mem_ctx, "*");
+ state->attrs = talloc_attrs(search->mem_ctx, "uid", "sambaSid",
+ "displayName", "description",
+ "sambaAcctFlags", NULL);
+ state->attrsonly = 0;
+ state->pagedresults_cookie = NULL;
+ state->entries = NULL;
+ state->ldap2displayentry = ldapuser2displayentry;
+
+ if ((state->filter == NULL) || (state->attrs == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ search->private_data = state;
+ search->next_entry = ldapsam_search_next_entry;
+ search->search_end = ldapsam_search_end;
+
+ return ldapsam_search_firstpage(search);
+}
+
+static bool ldapgroup2displayentry(struct ldap_search_state *state,
+ TALLOC_CTX *mem_ctx,
+ LDAP *ld, LDAPMessage *entry,
+ struct samr_displayentry *result)
+{
+ char **vals;
+ size_t converted_size;
+ DOM_SID sid;
+ uint16 group_type;
+
+ result->account_name = "";
+ result->fullname = "";
+ result->description = "";
+
+
+ vals = ldap_get_values(ld, entry, "sambaGroupType");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(5, ("\"sambaGroupType\" not found\n"));
+ if (vals != NULL) {
+ ldap_value_free(vals);
+ }
+ return False;
+ }
+
+ group_type = atoi(vals[0]);
+
+ if ((state->group_type != 0) &&
+ ((state->group_type != group_type))) {
+ ldap_value_free(vals);
+ return False;
+ }
+
+ ldap_value_free(vals);
+
+ /* display name is the NT group name */
+
+ vals = ldap_get_values(ld, entry, "displayName");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(8, ("\"displayName\" not found\n"));
+
+ /* fallback to the 'cn' attribute */
+ vals = ldap_get_values(ld, entry, "cn");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(5, ("\"cn\" not found\n"));
+ return False;
+ }
+ if (!pull_utf8_talloc(mem_ctx,
+ CONST_DISCARD(char **,
+ &result->account_name),
+ vals[0], &converted_size))
+ {
+ DEBUG(0,("ldapgroup2displayentry: pull_utf8_talloc "
+ "failed: %s", strerror(errno)));
+ }
+ }
+ else if (!pull_utf8_talloc(mem_ctx,
+ CONST_DISCARD(char **,
+ &result->account_name),
+ vals[0], &converted_size))
+ {
+ DEBUG(0,("ldapgroup2displayentry: pull_utf8_talloc failed: %s",
+ strerror(errno)));
+ }
+
+ ldap_value_free(vals);
+
+ vals = ldap_get_values(ld, entry, "description");
+ if ((vals == NULL) || (vals[0] == NULL))
+ DEBUG(8, ("\"description\" not found\n"));
+ else if (!pull_utf8_talloc(mem_ctx,
+ CONST_DISCARD(char **, &result->description),
+ vals[0], &converted_size))
+ {
+ DEBUG(0,("ldapgroup2displayentry: pull_utf8_talloc failed: %s",
+ strerror(errno)));
+ }
+ ldap_value_free(vals);
+
+ if ((result->account_name == NULL) ||
+ (result->fullname == NULL) ||
+ (result->description == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ vals = ldap_get_values(ld, entry, "sambaSid");
+ if ((vals == NULL) || (vals[0] == NULL)) {
+ DEBUG(0, ("\"objectSid\" not found\n"));
+ if (vals != NULL) {
+ ldap_value_free(vals);
+ }
+ return False;
+ }
+
+ if (!string_to_sid(&sid, vals[0])) {
+ DEBUG(0, ("Could not convert %s to SID\n", vals[0]));
+ return False;
+ }
+
+ ldap_value_free(vals);
+
+ switch (group_type) {
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &sid, &result->rid)
+ && !sid_peek_check_rid(&global_sid_Builtin, &sid, &result->rid))
+ {
+ DEBUG(0, ("%s is not in our domain\n",
+ sid_string_dbg(&sid)));
+ return False;
+ }
+ break;
+
+ default:
+ DEBUG(0,("unkown group type: %d\n", group_type));
+ return False;
+ }
+
+ result->acct_flags = 0;
+
+ return True;
+}
+
+static bool ldapsam_search_grouptype(struct pdb_methods *methods,
+ struct pdb_search *search,
+ const DOM_SID *sid,
+ enum lsa_SidType type)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ struct ldap_search_state *state;
+ fstring tmp;
+
+ state = TALLOC_P(search->mem_ctx, struct ldap_search_state);
+ if (state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ state->connection = ldap_state->smbldap_state;
+
+ state->base = talloc_strdup(search->mem_ctx, lp_ldap_group_suffix());
+ state->connection = ldap_state->smbldap_state;
+ state->scope = LDAP_SCOPE_SUBTREE;
+ state->filter = talloc_asprintf(search->mem_ctx,
+ "(&(objectclass=%s)"
+ "(sambaGroupType=%d)(sambaSID=%s*))",
+ LDAP_OBJ_GROUPMAP,
+ type, sid_to_fstring(tmp, sid));
+ state->attrs = talloc_attrs(search->mem_ctx, "cn", "sambaSid",
+ "displayName", "description",
+ "sambaGroupType", NULL);
+ state->attrsonly = 0;
+ state->pagedresults_cookie = NULL;
+ state->entries = NULL;
+ state->group_type = type;
+ state->ldap2displayentry = ldapgroup2displayentry;
+
+ if ((state->filter == NULL) || (state->attrs == NULL)) {
+ DEBUG(0, ("talloc failed\n"));
+ return False;
+ }
+
+ search->private_data = state;
+ search->next_entry = ldapsam_search_next_entry;
+ search->search_end = ldapsam_search_end;
+
+ return ldapsam_search_firstpage(search);
+}
+
+static bool ldapsam_search_groups(struct pdb_methods *methods,
+ struct pdb_search *search)
+{
+ return ldapsam_search_grouptype(methods, search, get_global_sam_sid(), SID_NAME_DOM_GRP);
+}
+
+static bool ldapsam_search_aliases(struct pdb_methods *methods,
+ struct pdb_search *search,
+ const DOM_SID *sid)
+{
+ return ldapsam_search_grouptype(methods, search, sid, SID_NAME_ALIAS);
+}
+
+static bool ldapsam_rid_algorithm(struct pdb_methods *methods)
+{
+ return False;
+}
+
+static NTSTATUS ldapsam_get_new_rid(struct ldapsam_privates *priv,
+ uint32 *rid)
+{
+ struct smbldap_state *smbldap_state = priv->smbldap_state;
+
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ NTSTATUS status;
+ char *value;
+ int rc;
+ uint32 nextRid = 0;
+ const char *dn;
+
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = smbldap_search_domain_info(smbldap_state, &result,
+ get_global_sam_name(), False);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not get domain info: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ entry = ldap_first_entry(priv2ld(priv), result);
+ if (entry == NULL) {
+ DEBUG(0, ("Could not get domain info entry\n"));
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ /* Find the largest of the three attributes "sambaNextRid",
+ "sambaNextGroupRid" and "sambaNextUserRid". I gave up on the
+ concept of differentiating between user and group rids, and will
+ use only "sambaNextRid" in the future. But for compatibility
+ reasons I look if others have chosen different strategies -- VL */
+
+ value = smbldap_talloc_single_attribute(priv2ld(priv), entry,
+ "sambaNextRid", mem_ctx);
+ if (value != NULL) {
+ uint32 tmp = (uint32)strtoul(value, NULL, 10);
+ nextRid = MAX(nextRid, tmp);
+ }
+
+ value = smbldap_talloc_single_attribute(priv2ld(priv), entry,
+ "sambaNextUserRid", mem_ctx);
+ if (value != NULL) {
+ uint32 tmp = (uint32)strtoul(value, NULL, 10);
+ nextRid = MAX(nextRid, tmp);
+ }
+
+ value = smbldap_talloc_single_attribute(priv2ld(priv), entry,
+ "sambaNextGroupRid", mem_ctx);
+ if (value != NULL) {
+ uint32 tmp = (uint32)strtoul(value, NULL, 10);
+ nextRid = MAX(nextRid, tmp);
+ }
+
+ if (nextRid == 0) {
+ nextRid = BASE_RID-1;
+ }
+
+ nextRid += 1;
+
+ smbldap_make_mod(priv2ld(priv), entry, &mods, "sambaNextRid",
+ talloc_asprintf(mem_ctx, "%d", nextRid));
+ talloc_autofree_ldapmod(mem_ctx, mods);
+
+ if ((dn = smbldap_talloc_dn(mem_ctx, priv2ld(priv), entry)) == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = smbldap_modify(smbldap_state, dn, mods);
+
+ /* ACCESS_DENIED is used as a placeholder for "the modify failed,
+ * please retry" */
+
+ status = (rc == LDAP_SUCCESS) ? NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+
+ done:
+ if (NT_STATUS_IS_OK(status)) {
+ *rid = nextRid;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return status;
+}
+
+static NTSTATUS ldapsam_new_rid_internal(struct pdb_methods *methods, uint32 *rid)
+{
+ int i;
+
+ for (i=0; i<10; i++) {
+ NTSTATUS result = ldapsam_get_new_rid(
+ (struct ldapsam_privates *)methods->private_data, rid);
+ if (NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (!NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
+ return result;
+ }
+
+ /* The ldap update failed (maybe a race condition), retry */
+ }
+
+ /* Tried 10 times, fail. */
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+static bool ldapsam_new_rid(struct pdb_methods *methods, uint32 *rid)
+{
+ NTSTATUS result = ldapsam_new_rid_internal(methods, rid);
+ return NT_STATUS_IS_OK(result) ? True : False;
+}
+
+static bool ldapsam_sid_to_id(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ union unid_t *id, enum lsa_SidType *type)
+{
+ struct ldapsam_privates *priv =
+ (struct ldapsam_privates *)methods->private_data;
+ char *filter;
+ const char *attrs[] = { "sambaGroupType", "gidNumber", "uidNumber",
+ NULL };
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ bool ret = False;
+ char *value;
+ int rc;
+
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return False;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(sambaSid=%s)"
+ "(|(objectClass=%s)(objectClass=%s)))",
+ sid_string_talloc(mem_ctx, sid),
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_SAMBASAMACCOUNT);
+ if (filter == NULL) {
+ DEBUG(5, ("talloc_asprintf failed\n"));
+ goto done;
+ }
+
+ rc = smbldap_search_suffix(priv->smbldap_state, filter,
+ attrs, &result);
+ if (rc != LDAP_SUCCESS) {
+ goto done;
+ }
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ if (ldap_count_entries(priv2ld(priv), result) != 1) {
+ DEBUG(10, ("Got %d entries, expected one\n",
+ ldap_count_entries(priv2ld(priv), result)));
+ goto done;
+ }
+
+ entry = ldap_first_entry(priv2ld(priv), result);
+
+ value = smbldap_talloc_single_attribute(priv2ld(priv), entry,
+ "sambaGroupType", mem_ctx);
+
+ if (value != NULL) {
+ const char *gid_str;
+ /* It's a group */
+
+ gid_str = smbldap_talloc_single_attribute(
+ priv2ld(priv), entry, "gidNumber", mem_ctx);
+ if (gid_str == NULL) {
+ DEBUG(1, ("%s has sambaGroupType but no gidNumber\n",
+ smbldap_talloc_dn(mem_ctx, priv2ld(priv),
+ entry)));
+ goto done;
+ }
+
+ id->gid = strtoul(gid_str, NULL, 10);
+ *type = (enum lsa_SidType)strtoul(value, NULL, 10);
+ ret = True;
+ goto done;
+ }
+
+ /* It must be a user */
+
+ value = smbldap_talloc_single_attribute(priv2ld(priv), entry,
+ "uidNumber", mem_ctx);
+ if (value == NULL) {
+ DEBUG(1, ("Could not find uidNumber in %s\n",
+ smbldap_talloc_dn(mem_ctx, priv2ld(priv), entry)));
+ goto done;
+ }
+
+ id->uid = strtoul(value, NULL, 10);
+ *type = SID_NAME_USER;
+
+ ret = True;
+ done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+/*
+ * The following functions is called only if
+ * ldapsam:trusted and ldapsam:editposix are
+ * set to true
+ */
+
+/*
+ * ldapsam_create_user creates a new
+ * posixAccount and sambaSamAccount object
+ * in the ldap users subtree
+ *
+ * The uid is allocated by winbindd.
+ */
+
+static NTSTATUS ldapsam_create_user(struct pdb_methods *my_methods,
+ TALLOC_CTX *tmp_ctx, const char *name,
+ uint32 acb_info, uint32 *rid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *entry = NULL;
+ LDAPMessage *result = NULL;
+ uint32 num_result;
+ bool is_machine = False;
+ bool add_posix = False;
+ LDAPMod **mods = NULL;
+ struct samu *user;
+ char *filter;
+ char *username;
+ char *homedir;
+ char *gidstr;
+ char *uidstr;
+ char *shell;
+ const char *dn = NULL;
+ DOM_SID group_sid;
+ DOM_SID user_sid;
+ gid_t gid = -1;
+ uid_t uid = -1;
+ NTSTATUS ret;
+ int rc;
+
+ if (((acb_info & ACB_NORMAL) && name[strlen(name)-1] == '$') ||
+ acb_info & ACB_WSTRUST ||
+ acb_info & ACB_SVRTRUST ||
+ acb_info & ACB_DOMTRUST) {
+ is_machine = True;
+ }
+
+ username = escape_ldap_string_alloc(name);
+ filter = talloc_asprintf(tmp_ctx, "(&(uid=%s)(objectClass=%s))",
+ username, LDAP_OBJ_POSIXACCOUNT);
+ SAFE_FREE(username);
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_create_user: ldap search failed!\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_create_user: More than one user with name [%s] ?!\n", name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (num_result == 1) {
+ char *tmp;
+ /* check if it is just a posix account.
+ * or if there is a sid attached to this entry
+ */
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tmp = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "sambaSID", tmp_ctx);
+ if (tmp) {
+ DEBUG (1, ("ldapsam_create_user: The user [%s] already exist!\n", name));
+ return NT_STATUS_USER_EXISTS;
+ }
+
+ /* it is just a posix account, retrieve the dn for later use */
+ dn = smbldap_talloc_dn(tmp_ctx, priv2ld(ldap_state), entry);
+ if (!dn) {
+ DEBUG(0,("ldapsam_create_user: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (num_result == 0) {
+ add_posix = True;
+ }
+
+ /* Create the basic samu structure and generate the mods for the ldap commit */
+ if (!NT_STATUS_IS_OK((ret = ldapsam_new_rid_internal(my_methods, rid)))) {
+ DEBUG(1, ("ldapsam_create_user: Could not allocate a new RID\n"));
+ return ret;
+ }
+
+ sid_compose(&user_sid, get_global_sam_sid(), *rid);
+
+ user = samu_new(tmp_ctx);
+ if (!user) {
+ DEBUG(1,("ldapsam_create_user: Unable to allocate user struct\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_username(user, name, PDB_SET)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (!pdb_set_domain(user, get_global_sam_name(), PDB_SET)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (is_machine) {
+ if (acb_info & ACB_NORMAL) {
+ if (!pdb_set_acct_ctrl(user, ACB_WSTRUST, PDB_SET)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(user, acb_info, PDB_SET)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(user, ACB_NORMAL | ACB_DISABLED, PDB_SET)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (!pdb_set_user_sid(user, &user_sid, PDB_SET)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!init_ldap_from_sam(ldap_state, NULL, &mods, user, element_is_set_or_changed)) {
+ DEBUG(1,("ldapsam_create_user: Unable to fill user structs\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ldap_state->schema_ver != SCHEMAVER_SAMBASAMACCOUNT) {
+ DEBUG(1,("ldapsam_create_user: Unsupported schema version\n"));
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SAMBASAMACCOUNT);
+
+ if (add_posix) {
+ char *escape_name;
+
+ DEBUG(3,("ldapsam_create_user: Creating new posix user\n"));
+
+ /* retrieve the Domain Users group gid */
+ if (!sid_compose(&group_sid, get_global_sam_sid(), DOMAIN_GROUP_RID_USERS) ||
+ !sid_to_gid(&group_sid, &gid)) {
+ DEBUG (0, ("ldapsam_create_user: Unable to get the Domain Users gid: bailing out!\n"));
+ return NT_STATUS_INVALID_PRIMARY_GROUP;
+ }
+
+ /* lets allocate a new userid for this user */
+ if (!winbind_allocate_uid(&uid)) {
+ DEBUG (0, ("ldapsam_create_user: Unable to allocate a new user id: bailing out!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ if (is_machine) {
+ /* TODO: choose a more appropriate default for machines */
+ homedir = talloc_sub_specified(tmp_ctx, lp_template_homedir(), "SMB_workstations_home", ldap_state->domain_name, uid, gid);
+ shell = talloc_strdup(tmp_ctx, "/bin/false");
+ } else {
+ homedir = talloc_sub_specified(tmp_ctx, lp_template_homedir(), name, ldap_state->domain_name, uid, gid);
+ shell = talloc_sub_specified(tmp_ctx, lp_template_shell(), name, ldap_state->domain_name, uid, gid);
+ }
+ uidstr = talloc_asprintf(tmp_ctx, "%d", uid);
+ gidstr = talloc_asprintf(tmp_ctx, "%d", gid);
+
+ escape_name = escape_rdn_val_string_alloc(name);
+ if (!escape_name) {
+ DEBUG (0, ("ldapsam_create_user: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (is_machine) {
+ dn = talloc_asprintf(tmp_ctx, "uid=%s,%s", escape_name, lp_ldap_machine_suffix ());
+ } else {
+ dn = talloc_asprintf(tmp_ctx, "uid=%s,%s", escape_name, lp_ldap_user_suffix ());
+ }
+
+ SAFE_FREE(escape_name);
+
+ if (!homedir || !shell || !uidstr || !gidstr || !dn) {
+ DEBUG (0, ("ldapsam_create_user: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_ACCOUNT);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXACCOUNT);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "uidNumber", uidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "homeDirectory", homedir);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "loginShell", shell);
+ }
+
+ talloc_autofree_ldapmod(tmp_ctx, mods);
+
+ if (add_posix) {
+ rc = smbldap_add(ldap_state->smbldap_state, dn, mods);
+ } else {
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_create_user: failed to create a new user [%s] (dn = %s)\n", name ,dn));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2,("ldapsam_create_user: added account [%s] in the LDAP database\n", name));
+
+ flush_pwnam_cache();
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_delete_user(struct pdb_methods *my_methods, TALLOC_CTX *tmp_ctx, struct samu *sam_acct)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int num_result;
+ const char *dn;
+ char *filter;
+ int rc;
+
+ DEBUG(0,("ldapsam_delete_user: Attempt to delete user [%s]\n", pdb_get_username(sam_acct)));
+
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(uid=%s)"
+ "(objectClass=%s)"
+ "(objectClass=%s))",
+ pdb_get_username(sam_acct),
+ LDAP_OBJ_POSIXACCOUNT,
+ LDAP_OBJ_SAMBASAMACCOUNT);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_delete_user: user search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result == 0) {
+ DEBUG(0,("ldapsam_delete_user: user not found!\n"));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_delete_user: More than one user with name [%s] ?!\n", pdb_get_username(sam_acct)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* it is just a posix account, retrieve the dn for later use */
+ dn = smbldap_talloc_dn(tmp_ctx, priv2ld(ldap_state), entry);
+ if (!dn) {
+ DEBUG(0,("ldapsam_delete_user: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = smbldap_delete(ldap_state->smbldap_state, dn);
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ flush_pwnam_cache();
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * ldapsam_create_group creates a new
+ * posixGroup and sambaGroupMapping object
+ * in the ldap groups subtree
+ *
+ * The gid is allocated by winbindd.
+ */
+
+static NTSTATUS ldapsam_create_dom_group(struct pdb_methods *my_methods,
+ TALLOC_CTX *tmp_ctx,
+ const char *name,
+ uint32 *rid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret;
+ LDAPMessage *entry = NULL;
+ LDAPMessage *result = NULL;
+ uint32 num_result;
+ bool is_new_entry = False;
+ LDAPMod **mods = NULL;
+ char *filter;
+ char *groupsidstr;
+ char *groupname;
+ char *grouptype;
+ char *gidstr;
+ const char *dn = NULL;
+ DOM_SID group_sid;
+ gid_t gid = -1;
+ int rc;
+
+ groupname = escape_ldap_string_alloc(name);
+ filter = talloc_asprintf(tmp_ctx, "(&(cn=%s)(objectClass=%s))",
+ groupname, LDAP_OBJ_POSIXGROUP);
+ SAFE_FREE(groupname);
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_create_group: ldap search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_create_group: There exists more than one group with name [%s]: bailing out!\n", name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (num_result == 1) {
+ char *tmp;
+ /* check if it is just a posix group.
+ * or if there is a sid attached to this entry
+ */
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tmp = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "sambaSID", tmp_ctx);
+ if (tmp) {
+ DEBUG (1, ("ldapsam_create_group: The group [%s] already exist!\n", name));
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ /* it is just a posix group, retrieve the gid and the dn for later use */
+ tmp = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "gidNumber", tmp_ctx);
+ if (!tmp) {
+ DEBUG (1, ("ldapsam_create_group: Couldn't retrieve the gidNumber for [%s]?!?!\n", name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ gid = strtoul(tmp, NULL, 10);
+
+ dn = smbldap_talloc_dn(tmp_ctx, priv2ld(ldap_state), entry);
+ if (!dn) {
+ DEBUG(0,("ldapsam_create_group: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (num_result == 0) {
+ char *escape_name;
+
+ DEBUG(3,("ldapsam_create_user: Creating new posix group\n"));
+
+ is_new_entry = True;
+
+ /* lets allocate a new groupid for this group */
+ if (!winbind_allocate_gid(&gid)) {
+ DEBUG (0, ("ldapsam_create_group: Unable to allocate a new group id: bailing out!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ gidstr = talloc_asprintf(tmp_ctx, "%d", gid);
+
+ escape_name = escape_rdn_val_string_alloc(name);
+ if (!escape_name) {
+ DEBUG (0, ("ldapsam_create_group: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dn = talloc_asprintf(tmp_ctx, "cn=%s,%s", escape_name, lp_ldap_group_suffix());
+
+ SAFE_FREE(escape_name);
+
+ if (!gidstr || !dn) {
+ DEBUG (0, ("ldapsam_create_group: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_POSIXGROUP);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ }
+
+ if (!NT_STATUS_IS_OK((ret = ldapsam_new_rid_internal(my_methods, rid)))) {
+ DEBUG(1, ("ldapsam_create_group: Could not allocate a new RID\n"));
+ return ret;
+ }
+
+ sid_compose(&group_sid, get_global_sam_sid(), *rid);
+
+ groupsidstr = talloc_strdup(tmp_ctx, sid_string_talloc(tmp_ctx,
+ &group_sid));
+ grouptype = talloc_asprintf(tmp_ctx, "%d", SID_NAME_DOM_GRP);
+
+ if (!groupsidstr || !grouptype) {
+ DEBUG(0,("ldapsam_create_group: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid", groupsidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", grouptype);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", name);
+ talloc_autofree_ldapmod(tmp_ctx, mods);
+
+ if (is_new_entry) {
+ rc = smbldap_add(ldap_state->smbldap_state, dn, mods);
+#if 0
+ if (rc == LDAP_OBJECT_CLASS_VIOLATION) {
+ /* This call may fail with rfc2307bis schema */
+ /* Retry adding a structural class */
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "????");
+ rc = smbldap_add(ldap_state->smbldap_state, dn, mods);
+ }
+#endif
+ } else {
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_create_group: failed to create a new group [%s] (dn = %s)\n", name ,dn));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2,("ldapsam_create_group: added group [%s] in the LDAP database\n", name));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_delete_dom_group(struct pdb_methods *my_methods, TALLOC_CTX *tmp_ctx, uint32 rid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int num_result;
+ const char *dn;
+ char *gidstr;
+ char *filter;
+ DOM_SID group_sid;
+ int rc;
+
+ /* get the group sid */
+ sid_compose(&group_sid, get_global_sam_sid(), rid);
+
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(sambaSID=%s)"
+ "(objectClass=%s)"
+ "(objectClass=%s))",
+ sid_string_talloc(tmp_ctx, &group_sid),
+ LDAP_OBJ_POSIXGROUP,
+ LDAP_OBJ_GROUPMAP);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1,("ldapsam_delete_dom_group: group search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result == 0) {
+ DEBUG(1,("ldapsam_delete_dom_group: group not found!\n"));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_delete_dom_group: More than one group with the same SID ?!\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* here it is, retrieve the dn for later use */
+ dn = smbldap_talloc_dn(tmp_ctx, priv2ld(ldap_state), entry);
+ if (!dn) {
+ DEBUG(0,("ldapsam_delete_dom_group: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gidstr = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "gidNumber", tmp_ctx);
+ if (!gidstr) {
+ DEBUG (0, ("ldapsam_delete_dom_group: Unable to find the group's gid!\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* check no user have this group marked as primary group */
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(gidNumber=%s)"
+ "(objectClass=%s)"
+ "(objectClass=%s))",
+ gidstr,
+ LDAP_OBJ_POSIXACCOUNT,
+ LDAP_OBJ_SAMBASAMACCOUNT);
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1,("ldapsam_delete_dom_group: accounts search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result != 0) {
+ DEBUG(3,("ldapsam_delete_dom_group: Can't delete group, it is a primary group for %d users\n", num_result));
+ return NT_STATUS_MEMBERS_PRIMARY_GROUP;
+ }
+
+ rc = smbldap_delete(ldap_state->smbldap_state, dn);
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_change_groupmem(struct pdb_methods *my_methods,
+ TALLOC_CTX *tmp_ctx,
+ uint32 group_rid,
+ uint32 member_rid,
+ int modop)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *entry = NULL;
+ LDAPMessage *result = NULL;
+ uint32 num_result;
+ LDAPMod **mods = NULL;
+ char *filter;
+ char *uidstr;
+ const char *dn = NULL;
+ DOM_SID group_sid;
+ DOM_SID member_sid;
+ int rc;
+
+ switch (modop) {
+ case LDAP_MOD_ADD:
+ DEBUG(1,("ldapsam_change_groupmem: add new member(rid=%d) to a domain group(rid=%d)", member_rid, group_rid));
+ break;
+ case LDAP_MOD_DELETE:
+ DEBUG(1,("ldapsam_change_groupmem: delete member(rid=%d) from a domain group(rid=%d)", member_rid, group_rid));
+ break;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* get member sid */
+ sid_compose(&member_sid, get_global_sam_sid(), member_rid);
+
+ /* get the group sid */
+ sid_compose(&group_sid, get_global_sam_sid(), group_rid);
+
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(sambaSID=%s)"
+ "(objectClass=%s)"
+ "(objectClass=%s))",
+ sid_string_talloc(tmp_ctx, &member_sid),
+ LDAP_OBJ_POSIXACCOUNT,
+ LDAP_OBJ_SAMBASAMACCOUNT);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* get the member uid */
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1,("ldapsam_change_groupmem: member search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result == 0) {
+ DEBUG(1,("ldapsam_change_groupmem: member not found!\n"));
+ return NT_STATUS_NO_SUCH_MEMBER;
+ }
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_change_groupmem: More than one account with the same SID ?!\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (modop == LDAP_MOD_DELETE) {
+ /* check if we are trying to remove the member from his primary group */
+ char *gidstr;
+ gid_t user_gid, group_gid;
+
+ gidstr = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "gidNumber", tmp_ctx);
+ if (!gidstr) {
+ DEBUG (0, ("ldapsam_change_groupmem: Unable to find the member's gid!\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ user_gid = strtoul(gidstr, NULL, 10);
+
+ if (!sid_to_gid(&group_sid, &group_gid)) {
+ DEBUG (0, ("ldapsam_change_groupmem: Unable to get group gid from SID!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (user_gid == group_gid) {
+ DEBUG (3, ("ldapsam_change_groupmem: can't remove user from its own primary group!\n"));
+ return NT_STATUS_MEMBERS_PRIMARY_GROUP;
+ }
+ }
+
+ /* here it is, retrieve the uid for later use */
+ uidstr = smbldap_talloc_single_attribute(priv2ld(ldap_state), entry, "uid", tmp_ctx);
+ if (!uidstr) {
+ DEBUG (0, ("ldapsam_change_groupmem: Unable to find the member's name!\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ filter = talloc_asprintf(tmp_ctx,
+ "(&(sambaSID=%s)"
+ "(objectClass=%s)"
+ "(objectClass=%s))",
+ sid_string_talloc(tmp_ctx, &group_sid),
+ LDAP_OBJ_POSIXGROUP,
+ LDAP_OBJ_GROUPMAP);
+
+ /* get the group */
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1,("ldapsam_change_groupmem: group search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(tmp_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result == 0) {
+ DEBUG(1,("ldapsam_change_groupmem: group not found!\n"));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_change_groupmem: More than one group with the same SID ?!\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* here it is, retrieve the dn for later use */
+ dn = smbldap_talloc_dn(tmp_ctx, priv2ld(ldap_state), entry);
+ if (!dn) {
+ DEBUG(0,("ldapsam_change_groupmem: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbldap_set_mod(&mods, modop, "memberUid", uidstr);
+
+ talloc_autofree_ldapmod(tmp_ctx, mods);
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ if (rc != LDAP_SUCCESS) {
+ if (rc == LDAP_TYPE_OR_VALUE_EXISTS && modop == LDAP_MOD_ADD) {
+ DEBUG(1,("ldapsam_change_groupmem: member is already in group, add failed!\n"));
+ return NT_STATUS_MEMBER_IN_GROUP;
+ }
+ if (rc == LDAP_NO_SUCH_ATTRIBUTE && modop == LDAP_MOD_DELETE) {
+ DEBUG(1,("ldapsam_change_groupmem: member is not in group, delete failed!\n"));
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_add_groupmem(struct pdb_methods *my_methods,
+ TALLOC_CTX *tmp_ctx,
+ uint32 group_rid,
+ uint32 member_rid)
+{
+ return ldapsam_change_groupmem(my_methods, tmp_ctx, group_rid, member_rid, LDAP_MOD_ADD);
+}
+static NTSTATUS ldapsam_del_groupmem(struct pdb_methods *my_methods,
+ TALLOC_CTX *tmp_ctx,
+ uint32 group_rid,
+ uint32 member_rid)
+{
+ return ldapsam_change_groupmem(my_methods, tmp_ctx, group_rid, member_rid, LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_set_primary_group(struct pdb_methods *my_methods,
+ TALLOC_CTX *mem_ctx,
+ struct samu *sampass)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *entry = NULL;
+ LDAPMessage *result = NULL;
+ uint32 num_result;
+ LDAPMod **mods = NULL;
+ char *filter;
+ char *escape_username;
+ char *gidstr;
+ const char *dn = NULL;
+ gid_t gid;
+ int rc;
+
+ DEBUG(0,("ldapsam_set_primary_group: Attempt to set primary group for user [%s]\n", pdb_get_username(sampass)));
+
+ if (!sid_to_gid(pdb_get_group_sid(sampass), &gid)) {
+ DEBUG(0,("ldapsam_set_primary_group: failed to retrieve gid from user's group SID!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ gidstr = talloc_asprintf(mem_ctx, "%d", gid);
+ if (!gidstr) {
+ DEBUG(0,("ldapsam_set_primary_group: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ escape_username = escape_ldap_string_alloc(pdb_get_username(sampass));
+ if (escape_username== NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(uid=%s)"
+ "(objectClass=%s)"
+ "(objectClass=%s))",
+ escape_username,
+ LDAP_OBJ_POSIXACCOUNT,
+ LDAP_OBJ_SAMBASAMACCOUNT);
+
+ SAFE_FREE(escape_username);
+
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, NULL, &result);
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_set_primary_group: user search failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ talloc_autofree_ldapmsg(mem_ctx, result);
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result == 0) {
+ DEBUG(0,("ldapsam_set_primary_group: user not found!\n"));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_set_primary_group: More than one user with name [%s] ?!\n", pdb_get_username(sampass)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entry = ldap_first_entry(priv2ld(ldap_state), result);
+ if (!entry) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* retrieve the dn for later use */
+ dn = smbldap_talloc_dn(mem_ctx, priv2ld(ldap_state), entry);
+ if (!dn) {
+ DEBUG(0,("ldapsam_set_primary_group: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* remove the old one, and add the new one, this way we do not risk races */
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods, "gidNumber", gidstr);
+
+ if (mods == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldapsam_set_primary_group: failed to modify [%s] primary group to [%s]\n",
+ pdb_get_username(sampass), gidstr));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ flush_pwnam_cache();
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************************
+ trusted domains functions
+ *********************************************************************/
+
+static char *trusteddom_dn(struct ldapsam_privates *ldap_state,
+ const char *domain)
+{
+ return talloc_asprintf(talloc_tos(), "sambaDomainName=%s,%s", domain,
+ ldap_state->domain_dn);
+}
+
+static bool get_trusteddom_pw_int(struct ldapsam_privates *ldap_state,
+ TALLOC_CTX *mem_ctx,
+ const char *domain, LDAPMessage **entry)
+{
+ int rc;
+ char *filter;
+ int scope = LDAP_SCOPE_SUBTREE;
+ const char **attrs = NULL; /* NULL: get all attrs */
+ int attrsonly = 0; /* 0: return values too */
+ LDAPMessage *result = NULL;
+ char *trusted_dn;
+ uint32 num_result;
+
+ filter = talloc_asprintf(talloc_tos(),
+ "(&(objectClass=%s)(sambaDomainName=%s))",
+ LDAP_OBJ_TRUSTDOM_PASSWORD, domain);
+
+ trusted_dn = trusteddom_dn(ldap_state, domain);
+ if (trusted_dn == NULL) {
+ return False;
+ }
+ rc = smbldap_search(ldap_state->smbldap_state, trusted_dn, scope,
+ filter, attrs, attrsonly, &result);
+
+ if (result != NULL) {
+ talloc_autofree_ldapmsg(mem_ctx, result);
+ }
+
+ if (rc == LDAP_NO_SUCH_OBJECT) {
+ *entry = NULL;
+ return True;
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ return False;
+ }
+
+ num_result = ldap_count_entries(priv2ld(ldap_state), result);
+
+ if (num_result > 1) {
+ DEBUG(1, ("ldapsam_get_trusteddom_pw: more than one "
+ "%s object for domain '%s'?!\n",
+ LDAP_OBJ_TRUSTDOM_PASSWORD, domain));
+ return False;
+ }
+
+ if (num_result == 0) {
+ DEBUG(1, ("ldapsam_get_trusteddom_pw: no "
+ "%s object for domain %s.\n",
+ LDAP_OBJ_TRUSTDOM_PASSWORD, domain));
+ *entry = NULL;
+ } else {
+ *entry = ldap_first_entry(priv2ld(ldap_state), result);
+ }
+
+ return True;
+}
+
+static bool ldapsam_get_trusteddom_pw(struct pdb_methods *methods,
+ const char *domain,
+ char** pwd,
+ DOM_SID *sid,
+ time_t *pass_last_set_time)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *entry = NULL;
+
+ DEBUG(10, ("ldapsam_get_trusteddom_pw called for domain %s\n", domain));
+
+ if (!get_trusteddom_pw_int(ldap_state, talloc_tos(), domain, &entry) ||
+ (entry == NULL))
+ {
+ return False;
+ }
+
+ /* password */
+ if (pwd != NULL) {
+ char *pwd_str;
+ pwd_str = smbldap_talloc_single_attribute(priv2ld(ldap_state),
+ entry, "sambaClearTextPassword", talloc_tos());
+ if (pwd_str == NULL) {
+ return False;
+ }
+ /* trusteddom_pw routines do not use talloc yet... */
+ *pwd = SMB_STRDUP(pwd_str);
+ if (*pwd == NULL) {
+ return False;
+ }
+ }
+
+ /* last change time */
+ if (pass_last_set_time != NULL) {
+ char *time_str;
+ time_str = smbldap_talloc_single_attribute(priv2ld(ldap_state),
+ entry, "sambaPwdLastSet", talloc_tos());
+ if (time_str == NULL) {
+ return False;
+ }
+ *pass_last_set_time = (time_t)atol(time_str);
+ }
+
+ /* domain sid */
+ if (sid != NULL) {
+ char *sid_str;
+ DOM_SID *dom_sid;
+ sid_str = smbldap_talloc_single_attribute(priv2ld(ldap_state),
+ entry, "sambaSID",
+ talloc_tos());
+ if (sid_str == NULL) {
+ return False;
+ }
+ dom_sid = string_sid_talloc(talloc_tos(), sid_str);
+ if (dom_sid == NULL) {
+ return False;
+ }
+ sid_copy(sid, dom_sid);
+ }
+
+ return True;
+}
+
+static bool ldapsam_set_trusteddom_pw(struct pdb_methods *methods,
+ const char* domain,
+ const char* pwd,
+ const DOM_SID *sid)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ char *prev_pwd = NULL;
+ char *trusted_dn = NULL;
+ int rc;
+
+ DEBUG(10, ("ldapsam_set_trusteddom_pw called for domain %s\n", domain));
+
+ /*
+ * get the current entry (if there is one) in order to put the
+ * current password into the previous password attribute
+ */
+ if (!get_trusteddom_pw_int(ldap_state, talloc_tos(), domain, &entry)) {
+ return False;
+ }
+
+ mods = NULL;
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods, "objectClass",
+ LDAP_OBJ_TRUSTDOM_PASSWORD);
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods, "sambaDomainName",
+ domain);
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods, "sambaSID",
+ sid_string_tos(sid));
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods, "sambaPwdLastSet",
+ talloc_asprintf(talloc_tos(), "%li", time(NULL)));
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods,
+ "sambaClearTextPassword", pwd);
+
+ talloc_autofree_ldapmod(talloc_tos(), mods);
+
+ if (entry != NULL) {
+ prev_pwd = smbldap_talloc_single_attribute(priv2ld(ldap_state),
+ entry, "sambaClearTextPassword", talloc_tos());
+ if (prev_pwd != NULL) {
+ smbldap_make_mod(priv2ld(ldap_state), entry, &mods,
+ "sambaPreviousClearTextPassword",
+ prev_pwd);
+ }
+ }
+
+ trusted_dn = trusteddom_dn(ldap_state, domain);
+ if (trusted_dn == NULL) {
+ return False;
+ }
+ if (entry == NULL) {
+ rc = smbldap_add(ldap_state->smbldap_state, trusted_dn, mods);
+ } else {
+ rc = smbldap_modify(ldap_state->smbldap_state, trusted_dn, mods);
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1, ("error writing trusted domain password!\n"));
+ return False;
+ }
+
+ return True;
+}
+
+static bool ldapsam_del_trusteddom_pw(struct pdb_methods *methods,
+ const char *domain)
+{
+ int rc;
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *entry = NULL;
+ const char *trusted_dn;
+
+ if (!get_trusteddom_pw_int(ldap_state, talloc_tos(), domain, &entry)) {
+ return False;
+ }
+
+ if (entry == NULL) {
+ DEBUG(5, ("ldapsam_del_trusteddom_pw: no such trusted domain: "
+ "%s\n", domain));
+ return True;
+ }
+
+ trusted_dn = smbldap_talloc_dn(talloc_tos(), priv2ld(ldap_state),
+ entry);
+ if (trusted_dn == NULL) {
+ DEBUG(0,("ldapsam_del_trusteddom_pw: Out of memory!\n"));
+ return False;
+ }
+
+ rc = smbldap_delete(ldap_state->smbldap_state, trusted_dn);
+ if (rc != LDAP_SUCCESS) {
+ return False;
+ }
+
+ return True;
+}
+
+static NTSTATUS ldapsam_enum_trusteddoms(struct pdb_methods *methods,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ struct trustdom_info ***domains)
+{
+ int rc;
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ char *filter;
+ int scope = LDAP_SCOPE_SUBTREE;
+ const char *attrs[] = { "sambaDomainName", "sambaSID", NULL };
+ int attrsonly = 0; /* 0: return values too */
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+
+ filter = talloc_asprintf(talloc_tos(), "(objectClass=%s)",
+ LDAP_OBJ_TRUSTDOM_PASSWORD);
+
+ rc = smbldap_search(ldap_state->smbldap_state,
+ ldap_state->domain_dn,
+ scope,
+ filter,
+ attrs,
+ attrsonly,
+ &result);
+
+ if (result != NULL) {
+ talloc_autofree_ldapmsg(mem_ctx, result);
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *num_domains = 0;
+ if (!(*domains = TALLOC_ARRAY(mem_ctx, struct trustdom_info *, 1))) {
+ DEBUG(1, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (entry = ldap_first_entry(priv2ld(ldap_state), result);
+ entry != NULL;
+ entry = ldap_next_entry(priv2ld(ldap_state), entry))
+ {
+ char *dom_name, *dom_sid_str;
+ struct trustdom_info *dom_info;
+
+ dom_info = TALLOC_P(*domains, struct trustdom_info);
+ if (dom_info == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dom_name = smbldap_talloc_single_attribute(priv2ld(ldap_state),
+ entry,
+ "sambaDomainName",
+ talloc_tos());
+ if (dom_name == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ dom_info->name = dom_name;
+
+ dom_sid_str = smbldap_talloc_single_attribute(
+ priv2ld(ldap_state), entry, "sambaSID",
+ talloc_tos());
+ if (dom_sid_str == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!string_to_sid(&dom_info->sid, dom_sid_str)) {
+ DEBUG(1, ("Error calling string_to_sid on SID %s\n",
+ dom_sid_str));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ADD_TO_ARRAY(*domains, struct trustdom_info *, dom_info,
+ domains, num_domains);
+
+ if (*domains == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ DEBUG(5, ("ldapsam_enum_trusteddoms: got %d domains\n", *num_domains));
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************************
+ Housekeeping
+ *********************************************************************/
+
+static void free_private_data(void **vp)
+{
+ struct ldapsam_privates **ldap_state = (struct ldapsam_privates **)vp;
+
+ smbldap_free_struct(&(*ldap_state)->smbldap_state);
+
+ if ((*ldap_state)->result != NULL) {
+ ldap_msgfree((*ldap_state)->result);
+ (*ldap_state)->result = NULL;
+ }
+ if ((*ldap_state)->domain_dn != NULL) {
+ SAFE_FREE((*ldap_state)->domain_dn);
+ }
+
+ *ldap_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+/*********************************************************************
+ Intitalise the parts of the pdb_methods structure that are common to
+ all pdb_ldap modes
+*********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam_common(struct pdb_methods **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam";
+
+ (*pdb_method)->getsampwnam = ldapsam_getsampwnam;
+ (*pdb_method)->getsampwsid = ldapsam_getsampwsid;
+ (*pdb_method)->add_sam_account = ldapsam_add_sam_account;
+ (*pdb_method)->update_sam_account = ldapsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = ldapsam_delete_sam_account;
+ (*pdb_method)->rename_sam_account = ldapsam_rename_sam_account;
+
+ (*pdb_method)->getgrsid = ldapsam_getgrsid;
+ (*pdb_method)->getgrgid = ldapsam_getgrgid;
+ (*pdb_method)->getgrnam = ldapsam_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = ldapsam_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = ldapsam_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = ldapsam_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = ldapsam_enum_group_mapping;
+
+ (*pdb_method)->get_account_policy = ldapsam_get_account_policy;
+ (*pdb_method)->set_account_policy = ldapsam_set_account_policy;
+
+ (*pdb_method)->get_seq_num = ldapsam_get_seq_num;
+
+ (*pdb_method)->rid_algorithm = ldapsam_rid_algorithm;
+ (*pdb_method)->new_rid = ldapsam_new_rid;
+
+ (*pdb_method)->get_trusteddom_pw = ldapsam_get_trusteddom_pw;
+ (*pdb_method)->set_trusteddom_pw = ldapsam_set_trusteddom_pw;
+ (*pdb_method)->del_trusteddom_pw = ldapsam_del_trusteddom_pw;
+ (*pdb_method)->enum_trusteddoms = ldapsam_enum_trusteddoms;
+
+ /* TODO: Setup private data and free */
+
+ if ( !(ldap_state = TALLOC_ZERO_P(*pdb_method, struct ldapsam_privates)) ) {
+ DEBUG(0, ("pdb_init_ldapsam_common: talloc() failed for ldapsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = smbldap_init(*pdb_method, pdb_get_event_context(),
+ location, &ldap_state->smbldap_state);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ return nt_status;
+ }
+
+ if ( !(ldap_state->domain_name = talloc_strdup(*pdb_method, get_global_sam_name()) ) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*pdb_method)->private_data = ldap_state;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise the 'compat' mode for pdb_ldap
+ *********************************************************************/
+
+NTSTATUS pdb_init_ldapsam_compat(struct pdb_methods **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+ char *uri = talloc_strdup( NULL, location );
+
+ trim_char( uri, '\"', '\"' );
+ nt_status = pdb_init_ldapsam_common( pdb_method, uri );
+ if ( uri )
+ TALLOC_FREE( uri );
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam_compat";
+
+ ldap_state = (struct ldapsam_privates *)((*pdb_method)->private_data);
+ ldap_state->schema_ver = SCHEMAVER_SAMBAACCOUNT;
+
+ sid_copy(&ldap_state->domain_sid, get_global_sam_sid());
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise the normal mode for pdb_ldap
+ *********************************************************************/
+
+NTSTATUS pdb_init_ldapsam(struct pdb_methods **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state = NULL;
+ uint32 alg_rid_base;
+ char *alg_rid_base_string = NULL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ DOM_SID ldap_domain_sid;
+ DOM_SID secrets_domain_sid;
+ char *domain_sid_string = NULL;
+ char *dn = NULL;
+ char *uri = talloc_strdup( NULL, location );
+
+ trim_char( uri, '\"', '\"' );
+ nt_status = pdb_init_ldapsam_common(pdb_method, uri);
+ if (uri) {
+ TALLOC_FREE(uri);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam";
+
+ (*pdb_method)->add_aliasmem = ldapsam_add_aliasmem;
+ (*pdb_method)->del_aliasmem = ldapsam_del_aliasmem;
+ (*pdb_method)->enum_aliasmem = ldapsam_enum_aliasmem;
+ (*pdb_method)->enum_alias_memberships = ldapsam_alias_memberships;
+ (*pdb_method)->search_users = ldapsam_search_users;
+ (*pdb_method)->search_groups = ldapsam_search_groups;
+ (*pdb_method)->search_aliases = ldapsam_search_aliases;
+
+ if (lp_parm_bool(-1, "ldapsam", "trusted", False)) {
+ (*pdb_method)->enum_group_members = ldapsam_enum_group_members;
+ (*pdb_method)->enum_group_memberships =
+ ldapsam_enum_group_memberships;
+ (*pdb_method)->lookup_rids = ldapsam_lookup_rids;
+ (*pdb_method)->sid_to_id = ldapsam_sid_to_id;
+
+ if (lp_parm_bool(-1, "ldapsam", "editposix", False)) {
+ (*pdb_method)->create_user = ldapsam_create_user;
+ (*pdb_method)->delete_user = ldapsam_delete_user;
+ (*pdb_method)->create_dom_group = ldapsam_create_dom_group;
+ (*pdb_method)->delete_dom_group = ldapsam_delete_dom_group;
+ (*pdb_method)->add_groupmem = ldapsam_add_groupmem;
+ (*pdb_method)->del_groupmem = ldapsam_del_groupmem;
+ (*pdb_method)->set_unix_primary_group = ldapsam_set_primary_group;
+ }
+ }
+
+ ldap_state = (struct ldapsam_privates *)((*pdb_method)->private_data);
+ ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT;
+
+ /* Try to setup the Domain Name, Domain SID, algorithmic rid base */
+
+ nt_status = smbldap_search_domain_info(ldap_state->smbldap_state,
+ &result,
+ ldap_state->domain_name, True);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(2, ("pdb_init_ldapsam: WARNING: Could not get domain "
+ "info, nor add one to the domain\n"));
+ DEBUGADD(2, ("pdb_init_ldapsam: Continuing on regardless, "
+ "will be unable to allocate new users/groups, "
+ "and will risk BDCs having inconsistant SIDs\n"));
+ sid_copy(&ldap_state->domain_sid, get_global_sam_sid());
+ return NT_STATUS_OK;
+ }
+
+ /* Given that the above might fail, everything below this must be
+ * optional */
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+ if (!entry) {
+ DEBUG(0, ("pdb_init_ldapsam: Could not get domain info "
+ "entry\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ldap_state->domain_dn = smb_xstrdup(dn);
+ ldap_memfree(dn);
+
+ domain_sid_string = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_USER_SID),
+ talloc_tos());
+
+ if (domain_sid_string) {
+ bool found_sid;
+ if (!string_to_sid(&ldap_domain_sid, domain_sid_string)) {
+ DEBUG(1, ("pdb_init_ldapsam: SID [%s] could not be "
+ "read as a valid SID\n", domain_sid_string));
+ ldap_msgfree(result);
+ TALLOC_FREE(domain_sid_string);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ found_sid = secrets_fetch_domain_sid(ldap_state->domain_name,
+ &secrets_domain_sid);
+ if (!found_sid || !sid_equal(&secrets_domain_sid,
+ &ldap_domain_sid)) {
+ DEBUG(1, ("pdb_init_ldapsam: Resetting SID for domain "
+ "%s based on pdb_ldap results %s -> %s\n",
+ ldap_state->domain_name,
+ sid_string_dbg(&secrets_domain_sid),
+ sid_string_dbg(&ldap_domain_sid)));
+
+ /* reset secrets.tdb sid */
+ secrets_store_domain_sid(ldap_state->domain_name,
+ &ldap_domain_sid);
+ DEBUG(1, ("New global sam SID: %s\n",
+ sid_string_dbg(get_global_sam_sid())));
+ }
+ sid_copy(&ldap_state->domain_sid, &ldap_domain_sid);
+ TALLOC_FREE(domain_sid_string);
+ }
+
+ alg_rid_base_string = smbldap_talloc_single_attribute(
+ ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string( dominfo_attr_list,
+ LDAP_ATTR_ALGORITHMIC_RID_BASE ),
+ talloc_tos());
+ if (alg_rid_base_string) {
+ alg_rid_base = (uint32)atol(alg_rid_base_string);
+ if (alg_rid_base != algorithmic_rid_base()) {
+ DEBUG(0, ("The value of 'algorithmic RID base' has "
+ "changed since the LDAP\n"
+ "database was initialised. Aborting. \n"));
+ ldap_msgfree(result);
+ TALLOC_FREE(alg_rid_base_string);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ TALLOC_FREE(alg_rid_base_string);
+ }
+ ldap_msgfree(result);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_ldap_init(void)
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam", pdb_init_ldapsam)))
+ return nt_status;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam_compat", pdb_init_ldapsam_compat)))
+ return nt_status;
+
+ /* Let pdb_nds register backends */
+ pdb_nds_init();
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/passdb/pdb_nds.c b/source3/passdb/pdb_nds.c
new file mode 100644
index 0000000000..1edd665d54
--- /dev/null
+++ b/source3/passdb/pdb_nds.c
@@ -0,0 +1,913 @@
+/*
+ Unix SMB/CIFS mplementation.
+ NDS LDAP helper functions for SAMBA
+ Copyright (C) Vince Brimhall 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+
+#include <lber.h>
+#include <ldap.h>
+#include <wchar.h>
+
+#include "smbldap.h"
+
+#define NMASLDAP_GET_LOGIN_CONFIG_REQUEST "2.16.840.1.113719.1.39.42.100.3"
+#define NMASLDAP_GET_LOGIN_CONFIG_RESPONSE "2.16.840.1.113719.1.39.42.100.4"
+#define NMASLDAP_SET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.11"
+#define NMASLDAP_SET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.12"
+#define NMASLDAP_GET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.13"
+#define NMASLDAP_GET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.14"
+
+#define NMAS_LDAP_EXT_VERSION 1
+
+/**********************************************************************
+ Take the request BER value and input data items and BER encodes the
+ data into the BER value
+**********************************************************************/
+
+static int berEncodePasswordData(
+ struct berval **requestBV,
+ const char *objectDN,
+ const char *password,
+ const char *password2)
+{
+ int err = 0, rc=0;
+ BerElement *requestBer = NULL;
+
+ const char * utf8ObjPtr = NULL;
+ int utf8ObjSize = 0;
+ const char * utf8PwdPtr = NULL;
+ int utf8PwdSize = 0;
+ const char * utf8Pwd2Ptr = NULL;
+ int utf8Pwd2Size = 0;
+
+
+ /* Convert objectDN and tag strings from Unicode to UTF-8 */
+ utf8ObjSize = strlen(objectDN)+1;
+ utf8ObjPtr = objectDN;
+
+ if (password != NULL)
+ {
+ utf8PwdSize = strlen(password)+1;
+ utf8PwdPtr = password;
+ }
+
+ if (password2 != NULL)
+ {
+ utf8Pwd2Size = strlen(password2)+1;
+ utf8Pwd2Ptr = password2;
+ }
+
+ /* Allocate a BerElement for the request parameters. */
+ if((requestBer = ber_alloc()) == NULL)
+ {
+ err = LDAP_ENCODING_ERROR;
+ goto Cleanup;
+ }
+
+ if (password != NULL && password2 != NULL)
+ {
+ /* BER encode the NMAS Version, the objectDN, and the password */
+ rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size);
+ }
+ else if (password != NULL)
+ {
+ /* BER encode the NMAS Version, the objectDN, and the password */
+ rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize);
+ }
+ else
+ {
+ /* BER encode the NMAS Version and the objectDN */
+ rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize);
+ }
+
+ if (rc < 0)
+ {
+ err = LDAP_ENCODING_ERROR;
+ goto Cleanup;
+ }
+ else
+ {
+ err = 0;
+ }
+
+ /* Convert the BER we just built to a berval that we'll send with the extended request. */
+ if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
+ {
+ err = LDAP_ENCODING_ERROR;
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if(requestBer)
+ {
+ ber_free(requestBer, 1);
+ }
+
+ return err;
+}
+
+/**********************************************************************
+ Take the request BER value and input data items and BER encodes the
+ data into the BER value
+**********************************************************************/
+
+static int berEncodeLoginData(
+ struct berval **requestBV,
+ char *objectDN,
+ unsigned int methodIDLen,
+ unsigned int *methodID,
+ char *tag,
+ size_t putDataLen,
+ void *putData)
+{
+ int err = 0;
+ BerElement *requestBer = NULL;
+
+ unsigned int i;
+ unsigned int elemCnt = methodIDLen / sizeof(unsigned int);
+
+ char *utf8ObjPtr=NULL;
+ int utf8ObjSize = 0;
+
+ char *utf8TagPtr = NULL;
+ int utf8TagSize = 0;
+
+ utf8ObjPtr = objectDN;
+ utf8ObjSize = strlen(utf8ObjPtr)+1;
+
+ utf8TagPtr = tag;
+ utf8TagSize = strlen(utf8TagPtr)+1;
+
+ /* Allocate a BerElement for the request parameters. */
+ if((requestBer = ber_alloc()) == NULL)
+ {
+ err = LDAP_ENCODING_ERROR;
+ goto Cleanup;
+ }
+
+ /* BER encode the NMAS Version and the objectDN */
+ err = (ber_printf(requestBer, "{io", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize) < 0) ? LDAP_ENCODING_ERROR : 0;
+
+ /* BER encode the MethodID Length and value */
+ if (!err)
+ {
+ err = (ber_printf(requestBer, "{i{", methodIDLen) < 0) ? LDAP_ENCODING_ERROR : 0;
+ }
+
+ for (i = 0; !err && i < elemCnt; i++)
+ {
+ err = (ber_printf(requestBer, "i", methodID[i]) < 0) ? LDAP_ENCODING_ERROR : 0;
+ }
+
+ if (!err)
+ {
+ err = (ber_printf(requestBer, "}}", 0) < 0) ? LDAP_ENCODING_ERROR : 0;
+ }
+
+ if(putData)
+ {
+ /* BER Encode the the tag and data */
+ err = (ber_printf(requestBer, "oio}", utf8TagPtr, utf8TagSize, putDataLen, putData, putDataLen) < 0) ? LDAP_ENCODING_ERROR : 0;
+ }
+ else
+ {
+ /* BER Encode the the tag */
+ err = (ber_printf(requestBer, "o}", utf8TagPtr, utf8TagSize) < 0) ? LDAP_ENCODING_ERROR : 0;
+ }
+
+ if (err)
+ {
+ goto Cleanup;
+ }
+
+ /* Convert the BER we just built to a berval that we'll send with the extended request. */
+ if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
+ {
+ err = LDAP_ENCODING_ERROR;
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if(requestBer)
+ {
+ ber_free(requestBer, 1);
+ }
+
+ return err;
+}
+
+/**********************************************************************
+ Takes the reply BER Value and decodes the NMAS server version and
+ return code and if a non null retData buffer was supplied, tries to
+ decode the the return data and length
+**********************************************************************/
+
+static int berDecodeLoginData(
+ struct berval *replyBV,
+ int *serverVersion,
+ size_t *retDataLen,
+ void *retData )
+{
+ int err = 0;
+ BerElement *replyBer = NULL;
+ char *retOctStr = NULL;
+ size_t retOctStrLen = 0;
+
+ if((replyBer = ber_init(replyBV)) == NULL)
+ {
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+ if(retData)
+ {
+ retOctStrLen = *retDataLen + 1;
+ retOctStr = SMB_MALLOC_ARRAY(char, retOctStrLen);
+ if(!retOctStr)
+ {
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+ if(ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen) != -1)
+ {
+ if (*retDataLen >= retOctStrLen)
+ {
+ memcpy(retData, retOctStr, retOctStrLen);
+ }
+ else if (!err)
+ {
+ err = LDAP_NO_MEMORY;
+ }
+
+ *retDataLen = retOctStrLen;
+ }
+ else if (!err)
+ {
+ err = LDAP_DECODING_ERROR;
+ }
+ }
+ else
+ {
+ if(ber_scanf(replyBer, "{ii}", serverVersion, &err) == -1)
+ {
+ if (!err)
+ {
+ err = LDAP_DECODING_ERROR;
+ }
+ }
+ }
+
+Cleanup:
+
+ if(replyBer)
+ {
+ ber_free(replyBer, 1);
+ }
+
+ if (retOctStr != NULL)
+ {
+ memset(retOctStr, 0, retOctStrLen);
+ free(retOctStr);
+ }
+
+ return err;
+}
+
+/**********************************************************************
+ Retrieves data in the login configuration of the specified object
+ that is tagged with the specified methodID and tag.
+**********************************************************************/
+
+static int getLoginConfig(
+ LDAP *ld,
+ char *objectDN,
+ unsigned int methodIDLen,
+ unsigned int *methodID,
+ char *tag,
+ size_t *dataLen,
+ void *data )
+{
+ int err = 0;
+ struct berval *requestBV = NULL;
+ char *replyOID = NULL;
+ struct berval *replyBV = NULL;
+ int serverVersion = 0;
+
+ /* Validate unicode parameters. */
+ if((strlen(objectDN) == 0) || ld == NULL)
+ {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ err = berEncodeLoginData(&requestBV, objectDN, methodIDLen, methodID, tag, 0, NULL);
+ if(err)
+ {
+ goto Cleanup;
+ }
+
+ /* Call the ldap_extended_operation (synchronously) */
+ if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_LOGIN_CONFIG_REQUEST,
+ requestBV, NULL, NULL, &replyOID, &replyBV)))
+ {
+ goto Cleanup;
+ }
+
+ /* Make sure there is a return OID */
+ if(!replyOID)
+ {
+ err = LDAP_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ /* Is this what we were expecting to get back. */
+ if(strcmp(replyOID, NMASLDAP_GET_LOGIN_CONFIG_RESPONSE))
+ {
+ err = LDAP_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ /* Do we have a good returned berval? */
+ if(!replyBV)
+ {
+ /* No; returned berval means we experienced a rather drastic error. */
+ /* Return operations error. */
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+ err = berDecodeLoginData(replyBV, &serverVersion, dataLen, data);
+
+ if(serverVersion != NMAS_LDAP_EXT_VERSION)
+ {
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if(replyBV)
+ {
+ ber_bvfree(replyBV);
+ }
+
+ /* Free the return OID string if one was returned. */
+ if(replyOID)
+ {
+ ldap_memfree(replyOID);
+ }
+
+ /* Free memory allocated while building the request ber and berval. */
+ if(requestBV)
+ {
+ ber_bvfree(requestBV);
+ }
+
+ /* Return the appropriate error/success code. */
+ return err;
+}
+
+/**********************************************************************
+ Attempts to get the Simple Password
+**********************************************************************/
+
+static int nmasldap_get_simple_pwd(
+ LDAP *ld,
+ char *objectDN,
+ size_t pwdLen,
+ char *pwd )
+{
+ int err = 0;
+ unsigned int methodID = 0;
+ unsigned int methodIDLen = sizeof(methodID);
+ char tag[] = {'P','A','S','S','W','O','R','D',' ','H','A','S','H',0};
+ char *pwdBuf=NULL;
+ size_t pwdBufLen, bufferLen;
+
+ bufferLen = pwdBufLen = pwdLen+2;
+ pwdBuf = SMB_MALLOC_ARRAY(char, pwdBufLen); /* digest and null */
+ if(pwdBuf == NULL)
+ {
+ return LDAP_NO_MEMORY;
+ }
+
+ err = getLoginConfig(ld, objectDN, methodIDLen, &methodID, tag, &pwdBufLen, pwdBuf);
+ if (err == 0)
+ {
+ if (pwdBufLen !=0)
+ {
+ pwdBuf[pwdBufLen] = 0; /* null terminate */
+
+ switch (pwdBuf[0])
+ {
+ case 1: /* cleartext password */
+ break;
+ case 2: /* SHA1 HASH */
+ case 3: /* MD5_ID */
+ case 4: /* UNIXCrypt_ID */
+ case 8: /* SSHA_ID */
+ default: /* Unknown digest */
+ err = LDAP_INAPPROPRIATE_AUTH; /* only return clear text */
+ break;
+ }
+
+ if (!err)
+ {
+ if (pwdLen >= pwdBufLen-1)
+ {
+ memcpy(pwd, &pwdBuf[1], pwdBufLen-1); /* skip digest tag and include null */
+ }
+ else
+ {
+ err = LDAP_NO_MEMORY;
+ }
+ }
+ }
+ }
+
+ if (pwdBuf != NULL)
+ {
+ memset(pwdBuf, 0, bufferLen);
+ free(pwdBuf);
+ }
+
+ return err;
+}
+
+
+/**********************************************************************
+ Attempts to set the Universal Password
+**********************************************************************/
+
+static int nmasldap_set_password(
+ LDAP *ld,
+ const char *objectDN,
+ const char *pwd )
+{
+ int err = 0;
+
+ struct berval *requestBV = NULL;
+ char *replyOID = NULL;
+ struct berval *replyBV = NULL;
+ int serverVersion;
+
+ /* Validate char parameters. */
+ if(objectDN == NULL || (strlen(objectDN) == 0) || pwd == NULL || ld == NULL)
+ {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ err = berEncodePasswordData(&requestBV, objectDN, pwd, NULL);
+ if(err)
+ {
+ goto Cleanup;
+ }
+
+ /* Call the ldap_extended_operation (synchronously) */
+ if((err = ldap_extended_operation_s(ld, NMASLDAP_SET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV)))
+ {
+ goto Cleanup;
+ }
+
+ /* Make sure there is a return OID */
+ if(!replyOID)
+ {
+ err = LDAP_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ /* Is this what we were expecting to get back. */
+ if(strcmp(replyOID, NMASLDAP_SET_PASSWORD_RESPONSE))
+ {
+ err = LDAP_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ /* Do we have a good returned berval? */
+ if(!replyBV)
+ {
+ /* No; returned berval means we experienced a rather drastic error. */
+ /* Return operations error. */
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+ err = berDecodeLoginData(replyBV, &serverVersion, NULL, NULL);
+
+ if(serverVersion != NMAS_LDAP_EXT_VERSION)
+ {
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+Cleanup:
+
+ if(replyBV)
+ {
+ ber_bvfree(replyBV);
+ }
+
+ /* Free the return OID string if one was returned. */
+ if(replyOID)
+ {
+ ldap_memfree(replyOID);
+ }
+
+ /* Free memory allocated while building the request ber and berval. */
+ if(requestBV)
+ {
+ ber_bvfree(requestBV);
+ }
+
+ /* Return the appropriate error/success code. */
+ return err;
+}
+
+/**********************************************************************
+ Attempts to get the Universal Password
+**********************************************************************/
+
+static int nmasldap_get_password(
+ LDAP *ld,
+ char *objectDN,
+ size_t *pwdSize, /* in bytes */
+ unsigned char *pwd )
+{
+ int err = 0;
+
+ struct berval *requestBV = NULL;
+ char *replyOID = NULL;
+ struct berval *replyBV = NULL;
+ int serverVersion;
+ char *pwdBuf;
+ size_t pwdBufLen, bufferLen;
+
+ /* Validate char parameters. */
+ if(objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL)
+ {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ bufferLen = pwdBufLen = *pwdSize;
+ pwdBuf = SMB_MALLOC_ARRAY(char, pwdBufLen+2);
+ if(pwdBuf == NULL)
+ {
+ return LDAP_NO_MEMORY;
+ }
+
+ err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL);
+ if(err)
+ {
+ goto Cleanup;
+ }
+
+ /* Call the ldap_extended_operation (synchronously) */
+ if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV)))
+ {
+ goto Cleanup;
+ }
+
+ /* Make sure there is a return OID */
+ if(!replyOID)
+ {
+ err = LDAP_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ /* Is this what we were expecting to get back. */
+ if(strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE))
+ {
+ err = LDAP_NOT_SUPPORTED;
+ goto Cleanup;
+ }
+
+ /* Do we have a good returned berval? */
+ if(!replyBV)
+ {
+ /* No; returned berval means we experienced a rather drastic error. */
+ /* Return operations error. */
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+ err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf);
+
+ if(serverVersion != NMAS_LDAP_EXT_VERSION)
+ {
+ err = LDAP_OPERATIONS_ERROR;
+ goto Cleanup;
+ }
+
+ if (!err && pwdBufLen != 0)
+ {
+ if (*pwdSize >= pwdBufLen+1 && pwd != NULL)
+ {
+ memcpy(pwd, pwdBuf, pwdBufLen);
+ pwd[pwdBufLen] = 0; /* add null termination */
+ }
+ *pwdSize = pwdBufLen; /* does not include null termination */
+ }
+
+Cleanup:
+
+ if(replyBV)
+ {
+ ber_bvfree(replyBV);
+ }
+
+ /* Free the return OID string if one was returned. */
+ if(replyOID)
+ {
+ ldap_memfree(replyOID);
+ }
+
+ /* Free memory allocated while building the request ber and berval. */
+ if(requestBV)
+ {
+ ber_bvfree(requestBV);
+ }
+
+ if (pwdBuf != NULL)
+ {
+ memset(pwdBuf, 0, bufferLen);
+ free(pwdBuf);
+ }
+
+ /* Return the appropriate error/success code. */
+ return err;
+}
+
+/**********************************************************************
+ Get the user's password from NDS.
+ *********************************************************************/
+
+int pdb_nds_get_password(
+ struct smbldap_state *ldap_state,
+ char *object_dn,
+ size_t *pwd_len,
+ char *pwd )
+{
+ LDAP *ld = ldap_state->ldap_struct;
+ int rc = -1;
+
+ rc = nmasldap_get_password(ld, object_dn, pwd_len, (unsigned char *)pwd);
+ if (rc == LDAP_SUCCESS) {
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nmasldap_get_password returned %s for %s\n", pwd, object_dn));
+#endif
+ DEBUG(5, ("NDS Universal Password retrieved for %s\n", object_dn));
+ } else {
+ DEBUG(3, ("NDS Universal Password NOT retrieved for %s\n", object_dn));
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ rc = nmasldap_get_simple_pwd(ld, object_dn, *pwd_len, pwd);
+ if (rc == LDAP_SUCCESS) {
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nmasldap_get_simple_pwd returned %s for %s\n", pwd, object_dn));
+#endif
+ DEBUG(5, ("NDS Simple Password retrieved for %s\n", object_dn));
+ } else {
+ /* We couldn't get the password */
+ DEBUG(3, ("NDS Simple Password NOT retrieved for %s\n", object_dn));
+ return LDAP_INVALID_CREDENTIALS;
+ }
+ }
+
+ /* We got the password */
+ return LDAP_SUCCESS;
+}
+
+/**********************************************************************
+ Set the users NDS, Universal and Simple passwords.
+ ********************************************************************/
+
+int pdb_nds_set_password(
+ struct smbldap_state *ldap_state,
+ char *object_dn,
+ const char *pwd )
+{
+ LDAP *ld = ldap_state->ldap_struct;
+ int rc = -1;
+ LDAPMod **tmpmods = NULL;
+
+ rc = nmasldap_set_password(ld, object_dn, pwd);
+ if (rc == LDAP_SUCCESS) {
+ DEBUG(5,("NDS Universal Password changed for user %s\n", object_dn));
+ } else {
+ char *ld_error = NULL;
+ ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
+
+ /* This will fail if Universal Password is not enabled for the user's context */
+ DEBUG(3,("NDS Universal Password could not be changed for user %s: %s (%s)\n",
+ object_dn, ldap_err2string(rc), ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ }
+
+ /* Set eDirectory Password */
+ smbldap_set_mod(&tmpmods, LDAP_MOD_REPLACE, "userPassword", pwd);
+ rc = smbldap_modify(ldap_state, object_dn, tmpmods);
+
+ return rc;
+}
+
+/**********************************************************************
+ Allow ldap server to update internal login attempt counters by
+ performing a simple bind. If the samba authentication failed attempt
+ the bind with a bogus, randomly generated password to count the
+ failed attempt. If the bind fails even though samba authentication
+ succeeded, this would indicate that the user's account is disabled,
+ time restrictions are in place or some other password policy
+ violation.
+*********************************************************************/
+
+static NTSTATUS pdb_nds_update_login_attempts(struct pdb_methods *methods,
+ struct samu *sam_acct, bool success)
+{
+ struct ldapsam_privates *ldap_state;
+
+ if ((!methods) || (!sam_acct)) {
+ DEBUG(3,("pdb_nds_update_login_attempts: invalid parameter.\n"));
+ return NT_STATUS_MEMORY_NOT_ALLOCATED;
+ }
+
+ ldap_state = (struct ldapsam_privates *)methods->private_data;
+
+ if (ldap_state) {
+ /* Attempt simple bind with user credentials to update eDirectory
+ password policy */
+ int rc = 0;
+ char *dn;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ const char **attr_list;
+ size_t pwd_len;
+ char clear_text_pw[512];
+ LDAP *ld = NULL;
+ const char *username = pdb_get_username(sam_acct);
+ bool got_clear_text_pw = False;
+
+ DEBUG(5,("pdb_nds_update_login_attempts: %s login for %s\n",
+ success ? "Successful" : "Failed", username));
+
+ result = (LDAPMessage *)pdb_get_backend_private_data(sam_acct, methods);
+ if (!result) {
+ attr_list = get_userattr_list(NULL,
+ ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_name(ldap_state, username, &result, attr_list );
+ TALLOC_FREE( attr_list );
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ pdb_set_backend_private_data(sam_acct, result, NULL,
+ methods, PDB_CHANGED);
+ talloc_autofree_ldapmsg(sam_acct, result);
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
+ DEBUG(0, ("pdb_nds_update_login_attempts: No user to modify!\n"));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ DEBUG(3, ("pdb_nds_update_login_attempts: username %s found dn '%s'\n", username, dn));
+
+ pwd_len = sizeof(clear_text_pw);
+ if (success == True) {
+ if (pdb_nds_get_password(ldap_state->smbldap_state, dn, &pwd_len, clear_text_pw) == LDAP_SUCCESS) {
+ /* Got clear text password. Use simple ldap bind */
+ got_clear_text_pw = True;
+ }
+ } else {
+ generate_random_buffer((unsigned char *)clear_text_pw, 24);
+ clear_text_pw[24] = '\0';
+ DEBUG(5,("pdb_nds_update_login_attempts: using random password %s\n", clear_text_pw));
+ }
+
+ if((success != True) || (got_clear_text_pw == True)) {
+
+ rc = smb_ldap_setup_full_conn(&ld, ldap_state->location);
+ if (rc) {
+ return NT_STATUS_INVALID_CONNECTION;
+ }
+
+ /* Attempt simple bind with real or bogus password */
+ rc = ldap_simple_bind_s(ld, dn, clear_text_pw);
+ ldap_unbind(ld);
+ if (rc == LDAP_SUCCESS) {
+ DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Successful for %s\n", username));
+ } else {
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_RESTRICTION;
+ DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Failed for %s\n", username));
+ switch(rc) {
+ case LDAP_INVALID_CREDENTIALS:
+ nt_status = NT_STATUS_WRONG_PASSWORD;
+ break;
+ case LDAP_UNWILLING_TO_PERFORM:
+ /* eDir returns this if the account was disabled. */
+ /* The problem is we don't know if the given
+ password was correct for this account or
+ not. We have to return more info than we
+ should and tell the client NT_STATUS_ACCOUNT_DISABLED
+ so they don't think the password was bad. JRA. */
+ nt_status = NT_STATUS_ACCOUNT_DISABLED;
+ break;
+ default:
+ break;
+ }
+ return nt_status;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Intitalise the parts of the pdb_methods structuire that are common
+ to NDS_ldapsam modes
+ *********************************************************************/
+
+static NTSTATUS pdb_init_NDS_ldapsam_common(struct pdb_methods **pdb_method, const char *location)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)((*pdb_method)->private_data);
+
+ /* Mark this as eDirectory ldap */
+ ldap_state->is_nds_ldap = True;
+
+ /* Add pdb_nds specific method for updating login attempts. */
+ (*pdb_method)->update_login_attempts = pdb_nds_update_login_attempts;
+
+ /* Save location for use in pdb_nds_update_login_attempts */
+ ldap_state->location = SMB_STRDUP(location);
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************************
+ Initialise the 'nds compat' mode for pdb_ldap
+ *********************************************************************/
+
+static NTSTATUS pdb_init_NDS_ldapsam_compat(struct pdb_methods **pdb_method, const char *location)
+{
+ NTSTATUS nt_status = pdb_init_ldapsam_compat(pdb_method, location);
+
+ (*pdb_method)->name = "NDS_ldapsam_compat";
+
+ pdb_init_NDS_ldapsam_common(pdb_method, location);
+
+ return nt_status;
+}
+
+
+/**********************************************************************
+ Initialise the 'nds' normal mode for pdb_ldap
+ *********************************************************************/
+
+static NTSTATUS pdb_init_NDS_ldapsam(struct pdb_methods **pdb_method, const char *location)
+{
+ NTSTATUS nt_status = pdb_init_ldapsam(pdb_method, location);
+
+ (*pdb_method)->name = "NDS_ldapsam";
+
+ pdb_init_NDS_ldapsam_common(pdb_method, location);
+
+ return nt_status;
+}
+
+NTSTATUS pdb_nds_init(void)
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam", pdb_init_NDS_ldapsam)))
+ return nt_status;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam_compat", pdb_init_NDS_ldapsam_compat)))
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/passdb/pdb_smbpasswd.c b/source3/passdb/pdb_smbpasswd.c
new file mode 100644
index 0000000000..f72638bed5
--- /dev/null
+++ b/source3/passdb/pdb_smbpasswd.c
@@ -0,0 +1,1718 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Modified by Jeremy Allison 1995.
+ * Modified by Gerald (Jerry) Carter 2000-2001,2003
+ * Modified by Andrew Bartlett 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 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/*
+ smb_passwd is analogous to sam_passwd used everywhere
+ else. However, smb_passwd is limited to the information
+ stored by an smbpasswd entry
+ */
+
+struct smb_passwd
+{
+ uint32 smb_userid; /* this is actually the unix uid_t */
+ const char *smb_name; /* username string */
+
+ const unsigned char *smb_passwd; /* Null if no password */
+ const unsigned char *smb_nt_passwd; /* Null if no password */
+
+ uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+ time_t pass_last_set_time; /* password last set time */
+};
+
+struct smbpasswd_privates
+{
+ /* used for maintain locks on the smbpasswd file */
+ int pw_file_lock_depth;
+
+ /* Global File pointer */
+ FILE *pw_file;
+
+ /* formerly static variables */
+ struct smb_passwd pw_buf;
+ fstring user_name;
+ unsigned char smbpwd[16];
+ unsigned char smbntpwd[16];
+
+ /* retrive-once info */
+ const char *smbpasswd_file;
+};
+
+enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+/***************************************************************
+ Lock or unlock a fd for a known lock type. Abandon after waitsecs
+ seconds.
+****************************************************************/
+
+static bool do_file_lock(int fd, int waitsecs, int type)
+{
+ SMB_STRUCT_FLOCK lock;
+ int ret;
+ void (*oldsig_handler)(int);
+
+ gotalarm = 0;
+ oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ lock.l_pid = 0;
+
+ alarm(waitsecs);
+ /* Note we must *NOT* use sys_fcntl here ! JRA */
+ ret = fcntl(fd, SMB_F_SETLKW, &lock);
+ alarm(0);
+ CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
+
+ if (gotalarm) {
+ DEBUG(0, ("do_file_lock: failed to %s file.\n",
+ type == F_UNLCK ? "unlock" : "lock"));
+ return False;
+ }
+
+ return (ret == 0);
+}
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static bool pw_file_lock(int fd, int type, int secs, int *plock_depth)
+{
+ if (fd < 0) {
+ return False;
+ }
+
+ if(*plock_depth == 0) {
+ if (!do_file_lock(fd, secs, type)) {
+ DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+
+ (*plock_depth)++;
+
+ return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static bool pw_file_unlock(int fd, int *plock_depth)
+{
+ bool ret=True;
+
+ if (fd == 0 || *plock_depth == 0) {
+ return True;
+ }
+
+ if(*plock_depth == 1) {
+ ret = do_file_lock(fd, 5, F_UNLCK);
+ }
+
+ if (*plock_depth > 0) {
+ (*plock_depth)--;
+ }
+
+ if(!ret) {
+ DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
+ strerror(errno)));
+ }
+ return ret;
+}
+
+/**************************************************************
+ Intialize a smb_passwd struct
+ *************************************************************/
+
+static void pdb_init_smb(struct smb_passwd *user)
+{
+ if (user == NULL)
+ return;
+ ZERO_STRUCTP (user);
+
+ user->pass_last_set_time = (time_t)0;
+}
+
+/***************************************************************
+ Internal fn to enumerate the smbpasswd list. Returns a void pointer
+ to ensure no modification outside this module. Checks for atomic
+ rename of smbpasswd file on update or create once the lock has
+ been granted to prevent race conditions. JRA.
+****************************************************************/
+
+static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
+{
+ FILE *fp = NULL;
+ const char *open_mode = NULL;
+ int race_loop = 0;
+ int lock_type = F_RDLCK;
+
+ if (!*pfile) {
+ DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
+ return (NULL);
+ }
+
+ switch(type) {
+ case PWF_READ:
+ open_mode = "rb";
+ lock_type = F_RDLCK;
+ break;
+ case PWF_UPDATE:
+ open_mode = "r+b";
+ lock_type = F_WRLCK;
+ break;
+ case PWF_CREATE:
+ /*
+ * Ensure atomic file creation.
+ */
+ {
+ int i, fd = -1;
+
+ for(i = 0; i < 5; i++) {
+ if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) {
+ break;
+ }
+ sys_usleep(200); /* Spin, spin... */
+ }
+ if(fd == -1) {
+ DEBUG(0,("startsmbfilepwent_internal: too many race conditions \
+creating file %s\n", pfile));
+ return NULL;
+ }
+ close(fd);
+ open_mode = "r+b";
+ lock_type = F_WRLCK;
+ break;
+ }
+ }
+
+ for(race_loop = 0; race_loop < 5; race_loop++) {
+ DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
+
+ if((fp = sys_fopen(pfile, open_mode)) == NULL) {
+
+ /*
+ * If smbpasswd file doesn't exist, then create new one. This helps to avoid
+ * confusing error msg when adding user account first time.
+ */
+ if (errno == ENOENT) {
+ if ((fp = sys_fopen(pfile, "a+")) != NULL) {
+ DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
+exist. File successfully created.\n", pfile));
+ } else {
+ DEBUG(0, ("startsmbfilepwent_internal: file %s did not \
+exist. Couldn't create new one. Error was: %s",
+ pfile, strerror(errno)));
+ return NULL;
+ }
+ } else {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. \
+Error was: %s\n", pfile, strerror(errno)));
+ return NULL;
+ }
+ }
+
+ if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. \
+Error was %s\n", pfile, strerror(errno) ));
+ fclose(fp);
+ return NULL;
+ }
+
+ /*
+ * Only check for replacement races on update or create.
+ * For read we don't mind if the data is one record out of date.
+ */
+
+ if(type == PWF_READ) {
+ break;
+ } else {
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+
+ /*
+ * Avoid the potential race condition between the open and the lock
+ * by doing a stat on the filename and an fstat on the fd. If the
+ * two inodes differ then someone did a rename between the open and
+ * the lock. Back off and try the open again. Only do this 5 times to
+ * prevent infinate loops. JRA.
+ */
+
+ if (sys_stat(pfile,&sbuf1) != 0) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. \
+Error was %s\n", pfile, strerror(errno)));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ if (sys_fstat(fileno(fp),&sbuf2) != 0) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. \
+Error was %s\n", pfile, strerror(errno)));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ if( sbuf1.st_ino == sbuf2.st_ino) {
+ /* No race. */
+ break;
+ }
+
+ /*
+ * Race occurred - back off and try again...
+ */
+
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ }
+ }
+
+ if(race_loop == 5) {
+ DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
+ return NULL;
+ }
+
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, (char *)NULL, _IOFBF, 1024);
+
+ /* Make sure it is only rw by the owner */
+#ifdef HAVE_FCHMOD
+ if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
+#else
+ if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
+#endif
+ DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
+Error was %s\n.", pfile, strerror(errno) ));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ /* We have a lock on the file. */
+ return fp;
+}
+
+/***************************************************************
+ End enumeration of the smbpasswd list.
+****************************************************************/
+
+static void endsmbfilepwent(FILE *fp, int *lock_depth)
+{
+ if (!fp) {
+ return;
+ }
+
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
+}
+
+/*************************************************************************
+ Routine to return the next entry in the smbpasswd list.
+ *************************************************************************/
+
+static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
+{
+ /* Static buffers we will return. */
+ struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
+ char *user_name = smbpasswd_state->user_name;
+ unsigned char *smbpwd = smbpasswd_state->smbpwd;
+ unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
+ char linebuf[256];
+ int c;
+ unsigned char *p;
+ long uidval;
+ size_t linebuf_len;
+ char *status;
+
+ if(fp == NULL) {
+ DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
+ return NULL;
+ }
+
+ pdb_init_smb(pw_buf);
+ pw_buf->acct_ctrl = ACB_NORMAL;
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+ status = linebuf;
+ while (status && !feof(fp)) {
+ linebuf[0] = '\0';
+
+ status = fgets(linebuf, 256, fp);
+ if (status == NULL && ferror(fp)) {
+ return NULL;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+ if ((linebuf_len = strlen(linebuf)) == 0) {
+ continue;
+ }
+
+ if (linebuf[linebuf_len - 1] != '\n') {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp)) {
+ c = fgetc(fp);
+ if (c == '\n') {
+ break;
+ }
+ }
+ } else {
+ linebuf[linebuf_len - 1] = '\0';
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
+#endif
+ if ((linebuf[0] == 0) && feof(fp)) {
+ DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
+ break;
+ }
+
+ /*
+ * The line we have should be of the form :-
+ *
+ * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
+ * ignored....
+ *
+ * or,
+ *
+ * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
+ *
+ * if Windows NT compatible passwords are also present.
+ * [Account type] is an ascii encoding of the type of account.
+ * LCT-(8 hex digits) is the time_t value of the last change time.
+ */
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0') {
+ DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
+ continue;
+ }
+ p = (unsigned char *) strchr_m(linebuf, ':');
+ if (p == NULL) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
+ continue;
+ }
+
+ strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+ user_name[PTR_DIFF(p, linebuf)] = '\0';
+
+ /* Get smb uid. */
+
+ p++; /* Go past ':' */
+
+ if(*p == '-') {
+ DEBUG(0, ("getsmbfilepwent: user name %s has a negative uid.\n", user_name));
+ continue;
+ }
+
+ if (!isdigit(*p)) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (uid not number)\n",
+ user_name));
+ continue;
+ }
+
+ uidval = atoi((char *) p);
+
+ while (*p && isdigit(*p)) {
+ p++;
+ }
+
+ if (*p != ':') {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no : after uid)\n",
+ user_name));
+ continue;
+ }
+
+ pw_buf->smb_name = user_name;
+ pw_buf->smb_userid = uidval;
+
+ /*
+ * Now get the password value - this should be 32 hex digits
+ * which are the ascii representations of a 16 byte string.
+ * Get two at a time and put them into the password.
+ */
+
+ /* Skip the ':' */
+ p++;
+
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (passwd too short)\n",
+ user_name ));
+ continue;
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry for user %s (no terminating :)\n",
+ user_name));
+ continue;
+ }
+
+ if (strnequal((char *) p, "NO PASSWORD", 11)) {
+ pw_buf->smb_passwd = NULL;
+ pw_buf->acct_ctrl |= ACB_PWNOTREQ;
+ } else {
+ if (*p == '*' || *p == 'X') {
+ /* NULL LM password */
+ pw_buf->smb_passwd = NULL;
+ DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
+ } else if (pdb_gethexpwd((char *)p, smbpwd)) {
+ pw_buf->smb_passwd = smbpwd;
+ } else {
+ pw_buf->smb_passwd = NULL;
+ DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry for user %s \
+(non hex chars)\n", user_name));
+ }
+ }
+
+ /*
+ * Now check if the NT compatible password is
+ * available.
+ */
+ pw_buf->smb_nt_passwd = NULL;
+ p += 33; /* Move to the first character of the line after the lanman password. */
+ if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
+ if (*p != '*' && *p != 'X') {
+ if(pdb_gethexpwd((char *)p,smbntpwd)) {
+ pw_buf->smb_nt_passwd = smbntpwd;
+ }
+ }
+ p += 33; /* Move to the first character of the line after the NT password. */
+ }
+
+ DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
+ user_name, uidval));
+
+ if (*p == '[') {
+ unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
+ pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
+
+ /* Must have some account type set. */
+ if(pw_buf->acct_ctrl == 0) {
+ pw_buf->acct_ctrl = ACB_NORMAL;
+ }
+
+ /* Now try and get the last change time. */
+ if(end_p) {
+ p = end_p + 1;
+ }
+ if(*p == ':') {
+ p++;
+ if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
+ int i;
+ p += 4;
+ for(i = 0; i < 8; i++) {
+ if(p[i] == '\0' || !isxdigit(p[i])) {
+ break;
+ }
+ }
+ if(i == 8) {
+ /*
+ * p points at 8 characters of hex digits -
+ * read into a time_t as the seconds since
+ * 1970 that the password was last changed.
+ */
+ pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
+ }
+ }
+ }
+ } else {
+ /* 'Old' style file. Fake up based on user name. */
+ /*
+ * Currently trust accounts are kept in the same
+ * password file as 'normal accounts'. If this changes
+ * we will have to fix this code. JRA.
+ */
+ if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
+ pw_buf->acct_ctrl &= ~ACB_NORMAL;
+ pw_buf->acct_ctrl |= ACB_WSTRUST;
+ }
+ }
+
+ return pw_buf;
+ }
+
+ DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
+ return NULL;
+}
+
+/************************************************************************
+ Create a new smbpasswd entry - malloced space returned.
+*************************************************************************/
+
+static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
+{
+ int new_entry_length;
+ char *new_entry;
+ char *p;
+
+ new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 +
+ NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
+
+ if((new_entry = (char *)SMB_MALLOC( new_entry_length )) == NULL) {
+ DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n",
+ newpwd->smb_name ));
+ return NULL;
+ }
+
+ slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
+
+ p = new_entry+strlen(new_entry);
+ pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
+ p+=strlen(p);
+ *p = ':';
+ p++;
+
+ pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
+ p+=strlen(p);
+ *p = ':';
+ p++;
+
+ /* Add the account encoding and the last change time. */
+ slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
+ pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
+ (uint32)newpwd->pass_last_set_time);
+
+ return new_entry;
+}
+
+/************************************************************************
+ Routine to add an entry to the smbpasswd file.
+*************************************************************************/
+
+static NTSTATUS add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state,
+ struct smb_passwd *newpwd)
+{
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ struct smb_passwd *pwd = NULL;
+ FILE *fp = NULL;
+ int wr_len;
+ int fd;
+ size_t new_entry_length;
+ char *new_entry;
+ SMB_OFF_T offpos;
+
+ /* Open the smbpassword file - for update. */
+ fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth);
+
+ if (fp == NULL && errno == ENOENT) {
+ /* Try again - create. */
+ fp = startsmbfilepwent(pfile, PWF_CREATE, &smbpasswd_state->pw_file_lock_depth);
+ }
+
+ if (fp == NULL) {
+ DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
+ return map_nt_error_from_unix(errno);
+ }
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
+ if (strequal(newpwd->smb_name, pwd->smb_name)) {
+ DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ return NT_STATUS_USER_EXISTS;
+ }
+ }
+
+ /* Ok - entry doesn't exist. We can add it */
+
+ /* Create a new smb passwd entry and set it to the given password. */
+ /*
+ * The add user write needs to be atomic - so get the fd from
+ * the fp and do a raw write() call.
+ */
+ fd = fileno(fp);
+
+ if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) {
+ NTSTATUS result = map_nt_error_from_unix(errno);
+ DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ return result;
+ }
+
+ if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) {
+ DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_entry_length = strlen(new_entry);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
+ fd, (int)new_entry_length, new_entry));
+#endif
+
+ if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) {
+ NTSTATUS result = map_nt_error_from_unix(errno);
+ DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
+Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
+
+ /* Remove the entry we just wrote. */
+ if(sys_ftruncate(fd, offpos) == -1) {
+ DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
+Error was %s. Password file may be corrupt ! Please examine by hand !\n",
+ newpwd->smb_name, strerror(errno)));
+ }
+
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ free(new_entry);
+ return result;
+ }
+
+ free(new_entry);
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ return NT_STATUS_OK;
+}
+
+/************************************************************************
+ Routine to search the smbpasswd file for an entry matching the username.
+ and then modify its password entry. We can't use the startsmbpwent()/
+ getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
+ in the actual file to decide how much room we have to write data.
+ override = False, normal
+ override = True, override XXXXXXXX'd out password or NO PASS
+************************************************************************/
+
+static bool mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
+{
+ /* Static buffers we will return. */
+ fstring user_name;
+
+ char *status;
+ char linebuf[256];
+ char readbuf[1024];
+ int c;
+ fstring ascii_p16;
+ fstring encode_bits;
+ unsigned char *p = NULL;
+ size_t linebuf_len = 0;
+ FILE *fp;
+ int lockfd;
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ bool found_entry = False;
+ bool got_pass_last_set_time = False;
+
+ SMB_OFF_T pwd_seekpos = 0;
+
+ int i;
+ int wr_len;
+ int fd;
+
+ if (!*pfile) {
+ DEBUG(0, ("No SMB password file set\n"));
+ return False;
+ }
+ DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
+
+ fp = sys_fopen(pfile, "r+");
+
+ if (fp == NULL) {
+ DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
+ return False;
+ }
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
+
+ lockfd = fileno(fp);
+
+ if (!pw_file_lock(lockfd, F_WRLCK, 5, &smbpasswd_state->pw_file_lock_depth)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
+ fclose(fp);
+ return False;
+ }
+
+ /* Make sure it is only rw by the owner */
+ chmod(pfile, 0600);
+
+ /* We have a write lock on the file. */
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+ status = linebuf;
+ while (status && !feof(fp)) {
+ pwd_seekpos = sys_ftell(fp);
+
+ linebuf[0] = '\0';
+
+ status = fgets(linebuf, sizeof(linebuf), fp);
+ if (status == NULL && ferror(fp)) {
+ pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+ linebuf_len = strlen(linebuf);
+ if (linebuf[linebuf_len - 1] != '\n') {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp)) {
+ c = fgetc(fp);
+ if (c == '\n') {
+ break;
+ }
+ }
+ } else {
+ linebuf[linebuf_len - 1] = '\0';
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
+#endif
+
+ if ((linebuf[0] == 0) && feof(fp)) {
+ DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
+ break;
+ }
+
+ /*
+ * The line we have should be of the form :-
+ *
+ * username:uid:[32hex bytes]:....other flags presently
+ * ignored....
+ *
+ * or,
+ *
+ * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
+ *
+ * if Windows NT compatible passwords are also present.
+ */
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0') {
+ DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
+ continue;
+ }
+
+ p = (unsigned char *) strchr_m(linebuf, ':');
+
+ if (p == NULL) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
+ continue;
+ }
+
+ strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+ user_name[PTR_DIFF(p, linebuf)] = '\0';
+ if (strequal(user_name, pwd->smb_name)) {
+ found_entry = True;
+ break;
+ }
+ }
+
+ if (!found_entry) {
+ pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+
+ DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
+ pwd->smb_name));
+ return False;
+ }
+
+ DEBUG(6, ("mod_smbfilepwd_entry: entry exists for user %s\n", pwd->smb_name));
+
+ /* User name matches - get uid and password */
+ p++; /* Go past ':' */
+
+ if (!isdigit(*p)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (uid not number)\n",
+ pwd->smb_name));
+ pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ while (*p && isdigit(*p)) {
+ p++;
+ }
+ if (*p != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no : after uid)\n",
+ pwd->smb_name));
+ pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Now get the password value - this should be 32 hex digits
+ * which are the ascii representations of a 16 byte string.
+ * Get two at a time and put them into the password.
+ */
+ p++;
+
+ /* Record exact password position */
+ pwd_seekpos += PTR_DIFF(p, linebuf);
+
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
+ pwd->smb_name));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return (False);
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
+ pwd->smb_name));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ /* Now check if the NT compatible password is available. */
+ p += 33; /* Move to the first character of the line after the lanman password. */
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (passwd too short)\n",
+ pwd->smb_name));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return (False);
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry for user %s (no terminating :)\n",
+ pwd->smb_name));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Now check if the account info and the password last
+ * change time is available.
+ */
+ p += 33; /* Move to the first character of the line after the NT password. */
+
+ if (*p == '[') {
+ i = 0;
+ encode_bits[i++] = *p++;
+ while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) {
+ encode_bits[i++] = *p++;
+ }
+
+ encode_bits[i++] = ']';
+ encode_bits[i++] = '\0';
+
+ if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
+ /*
+ * We are using a new format, space padded
+ * acct ctrl field. Encode the given acct ctrl
+ * bits into it.
+ */
+ fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ } else {
+ DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format for user %s. \
+This is no longer supported.!\n", pwd->smb_name));
+ DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n"));
+ pw_file_unlock(lockfd, &smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ /* Go past the ']' */
+ if(linebuf_len > PTR_DIFF(p, linebuf)) {
+ p++;
+ }
+
+ if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
+ p++;
+
+ /* We should be pointing at the LCT entry. */
+ if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
+ p += 4;
+ for(i = 0; i < 8; i++) {
+ if(p[i] == '\0' || !isxdigit(p[i])) {
+ break;
+ }
+ }
+ if(i == 8) {
+ /*
+ * p points at 8 characters of hex digits -
+ * read into a time_t as the seconds since
+ * 1970 that the password was last changed.
+ */
+ got_pass_last_set_time = True;
+ } /* i == 8 */
+ } /* *p && StrnCaseCmp() */
+ } /* p == ':' */
+ } /* p == '[' */
+
+ /* Entry is correctly formed. */
+
+ /* Create the 32 byte representation of the new p16 */
+ pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
+
+ /* Add on the NT md4 hash */
+ ascii_p16[32] = ':';
+ wr_len = 66;
+ pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
+ ascii_p16[65] = ':';
+ ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
+
+ /* Add on the account info bits and the time of last password change. */
+ if(got_pass_last_set_time) {
+ slprintf(&ascii_p16[strlen(ascii_p16)],
+ sizeof(ascii_p16)-(strlen(ascii_p16)+1),
+ "%s:LCT-%08X:",
+ encode_bits, (uint32)pwd->pass_last_set_time );
+ wr_len = strlen(ascii_p16);
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("mod_smbfilepwd_entry: "));
+ dump_data(100, (uint8 *)ascii_p16, wr_len);
+#endif
+
+ if(wr_len > sizeof(linebuf)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return (False);
+ }
+
+ /*
+ * Do an atomic write into the file at the position defined by
+ * seekpos.
+ */
+
+ /* The mod user write needs to be atomic - so get the fd from
+ the fp and do a raw write() call.
+ */
+
+ fd = fileno(fp);
+
+ if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
+ DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ /* Sanity check - ensure the areas we are writing are framed by ':' */
+ if (read(fd, linebuf, wr_len+1) != wr_len+1) {
+ DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
+ DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
+ DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ if (write(fd, ascii_p16, wr_len) != wr_len) {
+ DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return False;
+ }
+
+ pw_file_unlock(lockfd,&smbpasswd_state->pw_file_lock_depth);
+ fclose(fp);
+ return True;
+}
+
+/************************************************************************
+ Routine to delete an entry in the smbpasswd file by name.
+*************************************************************************/
+
+static bool del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
+{
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ char *pfile2 = NULL;
+ struct smb_passwd *pwd = NULL;
+ FILE *fp = NULL;
+ FILE *fp_write = NULL;
+ int pfile2_lockdepth = 0;
+
+ pfile2 = talloc_asprintf(talloc_tos(),
+ "%s.%u",
+ pfile, (unsigned)sys_getpid());
+ if (!pfile2) {
+ return false;
+ }
+
+ /*
+ * Open the smbpassword file - for update. It needs to be update
+ * as we need any other processes to wait until we have replaced
+ * it.
+ */
+
+ if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &smbpasswd_state->pw_file_lock_depth)) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+ return False;
+ }
+
+ /*
+ * Create the replacement password file.
+ */
+ if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ return False;
+ }
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
+ char *new_entry;
+ size_t new_entry_length;
+
+ if (strequal(name, pwd->smb_name)) {
+ DEBUG(10, ("del_smbfilepwd_entry: found entry with "
+ "name %s - deleting it.\n", name));
+ continue;
+ }
+
+ /*
+ * We need to copy the entry out into the second file.
+ */
+
+ if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+ unlink(pfile2);
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ endsmbfilepwent(fp_write, &pfile2_lockdepth);
+ return False;
+ }
+
+ new_entry_length = strlen(new_entry);
+
+ if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) {
+ DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+ unlink(pfile2);
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ endsmbfilepwent(fp_write, &pfile2_lockdepth);
+ free(new_entry);
+ return False;
+ }
+
+ free(new_entry);
+ }
+
+ /*
+ * Ensure pfile2 is flushed before rename.
+ */
+
+ if(fflush(fp_write) != 0) {
+ DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ endsmbfilepwent(fp_write,&pfile2_lockdepth);
+ return False;
+ }
+
+ /*
+ * Do an atomic rename - then release the locks.
+ */
+
+ if(rename(pfile2,pfile) != 0) {
+ unlink(pfile2);
+ }
+
+ endsmbfilepwent(fp, &smbpasswd_state->pw_file_lock_depth);
+ endsmbfilepwent(fp_write,&pfile2_lockdepth);
+ return True;
+}
+
+/*********************************************************************
+ Create a smb_passwd struct from a struct samu.
+ We will not allocate any new memory. The smb_passwd struct
+ should only stay around as long as the struct samu does.
+ ********************************************************************/
+
+static bool build_smb_pass (struct smb_passwd *smb_pw, const struct samu *sampass)
+{
+ uint32 rid;
+
+ if (sampass == NULL)
+ return False;
+ ZERO_STRUCTP(smb_pw);
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
+ rid = pdb_get_user_rid(sampass);
+
+ /* If the user specified a RID, make sure its able to be both stored and retreived */
+ if (rid == DOMAIN_USER_RID_GUEST) {
+ struct passwd *passwd = getpwnam_alloc(NULL, lp_guestaccount());
+ if (!passwd) {
+ DEBUG(0, ("Could not find guest account via getpwnam()! (%s)\n", lp_guestaccount()));
+ return False;
+ }
+ smb_pw->smb_userid=passwd->pw_uid;
+ TALLOC_FREE(passwd);
+ } else if (algorithmic_pdb_rid_is_user(rid)) {
+ smb_pw->smb_userid=algorithmic_pdb_user_rid_to_uid(rid);
+ } else {
+ DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
+ return False;
+ }
+ }
+
+ smb_pw->smb_name=(const char*)pdb_get_username(sampass);
+
+ smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
+ smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
+
+ smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
+ smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
+
+ return True;
+}
+
+/*********************************************************************
+ Create a struct samu from a smb_passwd struct
+ ********************************************************************/
+
+static bool build_sam_account(struct smbpasswd_privates *smbpasswd_state,
+ struct samu *sam_pass, const struct smb_passwd *pw_buf)
+{
+ struct passwd *pwfile;
+
+ if ( !sam_pass ) {
+ DEBUG(5,("build_sam_account: struct samu is NULL\n"));
+ return False;
+ }
+
+ /* verify the user account exists */
+
+ if ( !(pwfile = Get_Pwnam_alloc(NULL, pw_buf->smb_name )) ) {
+ DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid "
+ "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
+ return False;
+ }
+
+ if ( !NT_STATUS_IS_OK( samu_set_unix(sam_pass, pwfile )) )
+ return False;
+
+ TALLOC_FREE(pwfile);
+
+ /* set remaining fields */
+
+ if (!pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET))
+ return False;
+ if (!pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET))
+ return False;
+ pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
+ pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+ pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+
+ return True;
+}
+
+/*****************************************************************
+ Functions to be implemented by the new passdb API
+ ****************************************************************/
+
+/****************************************************************
+ Search smbpasswd file by iterating over the entries. Do not
+ call getpwnam() for unix account information until we have found
+ the correct entry
+ ***************************************************************/
+
+static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods,
+ struct samu *sam_acct, const char *username)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *smb_pw;
+ FILE *fp = NULL;
+
+ DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
+
+ /* startsmbfilepwent() is used here as we don't want to lookup
+ the UNIX account in the local system password file until
+ we have a match. */
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL) {
+ DEBUG(0, ("Unable to open passdb database.\n"));
+ return nt_status;
+ }
+
+ while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
+ /* do nothing....another loop */ ;
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+ /* did we locate the username in smbpasswd */
+ if (smb_pw == NULL)
+ return nt_status;
+
+ DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+ if (!sam_acct) {
+ DEBUG(10,("getsampwnam (smbpasswd): struct samu is NULL\n"));
+ return nt_status;
+ }
+
+ /* now build the struct samu */
+ if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
+ return nt_status;
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, struct samu *sam_acct, const DOM_SID *sid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *smb_pw;
+ FILE *fp = NULL;
+ uint32 rid;
+
+ DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n",
+ sid_string_dbg(sid)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* More special case 'guest account' hacks... */
+ if (rid == DOMAIN_USER_RID_GUEST) {
+ const char *guest_account = lp_guestaccount();
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("Guest account not specfied!\n"));
+ return nt_status;
+ }
+ return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
+ }
+
+ /* Open the sam password file - not for update. */
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL) {
+ DEBUG(0, ("Unable to open passdb database.\n"));
+ return nt_status;
+ }
+
+ while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (algorithmic_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
+ /* do nothing */ ;
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+ /* did we locate the username in smbpasswd */
+ if (smb_pw == NULL)
+ return nt_status;
+
+ DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+ if (!sam_acct) {
+ DEBUG(10,("getsampwrid: (smbpasswd) struct samu is NULL\n"));
+ return nt_status;
+ }
+
+ /* now build the struct samu */
+ if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
+ return nt_status;
+
+ /* build_sam_account might change the SID on us, if the name was for the guest account */
+ if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
+ DEBUG(1, ("looking for user with sid %s instead returned %s "
+ "for account %s!?!\n", sid_string_dbg(sid),
+ sid_string_dbg(pdb_get_user_sid(sam_acct)),
+ pdb_get_username(sam_acct)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd smb_pw;
+
+ /* convert the struct samu */
+ if (!build_smb_pass(&smb_pw, sampass)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* add the entry */
+ return add_smbfilepwd_entry(smbpasswd_state, &smb_pw);
+}
+
+static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, struct samu *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd smb_pw;
+
+ /* convert the struct samu */
+ if (!build_smb_pass(&smb_pw, sampass)) {
+ DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* update the entry */
+ if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+ DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, struct samu *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+
+ const char *username = pdb_get_username(sampass);
+
+ if (del_smbfilepwd_entry(smbpasswd_state, username))
+ return NT_STATUS_OK;
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS smbpasswd_rename_sam_account (struct pdb_methods *my_methods,
+ struct samu *old_acct,
+ const char *newname)
+{
+ char *rename_script = NULL;
+ struct samu *new_acct = NULL;
+ bool interim_account = False;
+ TALLOC_CTX *ctx = talloc_tos();
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!*(lp_renameuser_script()))
+ goto done;
+
+ if ( !(new_acct = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !pdb_copy_sam_account( new_acct, old_acct )
+ || !pdb_set_username(new_acct, newname, PDB_CHANGED))
+ {
+ goto done;
+ }
+
+ ret = smbpasswd_add_sam_account(my_methods, new_acct);
+ if (!NT_STATUS_IS_OK(ret))
+ goto done;
+
+ interim_account = True;
+
+ /* rename the posix user */
+ rename_script = talloc_strdup(ctx,
+ lp_renameuser_script());
+ if (!rename_script) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (*rename_script) {
+ int rename_ret;
+
+ rename_script = talloc_string_sub2(ctx,
+ rename_script,
+ "%unew",
+ newname,
+ true,
+ false,
+ true);
+ if (!rename_script) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ rename_script = talloc_string_sub2(ctx,
+ rename_script,
+ "%uold",
+ pdb_get_username(old_acct),
+ true,
+ false,
+ true);
+ if (!rename_script) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rename_ret = smbrun(rename_script, NULL);
+
+ DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret));
+
+ if (rename_ret == 0) {
+ smb_nscd_flush_user_cache();
+ }
+
+ if (rename_ret)
+ goto done;
+ } else {
+ goto done;
+ }
+
+ smbpasswd_delete_sam_account(my_methods, old_acct);
+ interim_account = False;
+
+done:
+ /* cleanup */
+ if (interim_account)
+ smbpasswd_delete_sam_account(my_methods, new_acct);
+
+ if (new_acct)
+ TALLOC_FREE(new_acct);
+
+ return (ret);
+}
+
+static bool smbpasswd_rid_algorithm(struct pdb_methods *methods)
+{
+ return True;
+}
+
+static void free_private_data(void **vp)
+{
+ struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
+
+ endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
+
+ *privates = NULL;
+ /* No need to free any further, as it is talloc()ed */
+}
+
+struct smbpasswd_search_state {
+ uint32_t acct_flags;
+
+ struct samr_displayentry *entries;
+ uint32_t num_entries;
+ ssize_t array_size;
+ uint32_t current;
+};
+
+static void smbpasswd_search_end(struct pdb_search *search)
+{
+ struct smbpasswd_search_state *state = talloc_get_type_abort(
+ search->private_data, struct smbpasswd_search_state);
+ TALLOC_FREE(state);
+}
+
+static bool smbpasswd_search_next_entry(struct pdb_search *search,
+ struct samr_displayentry *entry)
+{
+ struct smbpasswd_search_state *state = talloc_get_type_abort(
+ search->private_data, struct smbpasswd_search_state);
+
+ if (state->current == state->num_entries) {
+ return false;
+ }
+
+ entry->idx = state->entries[state->current].idx;
+ entry->rid = state->entries[state->current].rid;
+ entry->acct_flags = state->entries[state->current].acct_flags;
+
+ entry->account_name = talloc_strdup(
+ search->mem_ctx, state->entries[state->current].account_name);
+ entry->fullname = talloc_strdup(
+ search->mem_ctx, state->entries[state->current].fullname);
+ entry->description = talloc_strdup(
+ search->mem_ctx, state->entries[state->current].description);
+
+ if ((entry->account_name == NULL) || (entry->fullname == NULL)
+ || (entry->description == NULL)) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ return false;
+ }
+
+ state->current += 1;
+ return true;
+}
+
+static bool smbpasswd_search_users(struct pdb_methods *methods,
+ struct pdb_search *search,
+ uint32_t acct_flags)
+{
+ struct smbpasswd_privates *smbpasswd_state =
+ (struct smbpasswd_privates*)methods->private_data;
+
+ struct smbpasswd_search_state *search_state;
+ struct smb_passwd *pwd;
+ FILE *fp;
+
+ search_state = TALLOC_ZERO_P(search->mem_ctx,
+ struct smbpasswd_search_state);
+ if (search_state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return false;
+ }
+ search_state->acct_flags = acct_flags;
+
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ,
+ &smbpasswd_state->pw_file_lock_depth);
+
+ if (fp == NULL) {
+ DEBUG(10, ("Unable to open smbpasswd file.\n"));
+ TALLOC_FREE(search_state);
+ return false;
+ }
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
+ struct samr_displayentry entry;
+ struct samu *user;
+
+ if ((acct_flags != 0)
+ && ((acct_flags & pwd->acct_ctrl) == 0)) {
+ continue;
+ }
+
+ user = samu_new(talloc_tos());
+ if (user == NULL) {
+ DEBUG(0, ("samu_new failed\n"));
+ break;
+ }
+
+ if (!build_sam_account(smbpasswd_state, user, pwd)) {
+ /* Already got debug msgs... */
+ break;
+ }
+
+ ZERO_STRUCT(entry);
+
+ entry.acct_flags = pdb_get_acct_ctrl(user);
+ sid_peek_rid(pdb_get_user_sid(user), &entry.rid);
+ entry.account_name = talloc_strdup(
+ search_state, pdb_get_username(user));
+ entry.fullname = talloc_strdup(
+ search_state, pdb_get_fullname(user));
+ entry.description = talloc_strdup(
+ search_state, pdb_get_acct_desc(user));
+
+ TALLOC_FREE(user);
+
+ if ((entry.account_name == NULL) || (entry.fullname == NULL)
+ || (entry.description == NULL)) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ break;
+ }
+
+ ADD_TO_LARGE_ARRAY(search_state, struct samr_displayentry,
+ entry, &search_state->entries,
+ &search_state->num_entries,
+ &search_state->array_size);
+ }
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+ search->private_data = search_state;
+ search->next_entry = smbpasswd_search_next_entry;
+ search->search_end = smbpasswd_search_end;
+
+ return true;
+}
+
+static NTSTATUS pdb_init_smbpasswd( struct pdb_methods **pdb_method, const char *location )
+{
+ NTSTATUS nt_status;
+ struct smbpasswd_privates *privates;
+
+ if ( !NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method )) ) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "smbpasswd";
+
+ (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
+ (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
+ (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
+ (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
+ (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
+ (*pdb_method)->rename_sam_account = smbpasswd_rename_sam_account;
+ (*pdb_method)->search_users = smbpasswd_search_users;
+
+ (*pdb_method)->rid_algorithm = smbpasswd_rid_algorithm;
+
+ /* Setup private data and free function */
+
+ if ( !(privates = TALLOC_ZERO_P( *pdb_method, struct smbpasswd_privates )) ) {
+ DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Store some config details */
+
+ if (location) {
+ privates->smbpasswd_file = talloc_strdup(*pdb_method, location);
+ } else {
+ privates->smbpasswd_file = talloc_strdup(*pdb_method, lp_smb_passwd_file());
+ }
+
+ if (!privates->smbpasswd_file) {
+ DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*pdb_method)->private_data = privates;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_smbpasswd_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
+}
diff --git a/source3/passdb/pdb_tdb.c b/source3/passdb/pdb_tdb.c
new file mode 100644
index 0000000000..e40f4bbab8
--- /dev/null
+++ b/source3/passdb/pdb_tdb.c
@@ -0,0 +1,1639 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Simo Sorce 2000-2003
+ * Copyright (C) Gerald Carter 2000-2006
+ * Copyright (C) Jeremy Allison 2001
+ * Copyright (C) Andrew Bartlett 2002
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#if 0 /* when made a module use this */
+
+static int tdbsam_debug_level = DBGC_ALL;
+#undef DBGC_CLASS
+#define DBGC_CLASS tdbsam_debug_level
+
+#else
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#endif
+
+#define TDBSAM_VERSION 3 /* Most recent TDBSAM version */
+#define TDBSAM_VERSION_STRING "INFO/version"
+#define PASSDB_FILE_NAME "passdb.tdb"
+#define USERPREFIX "USER_"
+#define USERPREFIX_LEN 5
+#define RIDPREFIX "RID_"
+#define PRIVPREFIX "PRIV_"
+
+/* GLOBAL TDB SAM CONTEXT */
+
+static struct db_context *db_sam;
+static char *tdbsam_filename;
+
+/**********************************************************************
+ Marshall/unmarshall struct samu structs.
+ *********************************************************************/
+
+#define TDB_FORMAT_STRING_V0 "ddddddBBBBBBBBBBBBddBBwdwdBwwd"
+#define TDB_FORMAT_STRING_V1 "dddddddBBBBBBBBBBBBddBBwdwdBwwd"
+#define TDB_FORMAT_STRING_V2 "dddddddBBBBBBBBBBBBddBBBwwdBwwd"
+
+/*********************************************************************
+*********************************************************************/
+
+static bool init_sam_from_buffer_v0(struct samu *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username = NULL;
+ char *domain = NULL;
+ char *nt_username = NULL;
+ char *dir_drive = NULL;
+ char *unknown_str = NULL;
+ char *munged_dial = NULL;
+ char *fullname = NULL;
+ char *homedir = NULL;
+ char *logon_script = NULL;
+ char *profile_path = NULL;
+ char *acct_desc = NULL;
+ char *workstations = NULL;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, remove_me, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours = NULL;
+ uint8 *lm_pw_ptr = NULL, *nt_pw_ptr = NULL;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, hourslen;
+ bool ret = True;
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer_v0: NULL parameters found!\n"));
+ return False;
+ }
+
+/* TDB_FORMAT_STRING_V0 "ddddddBBBBBBBBBBBBddBBwdwdBwwd" */
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING_V0,
+ &logon_time, /* d */
+ &logoff_time, /* d */
+ &kickoff_time, /* d */
+ &pass_last_set_time, /* d */
+ &pass_can_change_time, /* d */
+ &pass_must_change_time, /* d */
+ &username_len, &username, /* B */
+ &domain_len, &domain, /* B */
+ &nt_username_len, &nt_username, /* B */
+ &fullname_len, &fullname, /* B */
+ &homedir_len, &homedir, /* B */
+ &dir_drive_len, &dir_drive, /* B */
+ &logon_script_len, &logon_script, /* B */
+ &profile_path_len, &profile_path, /* B */
+ &acct_desc_len, &acct_desc, /* B */
+ &workstations_len, &workstations, /* B */
+ &unknown_str_len, &unknown_str, /* B */
+ &munged_dial_len, &munged_dial, /* B */
+ &user_rid, /* d */
+ &group_rid, /* d */
+ &lm_pw_len, &lm_pw_ptr, /* B */
+ &nt_pw_len, &nt_pw_ptr, /* B */
+ &acct_ctrl, /* w */
+ &remove_me, /* remove on the next TDB_FORMAT upgarde */ /* d */
+ &logon_divs, /* w */
+ &hours_len, /* d */
+ &hourslen, &hours, /* B */
+ &bad_password_count, /* w */
+ &logon_count, /* w */
+ &unknown_6); /* d */
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else {
+ pdb_set_dir_drive(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_drive()),
+ PDB_DEFAULT);
+ }
+
+ if (logon_script)
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_set_pw_history(sampass, NULL, 0, PDB_SET);
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(lm_pw_ptr);
+ SAFE_FREE(nt_pw_ptr);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool init_sam_from_buffer_v1(struct samu *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username = NULL;
+ char *domain = NULL;
+ char *nt_username = NULL;
+ char *dir_drive = NULL;
+ char *unknown_str = NULL;
+ char *munged_dial = NULL;
+ char *fullname = NULL;
+ char *homedir = NULL;
+ char *logon_script = NULL;
+ char *profile_path = NULL;
+ char *acct_desc = NULL;
+ char *workstations = NULL;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, remove_me, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours = NULL;
+ uint8 *lm_pw_ptr = NULL, *nt_pw_ptr = NULL;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, hourslen;
+ bool ret = True;
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer_v1: NULL parameters found!\n"));
+ return False;
+ }
+
+/* TDB_FORMAT_STRING_V1 "dddddddBBBBBBBBBBBBddBBwdwdBwwd" */
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING_V1,
+ &logon_time, /* d */
+ &logoff_time, /* d */
+ &kickoff_time, /* d */
+ /* Change from V0 is addition of bad_password_time field. */
+ &bad_password_time, /* d */
+ &pass_last_set_time, /* d */
+ &pass_can_change_time, /* d */
+ &pass_must_change_time, /* d */
+ &username_len, &username, /* B */
+ &domain_len, &domain, /* B */
+ &nt_username_len, &nt_username, /* B */
+ &fullname_len, &fullname, /* B */
+ &homedir_len, &homedir, /* B */
+ &dir_drive_len, &dir_drive, /* B */
+ &logon_script_len, &logon_script, /* B */
+ &profile_path_len, &profile_path, /* B */
+ &acct_desc_len, &acct_desc, /* B */
+ &workstations_len, &workstations, /* B */
+ &unknown_str_len, &unknown_str, /* B */
+ &munged_dial_len, &munged_dial, /* B */
+ &user_rid, /* d */
+ &group_rid, /* d */
+ &lm_pw_len, &lm_pw_ptr, /* B */
+ &nt_pw_len, &nt_pw_ptr, /* B */
+ &acct_ctrl, /* w */
+ &remove_me, /* d */
+ &logon_divs, /* w */
+ &hours_len, /* d */
+ &hourslen, &hours, /* B */
+ &bad_password_count, /* w */
+ &logon_count, /* w */
+ &unknown_6); /* d */
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+
+ /* Change from V0 is addition of bad_password_time field. */
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else {
+ pdb_set_dir_drive(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_drive()),
+ PDB_DEFAULT);
+ }
+
+ if (logon_script)
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_set_pw_history(sampass, NULL, 0, PDB_SET);
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(lm_pw_ptr);
+ SAFE_FREE(nt_pw_ptr);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+bool init_sam_from_buffer_v2(struct samu *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username = NULL;
+ char *domain = NULL;
+ char *nt_username = NULL;
+ char *dir_drive = NULL;
+ char *unknown_str = NULL;
+ char *munged_dial = NULL;
+ char *fullname = NULL;
+ char *homedir = NULL;
+ char *logon_script = NULL;
+ char *profile_path = NULL;
+ char *acct_desc = NULL;
+ char *workstations = NULL;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours = NULL;
+ uint8 *lm_pw_ptr = NULL, *nt_pw_ptr = NULL, *nt_pw_hist_ptr = NULL;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, nt_pw_hist_len, hourslen;
+ uint32 pwHistLen = 0;
+ bool ret = True;
+ fstring tmp_string;
+ bool expand_explicit = lp_passdb_expand_explicit();
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer_v2: NULL parameters found!\n"));
+ return False;
+ }
+
+/* TDB_FORMAT_STRING_V2 "dddddddBBBBBBBBBBBBddBBBwwdBwwd" */
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING_V2,
+ &logon_time, /* d */
+ &logoff_time, /* d */
+ &kickoff_time, /* d */
+ &bad_password_time, /* d */
+ &pass_last_set_time, /* d */
+ &pass_can_change_time, /* d */
+ &pass_must_change_time, /* d */
+ &username_len, &username, /* B */
+ &domain_len, &domain, /* B */
+ &nt_username_len, &nt_username, /* B */
+ &fullname_len, &fullname, /* B */
+ &homedir_len, &homedir, /* B */
+ &dir_drive_len, &dir_drive, /* B */
+ &logon_script_len, &logon_script, /* B */
+ &profile_path_len, &profile_path, /* B */
+ &acct_desc_len, &acct_desc, /* B */
+ &workstations_len, &workstations, /* B */
+ &unknown_str_len, &unknown_str, /* B */
+ &munged_dial_len, &munged_dial, /* B */
+ &user_rid, /* d */
+ &group_rid, /* d */
+ &lm_pw_len, &lm_pw_ptr, /* B */
+ &nt_pw_len, &nt_pw_ptr, /* B */
+ /* Change from V1 is addition of password history field. */
+ &nt_pw_hist_len, &nt_pw_hist_ptr, /* B */
+ &acct_ctrl, /* w */
+ /* Also "remove_me" field was removed. */
+ &logon_divs, /* w */
+ &hours_len, /* d */
+ &hourslen, &hours, /* B */
+ &bad_password_count, /* w */
+ &logon_count, /* w */
+ &unknown_6); /* d */
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ fstrcpy( tmp_string, homedir );
+ if (expand_explicit) {
+ standard_sub_basic( username, domain, tmp_string,
+ sizeof(tmp_string) );
+ }
+ pdb_set_homedir(sampass, tmp_string, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else
+ pdb_set_dir_drive(sampass, lp_logon_drive(), PDB_DEFAULT );
+
+ if (logon_script) {
+ fstrcpy( tmp_string, logon_script );
+ if (expand_explicit) {
+ standard_sub_basic( username, domain, tmp_string,
+ sizeof(tmp_string) );
+ }
+ pdb_set_logon_script(sampass, tmp_string, PDB_SET);
+ }
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ fstrcpy( tmp_string, profile_path );
+ if (expand_explicit) {
+ standard_sub_basic( username, domain, tmp_string,
+ sizeof(tmp_string) );
+ }
+ pdb_set_profile_path(sampass, tmp_string, PDB_SET);
+ }
+ else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass, username, domain,
+ lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ /* Change from V1 is addition of password history field. */
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHistLen);
+ if (pwHistLen) {
+ uint8 *pw_hist = SMB_MALLOC_ARRAY(uint8, pwHistLen * PW_HISTORY_ENTRY_LEN);
+ if (!pw_hist) {
+ ret = False;
+ goto done;
+ }
+ memset(pw_hist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
+ if (nt_pw_hist_ptr && nt_pw_hist_len) {
+ int i;
+ SMB_ASSERT((nt_pw_hist_len % PW_HISTORY_ENTRY_LEN) == 0);
+ nt_pw_hist_len /= PW_HISTORY_ENTRY_LEN;
+ for (i = 0; (i < pwHistLen) && (i < nt_pw_hist_len); i++) {
+ memcpy(&pw_hist[i*PW_HISTORY_ENTRY_LEN],
+ &nt_pw_hist_ptr[i*PW_HISTORY_ENTRY_LEN],
+ PW_HISTORY_ENTRY_LEN);
+ }
+ }
+ if (!pdb_set_pw_history(sampass, pw_hist, pwHistLen, PDB_SET)) {
+ SAFE_FREE(pw_hist);
+ ret = False;
+ goto done;
+ }
+ SAFE_FREE(pw_hist);
+ } else {
+ pdb_set_pw_history(sampass, NULL, 0, PDB_SET);
+ }
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(lm_pw_ptr);
+ SAFE_FREE(nt_pw_ptr);
+ SAFE_FREE(nt_pw_hist_ptr);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+
+/**********************************************************************
+ Intialize a struct samu struct from a BYTE buffer of size len
+ *********************************************************************/
+
+static bool init_sam_from_buffer(struct samu *sampass, uint8 *buf, uint32 buflen)
+{
+ return init_sam_from_buffer_v3(sampass, buf, buflen);
+}
+
+/**********************************************************************
+ Intialize a BYTE buffer from a struct samu struct
+ *********************************************************************/
+
+static uint32 init_buffer_from_sam (uint8 **buf, struct samu *sampass, bool size_only)
+{
+ return init_buffer_from_sam_v3(buf, sampass, size_only);
+}
+
+/**********************************************************************
+ Intialize a BYTE buffer from a struct samu struct
+ *********************************************************************/
+
+struct tdbsam_convert_state {
+ int32_t from;
+ bool success;
+};
+
+static int tdbsam_convert_one(struct db_record *rec, void *priv)
+{
+ struct tdbsam_convert_state *state =
+ (struct tdbsam_convert_state *)priv;
+ struct samu *user;
+ TDB_DATA data;
+ NTSTATUS status;
+ bool ret;
+
+ if (rec->key.dsize < USERPREFIX_LEN) {
+ return 0;
+ }
+ if (strncmp((char *)rec->key.dptr, USERPREFIX, USERPREFIX_LEN) != 0) {
+ return 0;
+ }
+
+ user = samu_new(talloc_tos());
+ if (user == NULL) {
+ DEBUG(0,("tdbsam_convert: samu_new() failed!\n"));
+ state->success = false;
+ return -1;
+ }
+
+ DEBUG(10,("tdbsam_convert: Try unpacking a record with (key:%s) "
+ "(version:%d)\n", rec->key.dptr, state->from));
+
+ switch (state->from) {
+ case 0:
+ ret = init_sam_from_buffer_v0(user, (uint8 *)rec->value.dptr,
+ rec->value.dsize);
+ break;
+ case 1:
+ ret = init_sam_from_buffer_v1(user, (uint8 *)rec->value.dptr,
+ rec->value.dsize);
+ break;
+ case 2:
+ ret = init_sam_from_buffer_v2(user, (uint8 *)rec->value.dptr,
+ rec->value.dsize);
+ break;
+ case 3:
+ ret = init_sam_from_buffer_v3(user, (uint8 *)rec->value.dptr,
+ rec->value.dsize);
+ break;
+ default:
+ /* unknown tdbsam version */
+ ret = False;
+ }
+ if (!ret) {
+ DEBUG(0,("tdbsam_convert: Bad struct samu entry returned "
+ "from TDB (key:%s) (version:%d)\n", rec->key.dptr,
+ state->from));
+ TALLOC_FREE(user);
+ state->success = false;
+ return -1;
+ }
+
+ data.dsize = init_buffer_from_sam(&data.dptr, user, false);
+ TALLOC_FREE(user);
+
+ if (data.dsize == -1) {
+ DEBUG(0,("tdbsam_convert: cannot pack the struct samu into "
+ "the new format\n"));
+ state->success = false;
+ return -1;
+ }
+
+ status = rec->store(rec, data, TDB_MODIFY);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Could not store the new record: %s\n",
+ nt_errstr(status)));
+ state->success = false;
+ return -1;
+ }
+
+ return 0;
+}
+
+static bool tdbsam_convert(struct db_context *db, int32 from)
+{
+ struct tdbsam_convert_state state;
+ int ret;
+
+ state.from = from;
+ state.success = true;
+
+ if (db->transaction_start(db) != 0) {
+ DEBUG(0, ("Could not start transaction\n"));
+ return false;
+ }
+
+ ret = db->traverse(db, tdbsam_convert_one, &state);
+ if (ret < 0) {
+ DEBUG(0, ("traverse failed\n"));
+ goto cancel;
+ }
+
+ if (!state.success) {
+ DEBUG(0, ("Converting records failed\n"));
+ goto cancel;
+ }
+
+ if (dbwrap_store_int32(db, TDBSAM_VERSION_STRING,
+ TDBSAM_VERSION) != 0) {
+ DEBUG(0, ("Could not store tdbsam version\n"));
+ goto cancel;
+ }
+
+ if (db->transaction_commit(db) != 0) {
+ DEBUG(0, ("Could not commit transaction\n"));
+ return false;
+ }
+
+ return true;
+
+ cancel:
+ if (db->transaction_cancel(db) != 0) {
+ smb_panic("transaction_cancel failed");
+ }
+
+ return false;
+}
+
+/*********************************************************************
+ Open the tdbsam file based on the absolute path specified.
+ Uses a reference count to allow multiple open calls.
+*********************************************************************/
+
+static bool tdbsam_open( const char *name )
+{
+ int32 version;
+
+ /* check if we are already open */
+
+ if ( db_sam ) {
+ return true;
+ }
+
+ /* Try to open tdb passwd. Create a new one if necessary */
+
+ db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
+ if (db_sam == NULL) {
+ DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd "
+ "[%s]\n", name));
+ return false;
+ }
+
+ /* Check the version */
+ version = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING);
+ if (version == -1) {
+ version = 0; /* Version not found, assume version 0 */
+ }
+
+ /* Compare the version */
+ if (version > TDBSAM_VERSION) {
+ /* Version more recent than the latest known */
+ DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
+ TALLOC_FREE(db_sam);
+ return false;
+ }
+
+ if ( version < TDBSAM_VERSION ) {
+ DEBUG(1, ("tdbsam_open: Converting version %d database to "
+ "version %d.\n", version, TDBSAM_VERSION));
+
+ if ( !tdbsam_convert(db_sam, version) ) {
+ DEBUG(0, ("tdbsam_open: Error when trying to convert "
+ "tdbsam [%s]\n",name));
+ TALLOC_FREE(db_sam);
+ return false;
+ }
+
+ DEBUG(3, ("TDBSAM converted successfully.\n"));
+ }
+
+ DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
+
+ return true;
+}
+
+/******************************************************************
+ Lookup a name in the SAM TDB
+******************************************************************/
+
+static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods,
+ struct samu *user, const char *sname)
+{
+ TDB_DATA data;
+ fstring keystr;
+ fstring name;
+
+ if ( !user ) {
+ DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Data is stored in all lower-case */
+ fstrcpy(name, sname);
+ strlower_m(name);
+
+ /* set search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+
+ /* open the database */
+
+ if ( !tdbsam_open( tdbsam_filename ) ) {
+ DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* get the record */
+
+ data = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr);
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* unpack the buffer */
+
+ if (!init_sam_from_buffer(user, data.dptr, data.dsize)) {
+ DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n"));
+ SAFE_FREE(data.dptr);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* success */
+
+ TALLOC_FREE(data.dptr);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods,
+ struct samu *user, uint32 rid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ TDB_DATA data;
+ fstring keystr;
+ fstring name;
+
+ if ( !user ) {
+ DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n"));
+ return nt_status;
+ }
+
+ /* set search key */
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+
+ /* open the database */
+
+ if ( !tdbsam_open( tdbsam_filename ) ) {
+ DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* get the record */
+
+ data = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr);
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstrcpy(name, (const char *)data.dptr);
+ TALLOC_FREE(data.dptr);
+
+ return tdbsam_getsampwnam (my_methods, user, name);
+}
+
+static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods,
+ struct samu * user, const DOM_SID *sid)
+{
+ uint32 rid;
+
+ if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return tdbsam_getsampwrid(my_methods, user, rid);
+}
+
+static bool tdb_delete_samacct_only( struct samu *sam_pass )
+{
+ fstring keystr;
+ fstring name;
+ NTSTATUS status;
+
+ fstrcpy(name, pdb_get_username(sam_pass));
+ strlower_m(name);
+
+ /* set the search key */
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+
+ /* it's outaa here! 8^) */
+
+ status = dbwrap_delete_bystring(db_sam, keystr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("Error deleting entry from tdb passwd "
+ "database: %s!\n", nt_errstr(status)));
+ return false;
+ }
+
+ return true;
+}
+
+/***************************************************************************
+ Delete a struct samu records for the username and RID key
+****************************************************************************/
+
+static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods,
+ struct samu *sam_pass)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ fstring keystr;
+ uint32 rid;
+ fstring name;
+
+ /* open the database */
+
+ if ( !tdbsam_open( tdbsam_filename ) ) {
+ DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n",
+ tdbsam_filename));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ fstrcpy(name, pdb_get_username(sam_pass));
+ strlower_m(name);
+
+ /* set the search key */
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+
+ rid = pdb_get_user_rid(sam_pass);
+
+ /* it's outaa here! 8^) */
+
+ if (db_sam->transaction_start(db_sam) != 0) {
+ DEBUG(0, ("Could not start transaction\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ nt_status = dbwrap_delete_bystring(db_sam, keystr);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5, ("Error deleting entry from tdb passwd "
+ "database: %s!\n", nt_errstr(nt_status)));
+ goto cancel;
+ }
+
+ /* set the search key */
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+
+ /* it's outaa here! 8^) */
+
+ nt_status = dbwrap_delete_bystring(db_sam, keystr);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5, ("Error deleting entry from tdb rid "
+ "database: %s!\n", nt_errstr(nt_status)));
+ goto cancel;
+ }
+
+ if (db_sam->transaction_commit(db_sam) != 0) {
+ DEBUG(0, ("Could not commit transaction\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+
+ cancel:
+ if (db_sam->transaction_cancel(db_sam) != 0) {
+ smb_panic("transaction_cancel failed");
+ }
+
+ return nt_status;
+}
+
+
+/***************************************************************************
+ Update the TDB SAM account record only
+ Assumes that the tdbsam is already open
+****************************************************************************/
+static bool tdb_update_samacct_only( struct samu* newpwd, int flag )
+{
+ TDB_DATA data;
+ uint8 *buf = NULL;
+ fstring keystr;
+ fstring name;
+ bool ret = false;
+ NTSTATUS status;
+
+ /* copy the struct samu struct into a BYTE buffer for storage */
+
+ if ( (data.dsize=init_buffer_from_sam (&buf, newpwd, False)) == -1 ) {
+ DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n"));
+ goto done;
+ }
+ data.dptr = buf;
+
+ fstrcpy(name, pdb_get_username(newpwd));
+ strlower_m(name);
+
+ DEBUG(5, ("Storing %saccount %s with RID %d\n",
+ flag == TDB_INSERT ? "(new) " : "", name,
+ pdb_get_user_rid(newpwd)));
+
+ /* setup the USER index key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+
+ /* add the account */
+
+ status = dbwrap_store_bystring(db_sam, keystr, data, flag);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to modify passwd TDB: %s!",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ ret = true;
+
+done:
+ /* cleanup */
+ SAFE_FREE(buf);
+ return ret;
+}
+
+/***************************************************************************
+ Update the TDB SAM RID record only
+ Assumes that the tdbsam is already open
+****************************************************************************/
+static bool tdb_update_ridrec_only( struct samu* newpwd, int flag )
+{
+ TDB_DATA data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS status;
+
+ fstrcpy(name, pdb_get_username(newpwd));
+ strlower_m(name);
+
+ /* setup RID data */
+ data = string_term_tdb_data(name);
+
+ /* setup the RID index key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX,
+ pdb_get_user_rid(newpwd));
+
+ /* add the reference */
+ status = dbwrap_store_bystring(db_sam, keystr, data, flag);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to modify TDB passwd: %s!\n",
+ nt_errstr(status)));
+ return false;
+ }
+
+ return true;
+
+}
+
+/***************************************************************************
+ Update the TDB SAM
+****************************************************************************/
+
+static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd,
+ int flag)
+{
+ if (!pdb_get_user_rid(newpwd)) {
+ DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n",
+ pdb_get_username(newpwd)));
+ return False;
+ }
+
+ /* open the database */
+
+ if ( !tdbsam_open( tdbsam_filename ) ) {
+ DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
+ return False;
+ }
+
+ if (db_sam->transaction_start(db_sam) != 0) {
+ DEBUG(0, ("Could not start transaction\n"));
+ return false;
+ }
+
+ if (!tdb_update_samacct_only(newpwd, flag)
+ || !tdb_update_ridrec_only(newpwd, flag)) {
+ goto cancel;
+ }
+
+ if (db_sam->transaction_commit(db_sam) != 0) {
+ DEBUG(0, ("Could not commit transaction\n"));
+ return false;
+ }
+
+ return true;
+
+ cancel:
+ if (db_sam->transaction_cancel(db_sam) != 0) {
+ smb_panic("transaction_cancel failed");
+ }
+ return false;
+}
+
+/***************************************************************************
+ Modifies an existing struct samu
+****************************************************************************/
+
+static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
+{
+ if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Adds an existing struct samu
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
+{
+ if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Renames a struct samu
+ - check for the posix user/rename user script
+ - Add and lock the new user record
+ - rename the posix user
+ - rewrite the rid->username record
+ - delete the old user
+ - unlock the new user record
+***************************************************************************/
+static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods,
+ struct samu *old_acct,
+ const char *newname)
+{
+ struct samu *new_acct = NULL;
+ char *rename_script = NULL;
+ int rename_ret;
+ fstring oldname_lower;
+ fstring newname_lower;
+
+ /* can't do anything without an external script */
+
+ if ( !(new_acct = samu_new( talloc_tos() )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rename_script = talloc_strdup(new_acct, lp_renameuser_script());
+ if (!rename_script) {
+ TALLOC_FREE(new_acct);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!*rename_script) {
+ TALLOC_FREE(new_acct);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ( !pdb_copy_sam_account(new_acct, old_acct)
+ || !pdb_set_username(new_acct, newname, PDB_CHANGED))
+ {
+ TALLOC_FREE(new_acct);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open the database */
+ if ( !tdbsam_open( tdbsam_filename ) ) {
+ DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n",
+ tdbsam_filename));
+ TALLOC_FREE(new_acct);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (db_sam->transaction_start(db_sam) != 0) {
+ DEBUG(0, ("Could not start transaction\n"));
+ TALLOC_FREE(new_acct);
+ return NT_STATUS_ACCESS_DENIED;
+
+ }
+
+ /* add the new account and lock it */
+ if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) {
+ goto cancel;
+ }
+
+ /* Rename the posix user. Follow the semantics of _samr_create_user()
+ so that we lower case the posix name but preserve the case in passdb */
+
+ fstrcpy( oldname_lower, pdb_get_username(old_acct) );
+ strlower_m( oldname_lower );
+
+ fstrcpy( newname_lower, newname );
+ strlower_m( newname_lower );
+
+ rename_script = talloc_string_sub2(new_acct,
+ rename_script,
+ "%unew",
+ newname_lower,
+ true,
+ false,
+ true);
+ if (!rename_script) {
+ goto cancel;
+ }
+ rename_script = talloc_string_sub2(new_acct,
+ rename_script,
+ "%uold",
+ oldname_lower,
+ true,
+ false,
+ true);
+ if (!rename_script) {
+ goto cancel;
+ }
+ rename_ret = smbrun(rename_script, NULL);
+
+ DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n",
+ rename_script, rename_ret));
+
+ if (rename_ret != 0) {
+ goto cancel;
+ }
+
+ smb_nscd_flush_user_cache();
+
+ /* rewrite the rid->username record */
+
+ if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) {
+ goto cancel;
+ }
+
+ tdb_delete_samacct_only( old_acct );
+
+ if (db_sam->transaction_commit(db_sam) != 0) {
+ /*
+ * Ok, we're screwed. We've changed the posix account, but
+ * could not adapt passdb.tdb. Shall we change the posix
+ * account back?
+ */
+ DEBUG(0, ("transaction_commit failed\n"));
+ TALLOC_FREE(new_acct);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ TALLOC_FREE(new_acct );
+ return NT_STATUS_OK;
+
+ cancel:
+ if (db_sam->transaction_cancel(db_sam) != 0) {
+ smb_panic("transaction_cancel failed");
+ }
+
+ TALLOC_FREE(new_acct);
+
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+static bool tdbsam_rid_algorithm(struct pdb_methods *methods)
+{
+ return False;
+}
+
+/*
+ * Historically, winbind was responsible for allocating RIDs, so the next RID
+ * value was stored in winbindd_idmap.tdb. It has been moved to passdb now,
+ * but for compatibility reasons we still keep the the next RID counter in
+ * winbindd_idmap.tdb.
+ */
+
+/*****************************************************************************
+ Initialise idmap database. For now (Dec 2005) this is a copy of the code in
+ sam/idmap_tdb.c. Maybe at a later stage we can remove that capability from
+ winbind completely and store the RID counter in passdb.tdb.
+
+ Dont' fully initialize with the HWM values, if it's new, we're only
+ interested in the RID counter.
+*****************************************************************************/
+
+static bool init_idmap_tdb(TDB_CONTEXT *tdb)
+{
+ int32 version;
+
+ if (tdb_lock_bystring(tdb, "IDMAP_VERSION") != 0) {
+ DEBUG(0, ("Could not lock IDMAP_VERSION\n"));
+ return False;
+ }
+
+ version = tdb_fetch_int32(tdb, "IDMAP_VERSION");
+
+ if (version == -1) {
+ /* No key found, must be a new db */
+ if (tdb_store_int32(tdb, "IDMAP_VERSION",
+ IDMAP_VERSION) != 0) {
+ DEBUG(0, ("Could not store IDMAP_VERSION\n"));
+ tdb_unlock_bystring(tdb, "IDMAP_VERSION");
+ return False;
+ }
+ version = IDMAP_VERSION;
+ }
+
+ if (version != IDMAP_VERSION) {
+ DEBUG(0, ("Expected IDMAP_VERSION=%d, found %d. Please "
+ "start winbind once\n", IDMAP_VERSION, version));
+ tdb_unlock_bystring(tdb, "IDMAP_VERSION");
+ return False;
+ }
+
+ tdb_unlock_bystring(tdb, "IDMAP_VERSION");
+ return True;
+}
+
+static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid)
+{
+ TDB_CONTEXT *tdb;
+ uint32 rid;
+ bool ret = False;
+
+ tdb = tdb_open_log(state_path("winbindd_idmap.tdb"), 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
+
+ if (tdb == NULL) {
+ DEBUG(1, ("Could not open idmap: %s\n", strerror(errno)));
+ goto done;
+ }
+
+ if (!init_idmap_tdb(tdb)) {
+ DEBUG(1, ("Could not init idmap\n"));
+ goto done;
+ }
+
+ rid = BASE_RID; /* Default if not set */
+
+ if (!tdb_change_uint32_atomic(tdb, "RID_COUNTER", &rid, 1)) {
+ DEBUG(3, ("tdbsam_new_rid: Failed to increase RID_COUNTER\n"));
+ goto done;
+ }
+
+ *prid = rid;
+ ret = True;
+
+ done:
+ if ((tdb != NULL) && (tdb_close(tdb) != 0)) {
+ smb_panic("tdb_close(idmap_tdb) failed");
+ }
+
+ return ret;
+}
+
+struct tdbsam_search_state {
+ struct pdb_methods *methods;
+ uint32_t acct_flags;
+
+ uint32_t *rids;
+ uint32_t num_rids;
+ ssize_t array_size;
+ uint32_t current;
+};
+
+static int tdbsam_collect_rids(struct db_record *rec, void *private_data)
+{
+ struct tdbsam_search_state *state = talloc_get_type_abort(
+ private_data, struct tdbsam_search_state);
+ size_t prefixlen = strlen(RIDPREFIX);
+ uint32 rid;
+
+ if ((rec->key.dsize < prefixlen)
+ || (strncmp((char *)rec->key.dptr, RIDPREFIX, prefixlen))) {
+ return 0;
+ }
+
+ rid = strtoul((char *)rec->key.dptr+prefixlen, NULL, 16);
+
+ ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids,
+ &state->array_size);
+
+ return 0;
+}
+
+static void tdbsam_search_end(struct pdb_search *search)
+{
+ struct tdbsam_search_state *state = talloc_get_type_abort(
+ search->private_data, struct tdbsam_search_state);
+ TALLOC_FREE(state);
+}
+
+static bool tdbsam_search_next_entry(struct pdb_search *search,
+ struct samr_displayentry *entry)
+{
+ struct tdbsam_search_state *state = talloc_get_type_abort(
+ search->private_data, struct tdbsam_search_state);
+ struct samu *user = NULL;
+ NTSTATUS status;
+ uint32_t rid;
+
+ again:
+ TALLOC_FREE(user);
+ user = samu_new(talloc_tos());
+ if (user == NULL) {
+ DEBUG(0, ("samu_new failed\n"));
+ return false;
+ }
+
+ if (state->current == state->num_rids) {
+ return false;
+ }
+
+ rid = state->rids[state->current++];
+
+ status = tdbsam_getsampwrid(state->methods, user, rid);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ /*
+ * Someone has deleted that user since we listed the RIDs
+ */
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("tdbsam_getsampwrid failed: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(user);
+ return false;
+ }
+
+ if ((state->acct_flags != 0) &&
+ ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) {
+ goto again;
+ }
+
+ entry->acct_flags = pdb_get_acct_ctrl(user);
+ entry->rid = rid;
+ entry->account_name = talloc_strdup(
+ search->mem_ctx, pdb_get_username(user));
+ entry->fullname = talloc_strdup(
+ search->mem_ctx, pdb_get_fullname(user));
+ entry->description = talloc_strdup(
+ search->mem_ctx, pdb_get_acct_desc(user));
+
+ TALLOC_FREE(user);
+
+ if ((entry->account_name == NULL) || (entry->fullname == NULL)
+ || (entry->description == NULL)) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ return false;
+ }
+
+ return true;
+}
+
+static bool tdbsam_search_users(struct pdb_methods *methods,
+ struct pdb_search *search,
+ uint32 acct_flags)
+{
+ struct tdbsam_search_state *state;
+
+ if (!tdbsam_open(tdbsam_filename)) {
+ DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n",
+ tdbsam_filename));
+ return false;
+ }
+
+ state = TALLOC_ZERO_P(search->mem_ctx, struct tdbsam_search_state);
+ if (state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return false;
+ }
+ state->acct_flags = acct_flags;
+ state->methods = methods;
+
+ db_sam->traverse_read(db_sam, tdbsam_collect_rids, state);
+
+ search->private_data = state;
+ search->next_entry = tdbsam_search_next_entry;
+ search->search_end = tdbsam_search_end;
+
+ return true;
+}
+
+/*********************************************************************
+ Initialize the tdb sam backend. Setup the dispath table of methods,
+ open the tdb, etc...
+*********************************************************************/
+
+static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ char *tdbfile = NULL;
+ const char *pfile = location;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "tdbsam";
+
+ (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
+ (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
+ (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
+ (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
+ (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account;
+ (*pdb_method)->search_users = tdbsam_search_users;
+
+ (*pdb_method)->rid_algorithm = tdbsam_rid_algorithm;
+ (*pdb_method)->new_rid = tdbsam_new_rid;
+
+ /* save the path for later */
+
+ if (!location) {
+ if (asprintf(&tdbfile, "%s/%s", lp_private_dir(),
+ PASSDB_FILE_NAME) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pfile = tdbfile;
+ }
+ tdbsam_filename = SMB_STRDUP(pfile);
+ if (!tdbsam_filename) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SAFE_FREE(tdbfile);
+
+ /* no private data */
+
+ (*pdb_method)->private_data = NULL;
+ (*pdb_method)->free_private_data = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_tdbsam_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);
+}
diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c
new file mode 100644
index 0000000000..4527ae7127
--- /dev/null
+++ b/source3/passdb/secrets.c
@@ -0,0 +1,1366 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Tim Potter 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* the Samba secrets database stores any generated, private information
+ such as the local SID and machine trust password */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static struct db_context *db_ctx;
+
+/* Urrrg. global.... */
+bool global_machine_password_needs_changing;
+
+/**
+ * Use a TDB to store an incrementing random seed.
+ *
+ * Initialised to the current pid, the very first time Samba starts,
+ * and incremented by one each time it is needed.
+ *
+ * @note Not called by systems with a working /dev/urandom.
+ */
+static void get_rand_seed(int *new_seed)
+{
+ *new_seed = sys_getpid();
+ if (db_ctx) {
+ dbwrap_change_int32_atomic(db_ctx, "INFO/random_seed",
+ new_seed, 1);
+ }
+}
+
+/* open up the secrets database */
+bool secrets_init(void)
+{
+ char *fname = NULL;
+ unsigned char dummy;
+
+ if (db_ctx != NULL)
+ return True;
+
+ fname = talloc_asprintf(talloc_tos(), "%s/secrets.tdb",
+ lp_private_dir());
+ if (fname == NULL) {
+ return false;
+ }
+
+ db_ctx = db_open(NULL, fname, 0,
+ TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+ if (db_ctx == NULL) {
+ DEBUG(0,("Failed to open %s\n", fname));
+ TALLOC_FREE(fname);
+ return False;
+ }
+
+ TALLOC_FREE(fname);
+
+ /**
+ * Set a reseed function for the crypto random generator
+ *
+ * This avoids a problem where systems without /dev/urandom
+ * could send the same challenge to multiple clients
+ */
+ set_rand_reseed_callback(get_rand_seed);
+
+ /* Ensure that the reseed is done now, while we are root, etc */
+ generate_random_buffer(&dummy, sizeof(dummy));
+
+ return True;
+}
+
+struct db_context *secrets_db_ctx(void)
+{
+ if (!secrets_init()) {
+ return NULL;
+ }
+
+ return db_ctx;
+}
+
+/*
+ * close secrets.tdb
+ */
+void secrets_shutdown(void)
+{
+ TALLOC_FREE(db_ctx);
+}
+
+/* read a entry from the secrets database - the caller must free the result
+ if size is non-null then the size of the entry is put in there
+ */
+void *secrets_fetch(const char *key, size_t *size)
+{
+ TDB_DATA dbuf;
+ void *result;
+
+ if (!secrets_init()) {
+ return NULL;
+ }
+
+ if (db_ctx->fetch(db_ctx, talloc_tos(), string_tdb_data(key),
+ &dbuf) != 0) {
+ return NULL;
+ }
+
+ result = memdup(dbuf.dptr, dbuf.dsize);
+ if (result == NULL) {
+ return NULL;
+ }
+ TALLOC_FREE(dbuf.dptr);
+
+ if (size) {
+ *size = dbuf.dsize;
+ }
+
+ return result;
+}
+
+/* store a secrets entry
+ */
+bool secrets_store(const char *key, const void *data, size_t size)
+{
+ NTSTATUS status;
+
+ if (!secrets_init()) {
+ return false;
+ }
+
+ status = dbwrap_trans_store(db_ctx, string_tdb_data(key),
+ make_tdb_data((const uint8 *)data, size),
+ TDB_REPLACE);
+ return NT_STATUS_IS_OK(status);
+}
+
+
+/* delete a secets database entry
+ */
+bool secrets_delete(const char *key)
+{
+ NTSTATUS status;
+ if (!secrets_init()) {
+ return false;
+ }
+
+ status = dbwrap_trans_delete(db_ctx, string_tdb_data(key));
+
+ return NT_STATUS_IS_OK(status);
+}
+
+/**
+ * Form a key for fetching the domain sid
+ *
+ * @param domain domain name
+ *
+ * @return keystring
+ **/
+static const char *domain_sid_keystr(const char *domain)
+{
+ char *keystr;
+
+ keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+ SECRETS_DOMAIN_SID, domain);
+ SMB_ASSERT(keystr != NULL);
+ return keystr;
+}
+
+bool secrets_store_domain_sid(const char *domain, const DOM_SID *sid)
+{
+ bool ret;
+
+ ret = secrets_store(domain_sid_keystr(domain), sid, sizeof(DOM_SID));
+
+ /* Force a re-query, in case we modified our domain */
+ if (ret)
+ reset_global_sam_sid();
+ return ret;
+}
+
+bool secrets_fetch_domain_sid(const char *domain, DOM_SID *sid)
+{
+ DOM_SID *dyn_sid;
+ size_t size = 0;
+
+ dyn_sid = (DOM_SID *)secrets_fetch(domain_sid_keystr(domain), &size);
+
+ if (dyn_sid == NULL)
+ return False;
+
+ if (size != sizeof(DOM_SID)) {
+ SAFE_FREE(dyn_sid);
+ return False;
+ }
+
+ *sid = *dyn_sid;
+ SAFE_FREE(dyn_sid);
+ return True;
+}
+
+bool secrets_store_domain_guid(const char *domain, struct GUID *guid)
+{
+ fstring key;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+ strupper_m(key);
+ return secrets_store(key, guid, sizeof(struct GUID));
+}
+
+bool secrets_fetch_domain_guid(const char *domain, struct GUID *guid)
+{
+ struct GUID *dyn_guid;
+ fstring key;
+ size_t size = 0;
+ struct GUID new_guid;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+ strupper_m(key);
+ dyn_guid = (struct GUID *)secrets_fetch(key, &size);
+
+ if (!dyn_guid) {
+ if (lp_server_role() == ROLE_DOMAIN_PDC) {
+ smb_uuid_generate_random(&new_guid);
+ if (!secrets_store_domain_guid(domain, &new_guid))
+ return False;
+ dyn_guid = (struct GUID *)secrets_fetch(key, &size);
+ }
+ if (dyn_guid == NULL) {
+ return False;
+ }
+ }
+
+ if (size != sizeof(struct GUID)) {
+ DEBUG(1,("UUID size %d is wrong!\n", (int)size));
+ SAFE_FREE(dyn_guid);
+ return False;
+ }
+
+ *guid = *dyn_guid;
+ SAFE_FREE(dyn_guid);
+ return True;
+}
+
+/**
+ * Form a key for fetching the machine trust account sec channel type
+ *
+ * @param domain domain name
+ *
+ * @return keystring
+ **/
+static const char *machine_sec_channel_type_keystr(const char *domain)
+{
+ char *keystr;
+
+ keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+ SECRETS_MACHINE_SEC_CHANNEL_TYPE,
+ domain);
+ SMB_ASSERT(keystr != NULL);
+ return keystr;
+}
+
+/**
+ * Form a key for fetching the machine trust account last change time
+ *
+ * @param domain domain name
+ *
+ * @return keystring
+ **/
+static const char *machine_last_change_time_keystr(const char *domain)
+{
+ char *keystr;
+
+ keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+ SECRETS_MACHINE_LAST_CHANGE_TIME,
+ domain);
+ SMB_ASSERT(keystr != NULL);
+ return keystr;
+}
+
+
+/**
+ * Form a key for fetching the machine trust account password
+ *
+ * @param domain domain name
+ *
+ * @return keystring
+ **/
+static const char *machine_password_keystr(const char *domain)
+{
+ char *keystr;
+
+ keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+ SECRETS_MACHINE_PASSWORD, domain);
+ SMB_ASSERT(keystr != NULL);
+ return keystr;
+}
+
+/**
+ * Form a key for fetching the machine trust account password
+ *
+ * @param domain domain name
+ *
+ * @return stored password's key
+ **/
+static const char *trust_keystr(const char *domain)
+{
+ char *keystr;
+
+ keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+ SECRETS_MACHINE_ACCT_PASS, domain);
+ SMB_ASSERT(keystr != NULL);
+ return keystr;
+}
+
+/**
+ * Form a key for fetching a trusted domain password
+ *
+ * @param domain trusted domain name
+ *
+ * @return stored password's key
+ **/
+static char *trustdom_keystr(const char *domain)
+{
+ char *keystr;
+
+ keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+ SECRETS_DOMTRUST_ACCT_PASS,
+ domain);
+ SMB_ASSERT(keystr != NULL);
+ return keystr;
+}
+
+/************************************************************************
+ Lock the trust password entry.
+************************************************************************/
+
+void *secrets_get_trust_account_lock(TALLOC_CTX *mem_ctx, const char *domain)
+{
+ if (!secrets_init()) {
+ return NULL;
+ }
+
+ return db_ctx->fetch_locked(
+ db_ctx, mem_ctx, string_term_tdb_data(trust_keystr(domain)));
+}
+
+/************************************************************************
+ Routine to get the default secure channel type for trust accounts
+************************************************************************/
+
+uint32 get_default_sec_channel(void)
+{
+ if (lp_server_role() == ROLE_DOMAIN_BDC ||
+ lp_server_role() == ROLE_DOMAIN_PDC) {
+ return SEC_CHAN_BDC;
+ } else {
+ return SEC_CHAN_WKSTA;
+ }
+}
+
+/************************************************************************
+ Routine to get the trust account password for a domain.
+ This only tries to get the legacy hashed version of the password.
+ The user of this function must have locked the trust password file using
+ the above secrets_lock_trust_account_password().
+************************************************************************/
+
+bool secrets_fetch_trust_account_password_legacy(const char *domain,
+ uint8 ret_pwd[16],
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ struct machine_acct_pass *pass;
+ size_t size = 0;
+
+ if (!(pass = (struct machine_acct_pass *)secrets_fetch(
+ trust_keystr(domain), &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+ return False;
+ }
+
+ if (size != sizeof(*pass)) {
+ DEBUG(0, ("secrets were of incorrect size!\n"));
+ SAFE_FREE(pass);
+ return False;
+ }
+
+ if (pass_last_set_time) {
+ *pass_last_set_time = pass->mod_time;
+ }
+ memcpy(ret_pwd, pass->hash, 16);
+
+ if (channel) {
+ *channel = get_default_sec_channel();
+ }
+
+ /* Test if machine password has expired and needs to be changed */
+ if (lp_machine_password_timeout()) {
+ if (pass->mod_time > 0 && time(NULL) > (pass->mod_time +
+ (time_t)lp_machine_password_timeout())) {
+ global_machine_password_needs_changing = True;
+ }
+ }
+
+ SAFE_FREE(pass);
+ return True;
+}
+
+/************************************************************************
+ Routine to get the trust account password for a domain.
+ The user of this function must have locked the trust password file using
+ the above secrets_lock_trust_account_password().
+************************************************************************/
+
+bool secrets_fetch_trust_account_password(const char *domain, uint8 ret_pwd[16],
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ char *plaintext;
+
+ plaintext = secrets_fetch_machine_password(domain, pass_last_set_time,
+ channel);
+ if (plaintext) {
+ DEBUG(4,("Using cleartext machine password\n"));
+ E_md4hash(plaintext, ret_pwd);
+ SAFE_FREE(plaintext);
+ return True;
+ }
+
+ return secrets_fetch_trust_account_password_legacy(domain, ret_pwd,
+ pass_last_set_time,
+ channel);
+}
+
+/**
+ * Pack SID passed by pointer
+ *
+ * @param pack_buf pointer to buffer which is to be filled with packed data
+ * @param bufsize size of packing buffer
+ * @param sid pointer to sid to be packed
+ *
+ * @return length of the packed representation of the whole structure
+ **/
+static size_t tdb_sid_pack(uint8 *pack_buf, int bufsize, DOM_SID* sid)
+{
+ int idx;
+ size_t len = 0;
+ uint8 *p = pack_buf;
+ int remaining_space = pack_buf ? bufsize : 0;
+
+ if (!sid) {
+ return -1;
+ }
+
+ len += tdb_pack(p, remaining_space, "bb", sid->sid_rev_num,
+ sid->num_auths);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+
+ for (idx = 0; idx < 6; idx++) {
+ len += tdb_pack(p, remaining_space, "b",
+ sid->id_auth[idx]);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+ }
+
+ for (idx = 0; idx < MAXSUBAUTHS; idx++) {
+ len += tdb_pack(p, remaining_space, "d",
+ sid->sub_auths[idx]);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+ }
+
+ return len;
+}
+
+/**
+ * Unpack SID into a pointer
+ *
+ * @param pack_buf pointer to buffer with packed representation
+ * @param bufsize size of the buffer
+ * @param sid pointer to sid structure to be filled with unpacked data
+ *
+ * @return size of structure unpacked from buffer
+ **/
+static size_t tdb_sid_unpack(uint8 *pack_buf, int bufsize, DOM_SID* sid)
+{
+ int idx, len = 0;
+
+ if (!sid || !pack_buf) return -1;
+
+ len += tdb_unpack(pack_buf + len, bufsize - len, "bb",
+ &sid->sid_rev_num, &sid->num_auths);
+
+ for (idx = 0; idx < 6; idx++) {
+ len += tdb_unpack(pack_buf + len, bufsize - len, "b",
+ &sid->id_auth[idx]);
+ }
+
+ for (idx = 0; idx < MAXSUBAUTHS; idx++) {
+ len += tdb_unpack(pack_buf + len, bufsize - len, "d",
+ &sid->sub_auths[idx]);
+ }
+
+ return len;
+}
+
+/**
+ * Pack TRUSTED_DOM_PASS passed by pointer
+ *
+ * @param pack_buf pointer to buffer which is to be filled with packed data
+ * @param bufsize size of the buffer
+ * @param pass pointer to trusted domain password to be packed
+ *
+ * @return length of the packed representation of the whole structure
+ **/
+static size_t tdb_trusted_dom_pass_pack(uint8 *pack_buf, int bufsize,
+ TRUSTED_DOM_PASS* pass)
+{
+ int idx, len = 0;
+ uint8 *p = pack_buf;
+ int remaining_space = pack_buf ? bufsize : 0;
+
+ if (!pass) {
+ return -1;
+ }
+
+ /* packing unicode domain name and password */
+ len += tdb_pack(p, remaining_space, "d",
+ pass->uni_name_len);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+
+ for (idx = 0; idx < 32; idx++) {
+ len += tdb_pack(p, remaining_space, "w",
+ pass->uni_name[idx]);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+ }
+
+ len += tdb_pack(p, remaining_space, "dPd", pass->pass_len,
+ pass->pass, pass->mod_time);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+
+ /* packing SID structure */
+ len += tdb_sid_pack(p, remaining_space, &pass->domain_sid);
+ if (pack_buf) {
+ p = pack_buf + len;
+ remaining_space = bufsize - len;
+ }
+
+ return len;
+}
+
+
+/**
+ * Unpack TRUSTED_DOM_PASS passed by pointer
+ *
+ * @param pack_buf pointer to buffer with packed representation
+ * @param bufsize size of the buffer
+ * @param pass pointer to trusted domain password to be filled with unpacked data
+ *
+ * @return size of structure unpacked from buffer
+ **/
+static size_t tdb_trusted_dom_pass_unpack(uint8 *pack_buf, int bufsize,
+ TRUSTED_DOM_PASS* pass)
+{
+ int idx, len = 0;
+ char *passp = NULL;
+
+ if (!pack_buf || !pass) return -1;
+
+ /* unpack unicode domain name and plaintext password */
+ len += tdb_unpack(pack_buf, bufsize - len, "d", &pass->uni_name_len);
+
+ for (idx = 0; idx < 32; idx++)
+ len += tdb_unpack(pack_buf + len, bufsize - len, "w",
+ &pass->uni_name[idx]);
+
+ len += tdb_unpack(pack_buf + len, bufsize - len, "dPd",
+ &pass->pass_len, &passp, &pass->mod_time);
+ if (passp) {
+ fstrcpy(pass->pass, passp);
+ }
+ SAFE_FREE(passp);
+
+ /* unpack domain sid */
+ len += tdb_sid_unpack(pack_buf + len, bufsize - len,
+ &pass->domain_sid);
+
+ return len;
+}
+
+/************************************************************************
+ Routine to get account password to trusted domain
+************************************************************************/
+
+bool secrets_fetch_trusted_domain_password(const char *domain, char** pwd,
+ DOM_SID *sid, time_t *pass_last_set_time)
+{
+ struct trusted_dom_pass pass;
+ size_t size = 0;
+
+ /* unpacking structures */
+ uint8 *pass_buf;
+ int pass_len = 0;
+
+ ZERO_STRUCT(pass);
+
+ /* fetching trusted domain password structure */
+ if (!(pass_buf = (uint8 *)secrets_fetch(trustdom_keystr(domain),
+ &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+ return False;
+ }
+
+ /* unpack trusted domain password */
+ pass_len = tdb_trusted_dom_pass_unpack(pass_buf, size, &pass);
+ SAFE_FREE(pass_buf);
+
+ if (pass_len != size) {
+ DEBUG(5, ("Invalid secrets size. Unpacked data doesn't match trusted_dom_pass structure.\n"));
+ return False;
+ }
+
+ /* the trust's password */
+ if (pwd) {
+ *pwd = SMB_STRDUP(pass.pass);
+ if (!*pwd) {
+ return False;
+ }
+ }
+
+ /* last change time */
+ if (pass_last_set_time) *pass_last_set_time = pass.mod_time;
+
+ /* domain sid */
+ if (sid != NULL) sid_copy(sid, &pass.domain_sid);
+
+ return True;
+}
+
+/**
+ * Routine to store the password for trusted domain
+ *
+ * @param domain remote domain name
+ * @param pwd plain text password of trust relationship
+ * @param sid remote domain sid
+ *
+ * @return true if succeeded
+ **/
+
+bool secrets_store_trusted_domain_password(const char* domain, const char* pwd,
+ const DOM_SID *sid)
+{
+ smb_ucs2_t *uni_dom_name;
+ bool ret;
+ size_t converted_size;
+
+ /* packing structures */
+ uint8 *pass_buf = NULL;
+ int pass_len = 0;
+
+ struct trusted_dom_pass pass;
+ ZERO_STRUCT(pass);
+
+ if (!push_ucs2_allocate(&uni_dom_name, domain, &converted_size)) {
+ DEBUG(0, ("Could not convert domain name %s to unicode\n",
+ domain));
+ return False;
+ }
+
+ strncpy_w(pass.uni_name, uni_dom_name, sizeof(pass.uni_name) - 1);
+ pass.uni_name_len = strlen_w(uni_dom_name)+1;
+ SAFE_FREE(uni_dom_name);
+
+ /* last change time */
+ pass.mod_time = time(NULL);
+
+ /* password of the trust */
+ pass.pass_len = strlen(pwd);
+ fstrcpy(pass.pass, pwd);
+
+ /* domain sid */
+ sid_copy(&pass.domain_sid, sid);
+
+ /* Calculate the length. */
+ pass_len = tdb_trusted_dom_pass_pack(NULL, 0, &pass);
+ pass_buf = SMB_MALLOC_ARRAY(uint8, pass_len);
+ if (!pass_buf) {
+ return false;
+ }
+ pass_len = tdb_trusted_dom_pass_pack(pass_buf, pass_len, &pass);
+ ret = secrets_store(trustdom_keystr(domain), (void *)pass_buf,
+ pass_len);
+ SAFE_FREE(pass_buf);
+ return ret;
+}
+
+/************************************************************************
+ Routine to delete the plaintext machine account password
+************************************************************************/
+
+bool secrets_delete_machine_password(const char *domain)
+{
+ return secrets_delete(machine_password_keystr(domain));
+}
+
+/************************************************************************
+ Routine to delete the plaintext machine account password, sec channel type and
+ last change time from secrets database
+************************************************************************/
+
+bool secrets_delete_machine_password_ex(const char *domain)
+{
+ if (!secrets_delete(machine_password_keystr(domain))) {
+ return false;
+ }
+ if (!secrets_delete(machine_sec_channel_type_keystr(domain))) {
+ return false;
+ }
+ return secrets_delete(machine_last_change_time_keystr(domain));
+}
+
+/************************************************************************
+ Routine to delete the domain sid
+************************************************************************/
+
+bool secrets_delete_domain_sid(const char *domain)
+{
+ return secrets_delete(domain_sid_keystr(domain));
+}
+
+/************************************************************************
+ Routine to set the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+
+bool secrets_store_machine_password(const char *pass, const char *domain, uint32 sec_channel)
+{
+ bool ret;
+ uint32 last_change_time;
+ uint32 sec_channel_type;
+
+ ret = secrets_store(machine_password_keystr(domain), pass, strlen(pass)+1);
+ if (!ret)
+ return ret;
+
+ SIVAL(&last_change_time, 0, time(NULL));
+ ret = secrets_store(machine_last_change_time_keystr(domain), &last_change_time, sizeof(last_change_time));
+
+ SIVAL(&sec_channel_type, 0, sec_channel);
+ ret = secrets_store(machine_sec_channel_type_keystr(domain), &sec_channel_type, sizeof(sec_channel_type));
+
+ return ret;
+}
+
+/************************************************************************
+ Routine to fetch the plaintext machine account password for a realm
+ the password is assumed to be a null terminated ascii string.
+************************************************************************/
+
+char *secrets_fetch_machine_password(const char *domain,
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ char *ret;
+ ret = (char *)secrets_fetch(machine_password_keystr(domain), NULL);
+
+ if (pass_last_set_time) {
+ size_t size;
+ uint32 *last_set_time;
+ last_set_time = (unsigned int *)secrets_fetch(machine_last_change_time_keystr(domain), &size);
+ if (last_set_time) {
+ *pass_last_set_time = IVAL(last_set_time,0);
+ SAFE_FREE(last_set_time);
+ } else {
+ *pass_last_set_time = 0;
+ }
+ }
+
+ if (channel) {
+ size_t size;
+ uint32 *channel_type;
+ channel_type = (unsigned int *)secrets_fetch(machine_sec_channel_type_keystr(domain), &size);
+ if (channel_type) {
+ *channel = IVAL(channel_type,0);
+ SAFE_FREE(channel_type);
+ } else {
+ *channel = get_default_sec_channel();
+ }
+ }
+
+ return ret;
+}
+
+/************************************************************************
+ Routine to delete the password for trusted domain
+************************************************************************/
+
+bool trusted_domain_password_delete(const char *domain)
+{
+ return secrets_delete(trustdom_keystr(domain));
+}
+
+bool secrets_store_ldap_pw(const char* dn, char* pw)
+{
+ char *key = NULL;
+ bool ret;
+
+ if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, dn) < 0) {
+ DEBUG(0, ("secrets_store_ldap_pw: asprintf failed!\n"));
+ return False;
+ }
+
+ ret = secrets_store(key, pw, strlen(pw)+1);
+
+ SAFE_FREE(key);
+ return ret;
+}
+
+/*******************************************************************
+ Find the ldap password.
+******************************************************************/
+
+bool fetch_ldap_pw(char **dn, char** pw)
+{
+ char *key = NULL;
+ size_t size = 0;
+
+ *dn = smb_xstrdup(lp_ldap_admin_dn());
+
+ if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) {
+ SAFE_FREE(*dn);
+ DEBUG(0, ("fetch_ldap_pw: asprintf failed!\n"));
+ }
+
+ *pw=(char *)secrets_fetch(key, &size);
+ SAFE_FREE(key);
+
+ if (!size) {
+ /* Upgrade 2.2 style entry */
+ char *p;
+ char* old_style_key = SMB_STRDUP(*dn);
+ char *data;
+ fstring old_style_pw;
+
+ if (!old_style_key) {
+ DEBUG(0, ("fetch_ldap_pw: strdup failed!\n"));
+ return False;
+ }
+
+ for (p=old_style_key; *p; p++)
+ if (*p == ',') *p = '/';
+
+ data=(char *)secrets_fetch(old_style_key, &size);
+ if ((data == NULL) || (size < sizeof(old_style_pw))) {
+ DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n"));
+ SAFE_FREE(old_style_key);
+ SAFE_FREE(*dn);
+ SAFE_FREE(data);
+ return False;
+ }
+
+ size = MIN(size, sizeof(fstring)-1);
+ strncpy(old_style_pw, data, size);
+ old_style_pw[size] = 0;
+
+ SAFE_FREE(data);
+
+ if (!secrets_store_ldap_pw(*dn, old_style_pw)) {
+ DEBUG(0,("fetch_ldap_pw: ldap secret could not be upgraded!\n"));
+ SAFE_FREE(old_style_key);
+ SAFE_FREE(*dn);
+ return False;
+ }
+ if (!secrets_delete(old_style_key)) {
+ DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n"));
+ }
+
+ SAFE_FREE(old_style_key);
+
+ *pw = smb_xstrdup(old_style_pw);
+ }
+
+ return True;
+}
+
+/**
+ * Get trusted domains info from secrets.tdb.
+ **/
+
+struct list_trusted_domains_state {
+ uint32 num_domains;
+ struct trustdom_info **domains;
+};
+
+static int list_trusted_domain(struct db_record *rec, void *private_data)
+{
+ const size_t prefix_len = strlen(SECRETS_DOMTRUST_ACCT_PASS);
+ size_t converted_size, packed_size = 0;
+ struct trusted_dom_pass pass;
+ struct trustdom_info *dom_info;
+
+ struct list_trusted_domains_state *state =
+ (struct list_trusted_domains_state *)private_data;
+
+ if ((rec->key.dsize < prefix_len)
+ || (strncmp((char *)rec->key.dptr, SECRETS_DOMTRUST_ACCT_PASS,
+ prefix_len) != 0)) {
+ return 0;
+ }
+
+ packed_size = tdb_trusted_dom_pass_unpack(
+ rec->value.dptr, rec->value.dsize, &pass);
+
+ if (rec->value.dsize != packed_size) {
+ DEBUG(2, ("Secrets record is invalid!\n"));
+ return 0;
+ }
+
+ if (pass.domain_sid.num_auths != 4) {
+ DEBUG(0, ("SID %s is not a domain sid, has %d "
+ "auths instead of 4\n",
+ sid_string_dbg(&pass.domain_sid),
+ pass.domain_sid.num_auths));
+ return 0;
+ }
+
+ if (!(dom_info = TALLOC_P(state->domains, struct trustdom_info))) {
+ DEBUG(0, ("talloc failed\n"));
+ return 0;
+ }
+
+ if (!pull_ucs2_talloc(dom_info, &dom_info->name, pass.uni_name,
+ &converted_size)) {
+ DEBUG(2, ("pull_ucs2_talloc failed\n"));
+ TALLOC_FREE(dom_info);
+ return 0;
+ }
+
+ sid_copy(&dom_info->sid, &pass.domain_sid);
+
+ ADD_TO_ARRAY(state->domains, struct trustdom_info *, dom_info,
+ &state->domains, &state->num_domains);
+
+ if (state->domains == NULL) {
+ state->num_domains = 0;
+ return -1;
+ }
+ return 0;
+}
+
+NTSTATUS secrets_trusted_domains(TALLOC_CTX *mem_ctx, uint32 *num_domains,
+ struct trustdom_info ***domains)
+{
+ struct list_trusted_domains_state state;
+
+ secrets_init();
+
+ if (db_ctx == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ state.num_domains = 0;
+
+ /*
+ * Make sure that a talloc context for the trustdom_info structs
+ * exists
+ */
+
+ if (!(state.domains = TALLOC_ARRAY(
+ mem_ctx, struct trustdom_info *, 1))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ db_ctx->traverse_read(db_ctx, list_trusted_domain, (void *)&state);
+
+ *num_domains = state.num_domains;
+ *domains = state.domains;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************************
+ Store a complete AFS keyfile into secrets.tdb.
+*******************************************************************************/
+
+bool secrets_store_afs_keyfile(const char *cell, const struct afs_keyfile *keyfile)
+{
+ fstring key;
+
+ if ((cell == NULL) || (keyfile == NULL))
+ return False;
+
+ if (ntohl(keyfile->nkeys) > SECRETS_AFS_MAXKEYS)
+ return False;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell);
+ return secrets_store(key, keyfile, sizeof(struct afs_keyfile));
+}
+
+/*******************************************************************************
+ Fetch the current (highest) AFS key from secrets.tdb
+*******************************************************************************/
+bool secrets_fetch_afs_key(const char *cell, struct afs_key *result)
+{
+ fstring key;
+ struct afs_keyfile *keyfile;
+ size_t size = 0;
+ uint32 i;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell);
+
+ keyfile = (struct afs_keyfile *)secrets_fetch(key, &size);
+
+ if (keyfile == NULL)
+ return False;
+
+ if (size != sizeof(struct afs_keyfile)) {
+ SAFE_FREE(keyfile);
+ return False;
+ }
+
+ i = ntohl(keyfile->nkeys);
+
+ if (i > SECRETS_AFS_MAXKEYS) {
+ SAFE_FREE(keyfile);
+ return False;
+ }
+
+ *result = keyfile->entry[i-1];
+
+ result->kvno = ntohl(result->kvno);
+
+ SAFE_FREE(keyfile);
+
+ return True;
+}
+
+/******************************************************************************
+ When kerberos is not available, choose between anonymous or
+ authenticated connections.
+
+ We need to use an authenticated connection if DCs have the
+ RestrictAnonymous registry entry set > 0, or the "Additional
+ restrictions for anonymous connections" set in the win2k Local
+ Security Policy.
+
+ Caller to free() result in domain, username, password
+*******************************************************************************/
+void secrets_fetch_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("IPC$ connections done anonymously\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
+/******************************************************************************
+ Open or create the schannel session store tdb.
+*******************************************************************************/
+
+static TDB_CONTEXT *open_schannel_session_store(TALLOC_CTX *mem_ctx)
+{
+ TDB_DATA vers;
+ uint32 ver;
+ TDB_CONTEXT *tdb_sc = NULL;
+ char *fname = talloc_asprintf(mem_ctx, "%s/schannel_store.tdb", lp_private_dir());
+
+ if (!fname) {
+ return NULL;
+ }
+
+ tdb_sc = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+ if (!tdb_sc) {
+ DEBUG(0,("open_schannel_session_store: Failed to open %s\n", fname));
+ TALLOC_FREE(fname);
+ return NULL;
+ }
+
+ vers = tdb_fetch_bystring(tdb_sc, "SCHANNEL_STORE_VERSION");
+ if (vers.dptr == NULL) {
+ /* First opener, no version. */
+ SIVAL(&ver,0,1);
+ vers.dptr = (uint8 *)&ver;
+ vers.dsize = 4;
+ tdb_store_bystring(tdb_sc, "SCHANNEL_STORE_VERSION", vers, TDB_REPLACE);
+ vers.dptr = NULL;
+ } else if (vers.dsize == 4) {
+ ver = IVAL(vers.dptr,0);
+ if (ver != 1) {
+ tdb_close(tdb_sc);
+ tdb_sc = NULL;
+ DEBUG(0,("open_schannel_session_store: wrong version number %d in %s\n",
+ (int)ver, fname ));
+ }
+ } else {
+ tdb_close(tdb_sc);
+ tdb_sc = NULL;
+ DEBUG(0,("open_schannel_session_store: wrong version number size %d in %s\n",
+ (int)vers.dsize, fname ));
+ }
+
+ SAFE_FREE(vers.dptr);
+ TALLOC_FREE(fname);
+
+ return tdb_sc;
+}
+
+/******************************************************************************
+ Store the schannel state after an AUTH2 call.
+ Note we must be root here.
+*******************************************************************************/
+
+bool secrets_store_schannel_session_info(TALLOC_CTX *mem_ctx,
+ const char *remote_machine,
+ const struct dcinfo *pdc)
+{
+ TDB_CONTEXT *tdb_sc = NULL;
+ TDB_DATA value;
+ bool ret;
+ char *keystr = talloc_asprintf_strupper_m(mem_ctx, "%s/%s",
+ SECRETS_SCHANNEL_STATE,
+ remote_machine);
+ if (!keystr) {
+ return False;
+ }
+
+ /* Work out how large the record is. */
+ value.dsize = tdb_pack(NULL, 0, "dBBBBBfff",
+ pdc->sequence,
+ 8, pdc->seed_chal.data,
+ 8, pdc->clnt_chal.data,
+ 8, pdc->srv_chal.data,
+ 16, pdc->sess_key,
+ 16, pdc->mach_pw,
+ pdc->mach_acct,
+ pdc->remote_machine,
+ pdc->domain);
+
+ value.dptr = TALLOC_ARRAY(mem_ctx, uint8, value.dsize);
+ if (!value.dptr) {
+ TALLOC_FREE(keystr);
+ return False;
+ }
+
+ value.dsize = tdb_pack(value.dptr, value.dsize, "dBBBBBfff",
+ pdc->sequence,
+ 8, pdc->seed_chal.data,
+ 8, pdc->clnt_chal.data,
+ 8, pdc->srv_chal.data,
+ 16, pdc->sess_key,
+ 16, pdc->mach_pw,
+ pdc->mach_acct,
+ pdc->remote_machine,
+ pdc->domain);
+
+ tdb_sc = open_schannel_session_store(mem_ctx);
+ if (!tdb_sc) {
+ TALLOC_FREE(keystr);
+ TALLOC_FREE(value.dptr);
+ return False;
+ }
+
+ ret = (tdb_store_bystring(tdb_sc, keystr, value, TDB_REPLACE) == 0 ? True : False);
+
+ DEBUG(3,("secrets_store_schannel_session_info: stored schannel info with key %s\n",
+ keystr ));
+
+ tdb_close(tdb_sc);
+ TALLOC_FREE(keystr);
+ TALLOC_FREE(value.dptr);
+ return ret;
+}
+
+/******************************************************************************
+ Restore the schannel state on a client reconnect.
+ Note we must be root here.
+*******************************************************************************/
+
+bool secrets_restore_schannel_session_info(TALLOC_CTX *mem_ctx,
+ const char *remote_machine,
+ struct dcinfo **ppdc)
+{
+ TDB_CONTEXT *tdb_sc = NULL;
+ TDB_DATA value;
+ unsigned char *pseed_chal = NULL;
+ unsigned char *pclnt_chal = NULL;
+ unsigned char *psrv_chal = NULL;
+ unsigned char *psess_key = NULL;
+ unsigned char *pmach_pw = NULL;
+ uint32 l1, l2, l3, l4, l5;
+ int ret;
+ struct dcinfo *pdc = NULL;
+ char *keystr = talloc_asprintf_strupper_m(mem_ctx, "%s/%s",
+ SECRETS_SCHANNEL_STATE,
+ remote_machine);
+
+ *ppdc = NULL;
+
+ if (!keystr) {
+ return False;
+ }
+
+ tdb_sc = open_schannel_session_store(mem_ctx);
+ if (!tdb_sc) {
+ TALLOC_FREE(keystr);
+ return False;
+ }
+
+ value = tdb_fetch_bystring(tdb_sc, keystr);
+ if (!value.dptr) {
+ DEBUG(0,("secrets_restore_schannel_session_info: Failed to find entry with key %s\n",
+ keystr ));
+ tdb_close(tdb_sc);
+ return False;
+ }
+
+ pdc = TALLOC_ZERO_P(mem_ctx, struct dcinfo);
+
+ /* Retrieve the record. */
+ ret = tdb_unpack(value.dptr, value.dsize, "dBBBBBfff",
+ &pdc->sequence,
+ &l1, &pseed_chal,
+ &l2, &pclnt_chal,
+ &l3, &psrv_chal,
+ &l4, &psess_key,
+ &l5, &pmach_pw,
+ &pdc->mach_acct,
+ &pdc->remote_machine,
+ &pdc->domain);
+
+ if (ret == -1 || l1 != 8 || l2 != 8 || l3 != 8 || l4 != 16 || l5 != 16) {
+ /* Bad record - delete it. */
+ tdb_delete_bystring(tdb_sc, keystr);
+ tdb_close(tdb_sc);
+ TALLOC_FREE(keystr);
+ TALLOC_FREE(pdc);
+ SAFE_FREE(pseed_chal);
+ SAFE_FREE(pclnt_chal);
+ SAFE_FREE(psrv_chal);
+ SAFE_FREE(psess_key);
+ SAFE_FREE(pmach_pw);
+ SAFE_FREE(value.dptr);
+ return False;
+ }
+
+ tdb_close(tdb_sc);
+
+ memcpy(pdc->seed_chal.data, pseed_chal, 8);
+ memcpy(pdc->clnt_chal.data, pclnt_chal, 8);
+ memcpy(pdc->srv_chal.data, psrv_chal, 8);
+ memcpy(pdc->sess_key, psess_key, 16);
+ memcpy(pdc->mach_pw, pmach_pw, 16);
+
+ /* We know these are true so didn't bother to store them. */
+ pdc->challenge_sent = True;
+ pdc->authenticated = True;
+
+ DEBUG(3,("secrets_restore_schannel_session_info: restored schannel info key %s\n",
+ keystr ));
+
+ SAFE_FREE(pseed_chal);
+ SAFE_FREE(pclnt_chal);
+ SAFE_FREE(psrv_chal);
+ SAFE_FREE(psess_key);
+ SAFE_FREE(pmach_pw);
+
+ TALLOC_FREE(keystr);
+ SAFE_FREE(value.dptr);
+
+ *ppdc = pdc;
+
+ return True;
+}
+
+bool secrets_store_generic(const char *owner, const char *key, const char *secret)
+{
+ char *tdbkey = NULL;
+ bool ret;
+
+ if (asprintf(&tdbkey, "SECRETS/GENERIC/%s/%s", owner, key) < 0) {
+ DEBUG(0, ("asprintf failed!\n"));
+ return False;
+ }
+
+ ret = secrets_store(tdbkey, secret, strlen(secret)+1);
+
+ SAFE_FREE(tdbkey);
+ return ret;
+}
+
+/*******************************************************************
+ Find the ldap password.
+******************************************************************/
+
+char *secrets_fetch_generic(const char *owner, const char *key)
+{
+ char *secret = NULL;
+ char *tdbkey = NULL;
+
+ if (( ! owner) || ( ! key)) {
+ DEBUG(1, ("Invalid Paramters"));
+ return NULL;
+ }
+
+ if (asprintf(&tdbkey, "SECRETS/GENERIC/%s/%s", owner, key) < 0) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NULL;
+ }
+
+ secret = (char *)secrets_fetch(tdbkey, NULL);
+ SAFE_FREE(tdbkey);
+
+ return secret;
+}
+
diff --git a/source3/passdb/util_builtin.c b/source3/passdb/util_builtin.c
new file mode 100644
index 0000000000..dbddddd25d
--- /dev/null
+++ b/source3/passdb/util_builtin.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+ Translate BUILTIN names to SIDs and vice versa
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+struct rid_name_map {
+ uint32 rid;
+ const char *name;
+};
+
+static const struct rid_name_map builtin_aliases[] = {
+ { BUILTIN_ALIAS_RID_ADMINS, "Administrators" },
+ { BUILTIN_ALIAS_RID_USERS, "Users" },
+ { BUILTIN_ALIAS_RID_GUESTS, "Guests" },
+ { BUILTIN_ALIAS_RID_POWER_USERS, "Power Users" },
+ { BUILTIN_ALIAS_RID_ACCOUNT_OPS, "Account Operators" },
+ { BUILTIN_ALIAS_RID_SYSTEM_OPS, "Server Operators" },
+ { BUILTIN_ALIAS_RID_PRINT_OPS, "Print Operators" },
+ { BUILTIN_ALIAS_RID_BACKUP_OPS, "Backup Operators" },
+ { BUILTIN_ALIAS_RID_REPLICATOR, "Replicator" },
+ { BUILTIN_ALIAS_RID_RAS_SERVERS, "RAS Servers" },
+ { BUILTIN_ALIAS_RID_PRE_2K_ACCESS, "Pre-Windows 2000 Compatible Access" },
+ { 0, NULL}};
+
+/*******************************************************************
+ Look up a rid in the BUILTIN domain
+ ********************************************************************/
+bool lookup_builtin_rid(TALLOC_CTX *mem_ctx, uint32 rid, const char **name)
+{
+ const struct rid_name_map *aliases = builtin_aliases;
+
+ while (aliases->name != NULL) {
+ if (rid == aliases->rid) {
+ *name = talloc_strdup(mem_ctx, aliases->name);
+ return True;
+ }
+ aliases++;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+ Look up a name in the BUILTIN domain
+ ********************************************************************/
+bool lookup_builtin_name(const char *name, uint32 *rid)
+{
+ const struct rid_name_map *aliases = builtin_aliases;
+
+ while (aliases->name != NULL) {
+ if (strequal(name, aliases->name)) {
+ *rid = aliases->rid;
+ return True;
+ }
+ aliases++;
+ }
+
+ return False;
+}
+
+/*****************************************************************
+ Return the name of the BUILTIN domain
+*****************************************************************/
+
+const char *builtin_domain_name(void)
+{
+ return "BUILTIN";
+}
+
+/*****************************************************************
+ Check if the SID is the builtin SID (S-1-5-32).
+*****************************************************************/
+
+bool sid_check_is_builtin(const DOM_SID *sid)
+{
+ return sid_equal(sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Check if the SID is one of the builtin SIDs (S-1-5-32-a).
+*****************************************************************/
+
+bool sid_check_is_in_builtin(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_check_is_builtin(&dom_sid);
+}
+
diff --git a/source3/passdb/util_unixsids.c b/source3/passdb/util_unixsids.c
new file mode 100644
index 0000000000..1b674d02a2
--- /dev/null
+++ b/source3/passdb/util_unixsids.c
@@ -0,0 +1,105 @@
+/*
+ Unix SMB/CIFS implementation.
+ Translate unix-defined names to SIDs and vice versa
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+bool sid_check_is_unix_users(const DOM_SID *sid)
+{
+ return sid_equal(sid, &global_sid_Unix_Users);
+}
+
+bool sid_check_is_in_unix_users(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_check_is_unix_users(&dom_sid);
+}
+
+bool uid_to_unix_users_sid(uid_t uid, DOM_SID *sid)
+{
+ sid_copy(sid, &global_sid_Unix_Users);
+ return sid_append_rid(sid, (uint32_t)uid);
+}
+
+bool gid_to_unix_groups_sid(gid_t gid, DOM_SID *sid)
+{
+ sid_copy(sid, &global_sid_Unix_Groups);
+ return sid_append_rid(sid, (uint32_t)gid);
+}
+
+const char *unix_users_domain_name(void)
+{
+ return "Unix User";
+}
+
+bool lookup_unix_user_name(const char *name, DOM_SID *sid)
+{
+ struct passwd *pwd;
+
+ pwd = getpwnam_alloc(NULL, name);
+ if (pwd == NULL) {
+ return False;
+ }
+
+ sid_copy(sid, &global_sid_Unix_Users);
+ sid_append_rid(sid, (uint32_t)pwd->pw_uid); /* For 64-bit uid's we have enough
+ * space ... */
+ TALLOC_FREE(pwd);
+ return True;
+}
+
+bool sid_check_is_unix_groups(const DOM_SID *sid)
+{
+ return sid_equal(sid, &global_sid_Unix_Groups);
+}
+
+bool sid_check_is_in_unix_groups(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_check_is_unix_groups(&dom_sid);
+}
+
+const char *unix_groups_domain_name(void)
+{
+ return "Unix Group";
+}
+
+bool lookup_unix_group_name(const char *name, DOM_SID *sid)
+{
+ struct group *grp;
+
+ grp = sys_getgrnam(name);
+ if (grp == NULL) {
+ return False;
+ }
+
+ sid_copy(sid, &global_sid_Unix_Groups);
+ sid_append_rid(sid, (uint32_t)grp->gr_gid); /* For 64-bit uid's we have enough
+ * space ... */
+ return True;
+}
diff --git a/source3/passdb/util_wellknown.c b/source3/passdb/util_wellknown.c
new file mode 100644
index 0000000000..3a30ab0a65
--- /dev/null
+++ b/source3/passdb/util_wellknown.c
@@ -0,0 +1,172 @@
+/*
+ Unix SMB/CIFS implementation.
+ Lookup routines for well-known SIDs
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+struct rid_name_map {
+ uint32 rid;
+ const char *name;
+};
+
+struct sid_name_map_info
+{
+ const DOM_SID *sid;
+ const char *name;
+ const struct rid_name_map *known_users;
+};
+
+static const struct rid_name_map everyone_users[] = {
+ { 0, "Everyone" },
+ { 0, NULL}};
+
+static const struct rid_name_map creator_owner_users[] = {
+ { 0, "Creator Owner" },
+ { 1, "Creator Group" },
+ { 0, NULL}};
+
+static const struct rid_name_map nt_authority_users[] = {
+ { 1, "Dialup" },
+ { 2, "Network"},
+ { 3, "Batch"},
+ { 4, "Interactive"},
+ { 6, "Service"},
+ { 7, "AnonymousLogon"},
+ { 8, "Proxy"},
+ { 9, "ServerLogon"},
+ { 10, "Self"},
+ { 11, "Authenticated Users"},
+ { 12, "Restricted"},
+ { 13, "Terminal Server User"},
+ { 14, "Remote Interactive Logon"},
+ { 15, "This Organization"},
+ { 18, "SYSTEM"},
+ { 19, "Local Service"},
+ { 20, "Network Service"},
+ { 0, NULL}};
+
+static struct sid_name_map_info special_domains[] = {
+ { &global_sid_World_Domain, "", everyone_users },
+ { &global_sid_Creator_Owner_Domain, "", creator_owner_users },
+ { &global_sid_NT_Authority, "NT Authority", nt_authority_users },
+ { NULL, NULL, NULL }};
+
+bool sid_check_is_wellknown_domain(const DOM_SID *sid, const char **name)
+{
+ int i;
+
+ for (i=0; special_domains[i].sid != NULL; i++) {
+ if (sid_equal(sid, special_domains[i].sid)) {
+ if (name != NULL) {
+ *name = special_domains[i].name;
+ }
+ return True;
+ }
+ }
+ return False;
+}
+
+bool sid_check_is_in_wellknown_domain(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_check_is_wellknown_domain(&dom_sid, NULL);
+}
+
+/**************************************************************************
+ Looks up a known username from one of the known domains.
+***************************************************************************/
+
+bool lookup_wellknown_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
+ const char **domain, const char **name)
+{
+ int i;
+ DOM_SID dom_sid;
+ uint32 rid;
+ const struct rid_name_map *users = NULL;
+
+ sid_copy(&dom_sid, sid);
+ if (!sid_split_rid(&dom_sid, &rid)) {
+ DEBUG(2, ("Could not split rid from SID\n"));
+ return False;
+ }
+
+ for (i=0; special_domains[i].sid != NULL; i++) {
+ if (sid_equal(&dom_sid, special_domains[i].sid)) {
+ *domain = talloc_strdup(mem_ctx,
+ special_domains[i].name);
+ users = special_domains[i].known_users;
+ break;
+ }
+ }
+
+ if (users == NULL) {
+ DEBUG(10, ("SID %s is no special sid\n", sid_string_dbg(sid)));
+ return False;
+ }
+
+ for (i=0; users[i].name != NULL; i++) {
+ if (rid == users[i].rid) {
+ *name = talloc_strdup(mem_ctx, users[i].name);
+ return True;
+ }
+ }
+
+ DEBUG(10, ("RID of special SID %s not found\n", sid_string_dbg(sid)));
+
+ return False;
+}
+
+/**************************************************************************
+ Try and map a name to one of the well known SIDs.
+***************************************************************************/
+
+bool lookup_wellknown_name(TALLOC_CTX *mem_ctx, const char *name,
+ DOM_SID *sid, const char **domain)
+{
+ int i, j;
+
+ DEBUG(10,("map_name_to_wellknown_sid: looking up %s\n", name));
+
+ for (i=0; special_domains[i].sid != NULL; i++) {
+ const struct rid_name_map *users =
+ special_domains[i].known_users;
+
+ if (users == NULL)
+ continue;
+
+ for (j=0; users[j].name != NULL; j++) {
+ if ( strequal(users[j].name, name) ) {
+ sid_copy(sid, special_domains[i].sid);
+ sid_append_rid(sid, users[j].rid);
+ *domain = talloc_strdup(
+ mem_ctx, special_domains[i].name);
+ return True;
+ }
+ }
+ }
+
+ return False;
+}