diff options
author | Gerald Carter <jerry@samba.org> | 2003-07-16 05:34:56 +0000 |
---|---|---|
committer | Gerald Carter <jerry@samba.org> | 2003-07-16 05:34:56 +0000 |
commit | 4a090ba06a54f5da179ac02bb307cc03d08831bf (patch) | |
tree | ed652ef36be7f16682c358816334f969a22f1c27 /source3/sam | |
parent | 95fe82670032a3a43571b46d7bbf2c26bc8cdcd9 (diff) | |
download | samba-4a090ba06a54f5da179ac02bb307cc03d08831bf.tar.gz samba-4a090ba06a54f5da179ac02bb307cc03d08831bf.tar.bz2 samba-4a090ba06a54f5da179ac02bb307cc03d08831bf.zip |
trying to get HEAD building again. If you want the code
prior to this merge, checkout HEAD_PRE_3_0_0_BETA_3_MERGE
(This used to be commit adb98e7b7cd0f025b52c570e4034eebf4047b1ad)
Diffstat (limited to 'source3/sam')
-rw-r--r-- | source3/sam/idmap.c | 308 | ||||
-rw-r--r-- | source3/sam/idmap_ldap.c | 1363 | ||||
-rw-r--r-- | source3/sam/idmap_tdb.c | 398 | ||||
-rw-r--r-- | source3/sam/idmap_util.c | 333 |
4 files changed, 1410 insertions, 992 deletions
diff --git a/source3/sam/idmap.c b/source3/sam/idmap.c index 9695e7b764..7a8f270e15 100644 --- a/source3/sam/idmap.c +++ b/source3/sam/idmap.c @@ -4,6 +4,7 @@ Copyright (C) Tim Potter 2000 Copyright (C) Anthony Liguori <aliguor@us.ibm.com> 2003 Copyright (C) Simo Sorce 2003 + Copyright (C) Jeremy Allison 2003. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,189 +25,292 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_IDMAP -static struct { - +struct idmap_function_entry { const char *name; - /* Function to create a member of the idmap_methods list */ - NTSTATUS (*reg_meth)(struct idmap_methods **methods); struct idmap_methods *methods; - -} remote_idmap_functions[] = { - { NULL, NULL, NULL } + struct idmap_function_entry *prev,*next; }; -static struct idmap_methods *local_map; -static struct idmap_methods *remote_map; - -static void lazy_initialize_idmap(void) -{ - static BOOL initialized = False; - if (initialized) return; - idmap_init(); - initialized = True; -} +static struct idmap_function_entry *backends = NULL; +static struct idmap_methods *cache_map; +static struct idmap_methods *remote_map; +/********************************************************************** + Get idmap methods. Don't allow tdb to be a remote method. +**********************************************************************/ -static struct idmap_methods *get_methods(const char *name) +static struct idmap_methods *get_methods(const char *name, BOOL cache_method) { - int i = 0; - struct idmap_methods *ret = NULL; + struct idmap_function_entry *entry = backends; - while (remote_idmap_functions[i].name && strcmp(remote_idmap_functions[i].name, name)) { - i++; + for(entry = backends; entry; entry = entry->next) { + if (!cache_method && strequal(entry->name, "tdb")) + continue; /* tdb is only cache method. */ + if (strequal(entry->name, name)) + return entry->methods; } - if (remote_idmap_functions[i].name) { + return NULL; +} - if (!remote_idmap_functions[i].methods) { - remote_idmap_functions[i].reg_meth(&remote_idmap_functions[i].methods); - } +/********************************************************************** + Allow a module to register itself as a method. +**********************************************************************/ - ret = remote_idmap_functions[i].methods; +NTSTATUS smb_register_idmap(int version, const char *name, struct idmap_methods *methods) +{ + struct idmap_function_entry *entry; + + if ((version != SMB_IDMAP_INTERFACE_VERSION)) { + DEBUG(0, ("smb_register_idmap: Failed to register idmap module.\n" + "The module was compiled against SMB_IDMAP_INTERFACE_VERSION %d,\n" + "current SMB_IDMAP_INTERFACE_VERSION is %d.\n" + "Please recompile against the current version of samba!\n", + version, SMB_IDMAP_INTERFACE_VERSION)); + return NT_STATUS_OBJECT_TYPE_MISMATCH; + } + + if (!name || !name[0] || !methods) { + DEBUG(0,("smb_register_idmap: called with NULL pointer or empty name!\n")); + return NT_STATUS_INVALID_PARAMETER; } - return ret; + if (get_methods(name, False)) { + DEBUG(0,("smb_register_idmap: idmap module %s already registered!\n", name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + entry = smb_xmalloc(sizeof(struct idmap_function_entry)); + entry->name = smb_xstrdup(name); + entry->methods = methods; + + DLIST_ADD(backends, entry); + DEBUG(5, ("smb_register_idmap: Successfully added idmap backend '%s'\n", name)); + return NT_STATUS_OK; } -/* Initialize backend */ -BOOL idmap_init(void) +/********************************************************************** + Initialise idmap cache and a remote backend (if configured). +**********************************************************************/ + +BOOL idmap_init(const char *remote_backend) { - const char *remote_backend = lp_idmap_backend(); + if (!backends) + static_init_idmap; + + if (!cache_map) { + cache_map = get_methods("tdb", True); - if (!local_map) { - idmap_reg_tdb(&local_map); - if (NT_STATUS_IS_ERR(local_map->init())) { - DEBUG(0, ("idmap_init: could not load or create local backend!\n")); + if (!cache_map) { + DEBUG(0, ("idmap_init: could not find tdb cache backend!\n")); + return False; + } + + if (!NT_STATUS_IS_OK(cache_map->init( NULL ))) { + DEBUG(0, ("idmap_init: could not initialise tdb cache backend!\n")); return False; } } if (!remote_map && remote_backend && *remote_backend != 0) { - DEBUG(3, ("idmap_init: using '%s' as remote backend\n", remote_backend)); + char *rem_backend = smb_xstrdup(remote_backend); + fstring params = ""; + char *pparams; + + /* get any mode parameters passed in */ + + if ( (pparams = strchr( rem_backend, ':' )) != NULL ) { + *pparams = '\0'; + pparams++; + fstrcpy( params, pparams ); + } + + DEBUG(3, ("idmap_init: using '%s' as remote backend\n", rem_backend)); - remote_map = get_methods(remote_backend); - if (!remote_map) { - DEBUG(0, ("idmap_init: could not load remote backend '%s'\n", remote_backend)); + if((remote_map = get_methods(rem_backend, False)) || + (NT_STATUS_IS_OK(smb_probe_module("idmap", rem_backend)) && + (remote_map = get_methods(rem_backend, False)))) { + remote_map->init(params); + } else { + DEBUG(0, ("idmap_init: could not load remote backend '%s'\n", rem_backend)); + SAFE_FREE(rem_backend); return False; } - remote_map->init(); + SAFE_FREE(rem_backend); } return True; } +/************************************************************************** + This is a rare operation, designed to allow an explicit mapping to be + set up for a sid to a POSIX id. +**************************************************************************/ + NTSTATUS idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type) { - NTSTATUS ret; - - lazy_initialize_idmap(); - - ret = local_map->set_mapping(sid, id, id_type); - if (NT_STATUS_IS_ERR(ret)) { - DEBUG (0, ("idmap_set_mapping: Error, unable to modify local cache!\n")); - DEBUGADD(0, ("Error: %s", nt_errstr(ret))); - return ret; + struct idmap_methods *map = remote_map; + DOM_SID tmp_sid; + + DEBUG(10, ("idmap_set_mapping: Set %s to %s %d\n", + sid_string_static(sid), + ((id_type & ID_TYPEMASK) == ID_USERID) ? "UID" : "GID", + ((id_type & ID_TYPEMASK) == ID_USERID) ? id.uid : id.gid)); + + if ( (NT_STATUS_IS_OK(cache_map-> + get_sid_from_id(&tmp_sid, id, + id_type | ID_QUERY_ONLY))) && + sid_equal(sid, &tmp_sid) ) { + /* Nothing to do, we already have that mapping */ + DEBUG(10, ("idmap_set_mapping: Mapping already there\n")); + return NT_STATUS_OK; } - /* Being able to update the remote cache is seldomly right. - Generally this is a forbidden operation. */ - if (!(id_type & ID_CACHE) && (remote_map != NULL)) { - remote_map->set_mapping(sid, id, id_type); - if (NT_STATUS_IS_ERR(ret)) { - DEBUG (0, ("idmap_set_mapping: Error, unable to modify remote cache!\n")); - DEBUGADD(0, ("Error: %s", nt_errstr(ret))); - } + if (map == NULL) { + /* Ok, we don't have a authoritative remote + mapping. So update our local cache only. */ + map = cache_map; } - return ret; + return map->set_mapping(sid, id, id_type); } -/* Get ID from SID */ +/************************************************************************** + Get ID from SID. This can create a mapping for a SID to a POSIX id. +**************************************************************************/ + NTSTATUS idmap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) { NTSTATUS ret; int loc_type; - lazy_initialize_idmap(); - loc_type = *id_type; - if (remote_map) { /* We have a central remote idmap */ - loc_type |= ID_NOMAP; + + if (remote_map) { + /* We have a central remote idmap so only look in + cache, don't allocate */ + loc_type |= ID_QUERY_ONLY; } - ret = local_map->get_id_from_sid(id, &loc_type, sid); - if (NT_STATUS_IS_ERR(ret)) { - if (remote_map) { - ret = remote_map->get_id_from_sid(id, id_type, sid); - if (NT_STATUS_IS_ERR(ret)) { - DEBUG(3, ("idmap_get_id_from_sid: error fetching id!\n")); - return ret; - } else { - loc_type |= ID_CACHE; - idmap_set_mapping(sid, *id, loc_type); - } - } - } else { + + ret = cache_map->get_id_from_sid(id, &loc_type, sid); + + if (NT_STATUS_IS_OK(ret)) { *id_type = loc_type & ID_TYPEMASK; + return NT_STATUS_OK; + } + + if (remote_map == NULL) { + return ret; + } + + /* Ok, the mapping was not in the cache, give the remote map a + second try. */ + + ret = remote_map->get_id_from_sid(id, id_type, sid); + + if (NT_STATUS_IS_OK(ret)) { + /* The remote backend gave us a valid mapping, cache it. */ + ret = cache_map->set_mapping(sid, *id, *id_type); } return ret; } -/* Get SID from ID */ +/************************************************************************** + Get SID from ID. This must have been created before. +**************************************************************************/ + NTSTATUS idmap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type) { NTSTATUS ret; int loc_type; - lazy_initialize_idmap(); - loc_type = id_type; if (remote_map) { - loc_type = id_type | ID_NOMAP; + loc_type = id_type | ID_QUERY_ONLY; } - ret = local_map->get_sid_from_id(sid, id, loc_type); - if (NT_STATUS_IS_ERR(ret)) { - if (remote_map) { - ret = remote_map->get_sid_from_id(sid, id, id_type); - if (NT_STATUS_IS_ERR(ret)) { - DEBUG(3, ("idmap_get_sid_from_id: unable to fetch sid!\n")); - return ret; - } else { - loc_type |= ID_CACHE; - idmap_set_mapping(sid, id, loc_type); - } - } + + ret = cache_map->get_sid_from_id(sid, id, loc_type); + + if (NT_STATUS_IS_OK(ret)) + return ret; + + if (remote_map == NULL) + return ret; + + /* We have a second chance, ask our authoritative backend */ + + ret = remote_map->get_sid_from_id(sid, id, id_type); + + if (NT_STATUS_IS_OK(ret)) { + /* The remote backend gave us a valid mapping, cache it. */ + ret = cache_map->set_mapping(sid, id, id_type); } return ret; } -/* Close backend */ +/************************************************************************** + Alloocate a new UNIX uid/gid +**************************************************************************/ + +NTSTATUS idmap_allocate_id(unid_t *id, int id_type) +{ + /* we have to allocate from the authoritative backend */ + + if ( remote_map ) + return remote_map->allocate_id( id, id_type ); + + return cache_map->allocate_id( id, id_type ); +} + +/************************************************************************** + Alloocate a new RID +**************************************************************************/ + +NTSTATUS idmap_allocate_rid(uint32 *rid, int type) +{ + /* we have to allocate from the authoritative backend */ + + if ( remote_map ) + return remote_map->allocate_rid( rid, type ); + + return cache_map->allocate_rid( rid, type ); +} + +/************************************************************************** + Shutdown maps. +**************************************************************************/ + NTSTATUS idmap_close(void) { NTSTATUS ret; - ret = local_map->close(); - if (NT_STATUS_IS_ERR(ret)) { - DEBUG(3, ("idmap_close: failed to close local cache!\n")); + ret = cache_map->close(); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(3, ("idmap_close: failed to close local tdb cache!\n")); } + cache_map = NULL; if (remote_map) { ret = remote_map->close(); - if (NT_STATUS_IS_ERR(ret)) { + if (!NT_STATUS_IS_OK(ret)) { DEBUG(3, ("idmap_close: failed to close remote idmap repository!\n")); } + remote_map = NULL; } return ret; } -/* Dump backend status */ +/************************************************************************** + Dump backend status. +**************************************************************************/ + void idmap_status(void) { - lazy_initialize_idmap(); - - local_map->status(); - if (remote_map) remote_map->status(); + cache_map->status(); + if (remote_map) + remote_map->status(); } diff --git a/source3/sam/idmap_ldap.c b/source3/sam/idmap_ldap.c index 33cf5fb030..9a1ee039d0 100644 --- a/source3/sam/idmap_ldap.c +++ b/source3/sam/idmap_ldap.c @@ -3,9 +3,10 @@ idmap LDAP backend - Copyright (C) Tim Potter 2000 - Copyright (C) Anthony Liguori 2003 - Copyright (C) Simo Sorce 2003 + Copyright (C) Tim Potter 2000 + Copyright (C) Anthony Liguori 2003 + Copyright (C) Simo Sorce 2003 + Copyright (C) Gerald Carter 2003 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,781 +32,912 @@ #include <lber.h> #include <ldap.h> +#include "smbldap.h" + +#define IDMAP_GROUP_SUFFIX "ou=idmap group" +#define IDMAP_USER_SUFFIX "ou=idmap people" + + struct ldap_idmap_state { - LDAP *ldap_struct; - time_t last_ping; - const char *uri; - char *bind_dn; - char *bind_secret; - unsigned int num_failures; - struct ldap_idmap_state *prev, *next; + struct smbldap_state *smbldap_state; + TALLOC_CTX *mem_ctx; + + uint32 low_allocated_user_rid; + uint32 high_allocated_user_rid; + uint32 low_allocated_group_rid; + uint32 high_allocated_group_rid; + }; -#define LDAP_IDMAP_DONT_PING_TIME 10 /* ping only all 10 seconds */ #define LDAP_MAX_ALLOC_ID 128 /* number tries while allocating new id */ static struct ldap_idmap_state ldap_state; -static int ldap_idmap_connect_system(struct ldap_idmap_state *state); static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type); +static NTSTATUS ldap_set_mapping_internals(const DOM_SID *sid, unid_t id, int id_type, + const char *ldap_dn, LDAPMessage *entry); static NTSTATUS ldap_idmap_close(void); -/******************************************************************* - find the ldap password -******************************************************************/ -static BOOL fetch_ldapsam_pw(char **dn, char** pw) -{ - char *key = NULL; - size_t size; - - *dn = smb_xstrdup(lp_ldap_admin_dn()); - - if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) { - SAFE_FREE(*dn); - DEBUG(0, ("fetch_ldapsam_pw: asprintf failed!\n")); - } - - *pw=secrets_fetch(key, &size); - SAFE_FREE(key); - - if (!size) { - /* Upgrade 2.2 style entry */ - char *p; - char* old_style_key = strdup(*dn); - char *data; - fstring old_style_pw; - - if (!old_style_key) { - DEBUG(0, ("fetch_ldapsam_pw: strdup failed!\n")); - return False; - } - - for (p=old_style_key; *p; p++) - if (*p == ',') *p = '/'; - - data=secrets_fetch(old_style_key, &size); - if (!size && size < sizeof(old_style_pw)) { - DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n")); - SAFE_FREE(old_style_key); - SAFE_FREE(*dn); - return False; - } +/********************************************************************** + Even if the sambaDomain attribute in LDAP tells us that this RID is + safe to use, always check before use. +*********************************************************************/ - strncpy(old_style_pw, data, size); - old_style_pw[size] = 0; +static BOOL sid_in_use(struct ldap_idmap_state *state, + const DOM_SID *sid, int *error) +{ + fstring filter; + fstring sid_string; + LDAPMessage *result = NULL; + int count; + int rc; + char *sid_attr[] = {LDAP_ATTRIBUTE_SID, NULL}; - SAFE_FREE(data); + slprintf(filter, sizeof(filter)-1, "(%s=%s)", LDAP_ATTRIBUTE_SID, sid_to_string(sid_string, sid)); - 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")); - } + rc = smbldap_search_suffix(state->smbldap_state, + filter, sid_attr, &result); - SAFE_FREE(old_style_key); + if (rc != LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(2, ("Failed to check if sid %s is alredy in use: %s\n", + sid_string, ld_error)); + SAFE_FREE(ld_error); - *pw = smb_xstrdup(old_style_pw); + *error = rc; + return True; } - return True; + if ((count = ldap_count_entries(state->smbldap_state->ldap_struct, result)) > 0) { + DEBUG(3, ("Sid %s already in use - trying next RID\n", + sid_string)); + ldap_msgfree(result); + return True; + } + + ldap_msgfree(result); + + /* good, sid is not in use */ + return False; } -/******************************************************************* - open a connection to the ldap server. -******************************************************************/ -static int ldap_idmap_open_connection(struct ldap_idmap_state *state) +/********************************************************************** + Set the new nextRid attribute, and return one we can use. + + This also checks that this RID is actually free - in case the admin + manually stole it :-). +*********************************************************************/ +static NTSTATUS ldap_next_rid(struct ldap_idmap_state *state, uint32 *rid, + int rid_type) { - int rc = LDAP_SUCCESS; - int version; - BOOL ldap_v3 = False; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + int rc; + LDAPMessage *domain_result = NULL; + LDAPMessage *entry = NULL; + char *dn; + LDAPMod **mods = NULL; + fstring old_rid_string; + fstring next_rid_string; + fstring algorithmic_rid_base_string; + uint32 next_rid; + uint32 alg_rid_base; + int attempts = 0; + char *ld_error = NULL; -#ifdef HAVE_LDAP_INITIALIZE - DEBUG(10, ("ldap_idmap_open_connection: %s\n", state->uri)); - - if ((rc = ldap_initialize(&state->ldap_struct, state->uri)) - != LDAP_SUCCESS) { - DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc))); - return rc; - } -#else - /* Parse the string manually */ + while (attempts < 10) { - int port = 0; - fstring protocol; - fstring host; - const char *p = state->uri; - SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254); - - /* skip leading "URL:" (if any) */ - if ( strncasecmp( p, "URL:", 4 ) == 0 ) { - p += 4; + if (!NT_STATUS_IS_OK(ret = smbldap_search_domain_info(state->smbldap_state, + &domain_result, get_global_sam_name(), True))) + { + return ret; } - - sscanf(p, "%10[^:]://%254s[^:]:%d", protocol, host, &port); - - if (port == 0) { - if (strequal(protocol, "ldap")) { - port = LDAP_PORT; - } else if (strequal(protocol, "ldaps")) { - port = LDAPS_PORT; - } else { - DEBUG(0, ("unrecognised protocol (%s)!\n", - protocol)); - } + + entry = ldap_first_entry(state->smbldap_state->ldap_struct, domain_result); + if (!entry) { + DEBUG(0, ("Could not get domain info entry\n")); + ldap_msgfree(domain_result); + return ret; } - - if ((state->ldap_struct = ldap_init(host, port)) == NULL) { - DEBUG(0, ("ldap_init failed !\n")); - return LDAP_OPERATIONS_ERROR; + + if ((dn = ldap_get_dn(state->smbldap_state->ldap_struct, entry)) == NULL) { + DEBUG(0, ("Could not get domain info DN\n")); + ldap_msgfree(domain_result); + return ret; } + + /* yes, we keep 3 seperate counters, one for rids between 1000 (BASE_RID) and + algorithmic_rid_base. The other two are to avoid stomping on the + different sets of algorithmic RIDs */ - if (strequal(protocol, "ldaps")) { -#ifdef LDAP_OPT_X_TLS - int tls = LDAP_OPT_X_TLS_HARD; - if (ldap_set_option (state->ldap_struct, - LDAP_OPT_X_TLS, &tls) != - LDAP_SUCCESS) - { - DEBUG(0, ("Failed to setup a TLS session\n")); - } + if (smbldap_get_single_attribute(state->smbldap_state->ldap_struct, entry, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE), + algorithmic_rid_base_string)) + { - DEBUG(3,("LDAPS option set...!\n")); -#else - DEBUG(0,("ldap_idmap_open_connection: Secure " - "connection not supported by LDAP client " - "libraries!\n")); - return LDAP_OPERATIONS_ERROR; -#endif - } - } -#endif - - if (ldap_get_option(state->ldap_struct, LDAP_OPT_PROTOCOL_VERSION, - &version) == LDAP_OPT_SUCCESS) { - if (version != LDAP_VERSION3) { - version = LDAP_VERSION3; - if (ldap_set_option(state->ldap_struct, - LDAP_OPT_PROTOCOL_VERSION, - &version) == LDAP_OPT_SUCCESS) { - ldap_v3 = True; - } + alg_rid_base = (uint32)atol(algorithmic_rid_base_string); } else { - ldap_v3 = True; + alg_rid_base = algorithmic_rid_base(); + /* Try to make the modification atomically by enforcing the + old value in the delete mod. */ + slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string)-1, "%d", alg_rid_base); + smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE), + algorithmic_rid_base_string); } - } - if (lp_ldap_ssl() == LDAP_SSL_START_TLS) { -#ifdef LDAP_OPT_X_TLS - if (ldap_v3) { - if ((rc = ldap_start_tls_s(state->ldap_struct, NULL, - NULL)) != LDAP_SUCCESS) { - DEBUG(0,("Failed to issue the StartTLS " - "instruction: %s\n", - ldap_err2string(rc))); - return rc; + next_rid = 0; + + if (alg_rid_base > BASE_RID) { + /* we have a non-default 'algorithmic rid base', so we have 'low' rids that we + can allocate to new users */ + if (smbldap_get_single_attribute(state->smbldap_state->ldap_struct, entry, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID), + old_rid_string)) + { + *rid = (uint32)atol(old_rid_string); + } else { + *rid = BASE_RID; + } + + next_rid = *rid+1; + if (next_rid >= alg_rid_base) { + return NT_STATUS_UNSUCCESSFUL; } - DEBUG (3, ("StartTLS issued: using a TLS " - "connection\n")); - } else { - DEBUG(0, ("Need LDAPv3 for Start TLS\n")); - return LDAP_OPERATIONS_ERROR; + slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid); + + /* Try to make the modification atomically by enforcing the + old value in the delete mod. */ + smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID), + next_rid_string); } -#else - DEBUG(0,("ldap_idmap_open_connection: StartTLS not supported by " - "LDAP client libraries!\n")); - return LDAP_OPERATIONS_ERROR; -#endif - } - - DEBUG(2, ("ldap_idmap_open_connection: connection opened\n")); - return rc; -} -/********************************************************************** -Connect to LDAP server -*********************************************************************/ -static int ldap_idmap_open(struct ldap_idmap_state *state) -{ - int rc; - SMB_ASSERT(state); - -#ifndef NO_LDAP_SECURITY - if (geteuid() != 0) { - DEBUG(0, - ("ldap_idmap_open: cannot access LDAP when not root\n")); - return LDAP_INSUFFICIENT_ACCESS; - } -#endif - - if ((state->ldap_struct != NULL) && - ((state->last_ping + LDAP_IDMAP_DONT_PING_TIME)<time(NULL))) { - struct sockaddr_un addr; - socklen_t len = sizeof(addr); - int sd; - - if (!ldap_get_option(state->ldap_struct, LDAP_OPT_DESC, &sd)&& - getpeername(sd, (struct sockaddr *) &addr, &len) < 0) { - /* the other end has died. reopen. */ - ldap_unbind_ext(state->ldap_struct, NULL, NULL); - state->ldap_struct = NULL; - state->last_ping = (time_t)0; - } else { - state->last_ping = time(NULL); - } - } - - if (state->ldap_struct != NULL) { - DEBUG(5,("ldap_idmap_open: already connected to the LDAP " - "server\n")); - return LDAP_SUCCESS; - } - - if ((rc = ldap_idmap_open_connection(state))) { - return rc; - } - - if ((rc = ldap_idmap_connect_system(state))) { - ldap_unbind_ext(state->ldap_struct, NULL, NULL); - state->ldap_struct = NULL; - return rc; - } - - - state->last_ping = time(NULL); - DEBUG(4,("The LDAP server is succesful connected\n")); - - return LDAP_SUCCESS; -} + if (!next_rid) { /* not got one already */ + switch (rid_type) { + case USER_RID_TYPE: + if (smbldap_get_single_attribute(state->smbldap_state->ldap_struct, entry, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID), + old_rid_string)) + { + + *rid = (uint32)atol(old_rid_string); + + } else { + *rid = state->low_allocated_user_rid; + } + break; + case GROUP_RID_TYPE: + if (smbldap_get_single_attribute(state->smbldap_state->ldap_struct, entry, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID), + old_rid_string)) + { + *rid = (uint32)atol(old_rid_string); + } else { + *rid = state->low_allocated_group_rid; + } + break; + } + + /* This is the core of the whole routine. If we had + scheme-style closures, there would be a *lot* less code + duplication... */ -static int ldap_idmap_retry_open(struct ldap_idmap_state *state, int *attempts) -{ - int rc; + next_rid = *rid+RID_MULTIPLIER; + slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid); + + switch (rid_type) { + case USER_RID_TYPE: + if (next_rid > state->high_allocated_user_rid) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* Try to make the modification atomically by enforcing the + old value in the delete mod. */ + smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID), + next_rid_string); + break; + + case GROUP_RID_TYPE: + if (next_rid > state->high_allocated_group_rid) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* Try to make the modification atomically by enforcing the + old value in the delete mod. */ + smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID), + next_rid_string); + break; + } + } - SMB_ASSERT(state && attempts); + if ((rc = ldap_modify_s(state->smbldap_state->ldap_struct, dn, mods)) == LDAP_SUCCESS) { + DOM_SID dom_sid; + DOM_SID sid; + pstring domain_sid_string; + int error = 0; - if (*attempts != 0) { - unsigned int sleep_time; - uint8 rand_byte = 128; /* a reasonable place to start */ + if (!smbldap_get_single_attribute(state->smbldap_state->ldap_struct, domain_result, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID), + domain_sid_string)) + { + ldap_mods_free(mods, True); + ldap_memfree(dn); + ldap_msgfree(domain_result); + return ret; + } - generate_random_buffer(&rand_byte, 1, False); + if (!string_to_sid(&dom_sid, domain_sid_string)) { + ldap_mods_free(mods, True); + ldap_memfree(dn); + ldap_msgfree(domain_result); + return ret; + } - sleep_time = (((*attempts)*(*attempts))/2)*rand_byte*2; - /* we retry after (0.5, 1, 2, 3, 4.5, 6) seconds - on average. - */ - DEBUG(3, ("Sleeping for %u milliseconds before reconnecting\n", - sleep_time)); - msleep(sleep_time); - } - (*attempts)++; + ldap_mods_free(mods, True); + mods = NULL; + ldap_memfree(dn); + ldap_msgfree(domain_result); - if ((rc = ldap_idmap_open(state))) { - DEBUG(1,("Connection to LDAP Server failed for the %d try!\n", - *attempts)); - return rc; - } - - return LDAP_SUCCESS; -} + sid_copy(&sid, &dom_sid); + sid_append_rid(&sid, *rid); -/******************************************************************* - a rebind function for authenticated referrals - This version takes a void* that we can shove useful stuff in :-) -******************************************************************/ -#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -#else -static int rebindproc_with_state (LDAP * ld, char **whop, char **credp, - int *methodp, int freeit, void *arg) -{ - struct ldap_idmap_state *state = arg; - - /** @TODO Should we be doing something to check what servers we rebind - to? Could we get a referral to a machine that we don't want to - give our username and password to? */ - - if (freeit) { - SAFE_FREE(*whop); - memset(*credp, '\0', strlen(*credp)); - SAFE_FREE(*credp); - } else { - DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", - state->bind_dn)); + /* check RID is not in use */ + if (sid_in_use(state, &sid, &error)) { + if (error) { + return ret; + } + continue; + } - *whop = strdup(state->bind_dn); - if (!*whop) { - return LDAP_NO_MEMORY; + return NT_STATUS_OK; } - *credp = strdup(state->bind_secret); - if (!*credp) { - SAFE_FREE(*whop); - return LDAP_NO_MEMORY; - } - *methodp = LDAP_AUTH_SIMPLE; - } - return 0; -} -#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ - -/******************************************************************* - a rebind function for authenticated referrals - This version takes a void* that we can shove useful stuff in :-) - and actually does the connection. -******************************************************************/ -#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -static int rebindproc_connect_with_state (LDAP *ldap_struct, - LDAP_CONST char *url, - ber_tag_t request, - ber_int_t msgid, void *arg) -{ - struct ldap_idmap_state *state = arg; - int rc; - DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n", - state->bind_dn)); - - /** @TODO Should we be doing something to check what servers we rebind - to? Could we get a referral to a machine that we don't want to - give our username and password to? */ - rc = ldap_simple_bind_s(ldap_struct, state->bind_dn, - state->bind_secret); - - return rc; -} -#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ - -/******************************************************************* - Add a rebind function for authenticated referrals -******************************************************************/ -#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -#else -# if LDAP_SET_REBIND_PROC_ARGS == 2 -static int rebindproc (LDAP *ldap_struct, char **whop, char **credp, - int *method, int freeit ) -{ - return rebindproc_with_state(ldap_struct, whop, credp, - method, freeit, &ldap_state); - -} -# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/ -#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ - -/******************************************************************* - a rebind function for authenticated referrals - this also does the connection, but no void*. -******************************************************************/ -#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -# if LDAP_SET_REBIND_PROC_ARGS == 2 -static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request, - ber_int_t msgid) -{ - return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, - msgid, &ldap_state); -} -# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/ -#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ - -/******************************************************************* - connect to the ldap server under system privilege. -******************************************************************/ -static int ldap_idmap_connect_system(struct ldap_idmap_state *state) -{ - int rc; - char *ldap_dn; - char *ldap_secret; + ld_error = NULL; + ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(2, ("Failed to modify rid: %s\n", ld_error ? ld_error : "(NULL")); + SAFE_FREE(ld_error); - /* get the password */ - if (!fetch_ldapsam_pw(&ldap_dn, &ldap_secret)) - { - DEBUG(0, ("ldap_idmap_connect_system: Failed to retrieve " - "password from secrets.tdb\n")); - return LDAP_INVALID_CREDENTIALS; - } + ldap_mods_free(mods, True); + mods = NULL; - state->bind_dn = ldap_dn; - state->bind_secret = ldap_secret; + ldap_memfree(dn); + dn = NULL; - /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite - (OpenLDAP) doesnt' seem to support it */ - - DEBUG(10,("ldap_idmap_connect_system: Binding to ldap server %s as " - "\"%s\"\n", state->uri, ldap_dn)); - -#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) -# if LDAP_SET_REBIND_PROC_ARGS == 2 - ldap_set_rebind_proc(state->ldap_struct, &rebindproc_connect); -# endif -# if LDAP_SET_REBIND_PROC_ARGS == 3 - ldap_set_rebind_proc(state->ldap_struct, - &rebindproc_connect_with_state, (void *)state); -# endif -#else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ -# if LDAP_SET_REBIND_PROC_ARGS == 2 - ldap_set_rebind_proc(state->ldap_struct, &rebindproc); -# endif -# if LDAP_SET_REBIND_PROC_ARGS == 3 - ldap_set_rebind_proc(state->ldap_struct, &rebindproc_with_state, - (void *)state); -# endif -#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ - - rc = ldap_simple_bind_s(state->ldap_struct, ldap_dn, ldap_secret); + ldap_msgfree(domain_result); + domain_result = NULL; - if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - ldap_get_option(state->ldap_struct, LDAP_OPT_ERROR_STRING, - &ld_error); - DEBUG(state->num_failures ? 2 : 0, - ("failed to bind to server with dn= %s Error: " - "%s\n\t%s\n", - ldap_dn ? ld_error : "(unknown)", - ldap_err2string(rc), ld_error)); - SAFE_FREE(ld_error); - state->num_failures++; - return rc; + { + /* Sleep for a random timeout */ + unsigned sleeptime = (sys_random()*sys_getpid()*attempts); + attempts += 1; + + sleeptime %= 100; + msleep(sleeptime); + } } - state->num_failures = 0; - - DEBUG(3, ("ldap_idmap_connect_system: succesful connection to the " - "LDAP server\n")); - return rc; + DEBUG(0, ("Failed to set new RID\n")); + return ret; } -static int ldap_idmap_search(struct ldap_idmap_state *state, - const char *base, int scope, const char *filter, - const char *attrs[], int attrsonly, - LDAPMessage **res) -{ - int rc = LDAP_SERVER_DOWN; - int attempts = 0; - char *utf8_filter; - - SMB_ASSERT(state); - - if (push_utf8_allocate(&utf8_filter, filter) == (size_t)-1) { - return LDAP_NO_MEMORY; - } - - while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) { - if ((rc = ldap_idmap_retry_open(state, &attempts)) != - LDAP_SUCCESS) continue; - - rc = ldap_search_s(state->ldap_struct, base, scope, - utf8_filter, (char**)attrs, attrsonly, res); - } - - if (rc == LDAP_SERVER_DOWN) { - DEBUG(0,("ldap_idmap_search: LDAP server is down!\n")); - ldap_idmap_close(); - } - SAFE_FREE(utf8_filter); - return rc; -} +/***************************************************************************** + Allocate a new RID +*****************************************************************************/ -/******************************************************************* -search an attribute and return the first value found. -******************************************************************/ -static BOOL ldap_idmap_attribute (struct ldap_idmap_state *state, - LDAPMessage * entry, - const char *attribute, pstring value) +static NTSTATUS ldap_allocate_rid(uint32 *rid, int rid_type) { - char **values; - value[0] = '\0'; - - if ((values = ldap_get_values (state->ldap_struct, entry, attribute)) - == NULL) { - DEBUG(10,("get_single_attribute: [%s] = [<does not exist>]\n", - attribute)); - return False; - } - if (convert_string(CH_UTF8, CH_UNIX, - values[0], -1, - value, sizeof(pstring)) == (size_t)-1) - { - DEBUG(1, ("ldap_idmap_attribute: string conversion of [%s] = " - "[%s] failed!\n", attribute, values[0])); - ldap_value_free(values); - return False; - } - ldap_value_free(values); - - return True; + return ldap_next_rid( &ldap_state, rid, rid_type ); } -static const char *attrs[] = {"objectClass", "uidNumber", "gidNumber", - "ntSid", NULL}; -static const char *pool_attr[] = {"uidNumber", "gidNumber", NULL}; +/***************************************************************************** + Allocate a new uid or gid +*****************************************************************************/ static NTSTATUS ldap_allocate_id(unid_t *id, int id_type) { NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; int rc = LDAP_SERVER_DOWN; int count = 0; - LDAPMessage *result = 0; - LDAPMessage *entry = 0; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; pstring id_str, new_id_str; - LDAPMod mod[2]; - LDAPMod *mods[3]; - const char *type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber"; - char *val[4]; + LDAPMod **mods = NULL; + const char *type; char *dn; + char **attr_list; + pstring filter; + uid_t luid, huid; + gid_t lgid, hgid; + - rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), - LDAP_SCOPE_SUBTREE, "(objectClass=unixIdPool)", - pool_attr, 0, &result); + type = (id_type & ID_USERID) ? + get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER ) : + get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER ); + + snprintf(filter, sizeof(filter)-1, "(objectClass=%s)", LDAP_OBJ_IDPOOL); + + attr_list = get_attr_list( idpool_attr_list ); + + rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(), + LDAP_SCOPE_SUBTREE, filter, + attr_list, 0, &result); + free_attr_list( attr_list ); + if (rc != LDAP_SUCCESS) { - DEBUG(0,("ldap_allocate_id: unixIdPool object not found\n")); + DEBUG(0,("ldap_allocate_id: %s object not found\n", LDAP_OBJ_IDPOOL)); goto out; } - count = ldap_count_entries(ldap_state.ldap_struct, result); + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); if (count != 1) { - DEBUG(0,("ldap_allocate_id: single unixIdPool not found\n")); + DEBUG(0,("ldap_allocate_id: single %s object not found\n", LDAP_OBJ_IDPOOL)); goto out; } - dn = ldap_get_dn(ldap_state.ldap_struct, result); - entry = ldap_first_entry(ldap_state.ldap_struct, result); + dn = ldap_get_dn(ldap_state.smbldap_state->ldap_struct, result); + entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result); - if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) { + if (!smbldap_get_single_attribute(ldap_state.smbldap_state->ldap_struct, entry, type, id_str)) { DEBUG(0,("ldap_allocate_id: %s attribute not found\n", type)); goto out; } + + /* this must succeed or else we wouldn't have initialized */ + + lp_idmap_uid( &luid, &huid); + lp_idmap_gid( &lgid, &hgid); + + /* make sure we still have room to grow */ + if (id_type & ID_USERID) { id->uid = strtoul(id_str, NULL, 10); - } else { + if (id->uid > huid ) { + DEBUG(0,("ldap_allocate_id: Cannot allocate uid above %d!\n", huid)); + goto out; + } + } + else { id->gid = strtoul(id_str, NULL, 10); + if (id->gid > hgid ) { + DEBUG(0,("ldap_allocate_id: Cannot allocate gid above %d!\n", hgid)); + goto out; + } } - - mod[0].mod_op = LDAP_MOD_DELETE; - mod[0].mod_type = strdup(type); - val[0] = id_str; val[1] = NULL; - mod[0].mod_values = val; - - pstr_sprintf(new_id_str, "%ud", + + snprintf(new_id_str, sizeof(new_id_str), "%u", ((id_type & ID_USERID) ? id->uid : id->gid) + 1); - mod[1].mod_op = LDAP_MOD_ADD; - mod[1].mod_type = strdup(type); - val[3] = new_id_str; val[4] = NULL; - mod[1].mod_values = val + 2; + + smbldap_set_mod( &mods, LDAP_MOD_DELETE, type, id_str ); + smbldap_set_mod( &mods, LDAP_MOD_ADD, type, new_id_str ); + + rc = ldap_modify_s(ldap_state.smbldap_state->ldap_struct, dn, mods); - mods[0] = mod; mods[1] = mod + 1; mods[2] = NULL; - rc = ldap_modify_s(ldap_state.ldap_struct, dn, mods); ldap_memfree(dn); - - if (rc == LDAP_SUCCESS) ret = NT_STATUS_OK; + ldap_mods_free( mods, True ); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("ldap_allocate_id: Failed to allocate new %s. ldap_modify() failed.\n", + type)); + goto out; + } + + ret = NT_STATUS_OK; out: return ret; } -/* Get a sid from an id */ +/***************************************************************************** + get a sid from an id +*****************************************************************************/ + static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type) { - LDAPMessage *result = 0; - LDAPMessage *entry = 0; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + fstring id_str; pstring sid_str; pstring filter; - char type = (id_type & ID_USERID) ? 'u' : 'g'; + pstring suffix; + const char *type; + const char *obj_class; int rc; int count; NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - - pstr_sprintf(filter, "(&(%cidNumber=%ud)(objectClass=sambaAccount))", - type, ((id_type & ID_USERID) ? id.uid : id.gid)); - rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), - LDAP_SCOPE_SUBTREE, filter, attrs, 0, - &result); - if (rc != LDAP_SUCCESS) { + char **attr_list; + + /* first we try for a samba user or group mapping */ + + if ( id_type & ID_USERID ) { + type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER ); + obj_class = LDAP_OBJ_SAMBASAMACCOUNT; + snprintf(id_str, sizeof(id_str), "%u", id.uid ); + pstrcpy( suffix, lp_ldap_suffix()); + } + else { + type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER ); + obj_class = LDAP_OBJ_GROUPMAP; + snprintf(id_str, sizeof(id_str), "%u", id.gid ); + pstrcpy( suffix, lp_ldap_group_suffix() ); + } + + attr_list = get_attr_list( sidmap_attr_list ); + snprintf(filter, sizeof(filter), "(&(|(objectClass=%s)(objectClass=%s))(%s=%s))", + LDAP_OBJ_IDMAP_ENTRY, obj_class, type, id_str); + + rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) goto out; - } + + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); + + /* fall back to looking up an idmap entry if we didn't find and + actual user or group */ - count = ldap_count_entries(ldap_state.ldap_struct, result); if (count == 0) { - pstr_sprintf(filter, - "(&(objectClass=idmapEntry)(%cidNumber=%ud))", - type, ((id_type & ID_USERID) ? id.uid : id.gid)); - rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), - LDAP_SCOPE_SUBTREE, filter, - attrs, 0, &result); - if (rc != LDAP_SUCCESS) { + ldap_msgfree(result); + result = NULL; + + snprintf(filter, sizeof(filter), "(&(objectClass=%s)(%s=%u))", + LDAP_OBJ_IDMAP_ENTRY, type, ((id_type & ID_USERID) ? id.uid : id.gid)); + + pstrcpy( suffix, lp_ldap_idmap_suffix() ); + + rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) goto out; - } - count = ldap_count_entries(ldap_state.ldap_struct, result); + + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); } if (count != 1) { - DEBUG(0,("ldap_get_sid_from_id: mapping not found for " - "%cid: %ud\n", (id_type&ID_USERID)?'u':'g', - ((id_type & ID_USERID) ? id.uid : id.gid))); + DEBUG(0,("ldap_get_sid_from_id: mapping not found for %s: %u\n", + type, ((id_type & ID_USERID) ? id.uid : id.gid))); goto out; } - entry = ldap_first_entry(ldap_state.ldap_struct, result); + entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result); - if (!ldap_idmap_attribute(&ldap_state, entry, "ntSid", sid_str)) { + if ( !smbldap_get_single_attribute(ldap_state.smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID, sid_str) ) goto out; - } - if (!string_to_sid(sid, sid_str)) { + if (!string_to_sid(sid, sid_str)) goto out; - } ret = NT_STATUS_OK; out: + free_attr_list( attr_list ); + + if (result) + ldap_msgfree(result); + return ret; } -/* Get an id from a sid */ -static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type, - const DOM_SID *sid) +/*********************************************************************** + Get an id from a sid +***********************************************************************/ + +static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) { - LDAPMessage *result = 0; - LDAPMessage *entry = 0; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; pstring sid_str; pstring filter; pstring id_str; - const char *type = (*id_type & ID_USERID) ? "uidNumber" : "gidNumber"; - const char *class = - (*id_type & ID_USERID) ? "sambaAccount" : "sambaGroupMapping"; + const char *suffix; + const char *type; + const char *obj_class; + const char *posix_obj_class; int rc; int count; + char **attr_list; + char *dn = NULL; NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - + + /* first try getting the mapping from a samba user or group */ + sid_to_string(sid_str, sid); - pstr_sprintf(filter, "(&(objectClass=%s)(ntSid=%s)", class, sid_str); - rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), - LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result); - if (rc != LDAP_SUCCESS) { - goto out; + if ( *id_type & ID_USERID ) { + type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER ); + obj_class = LDAP_OBJ_SAMBASAMACCOUNT; + posix_obj_class = LDAP_OBJ_POSIXACCOUNT; + suffix = lp_ldap_suffix(); + snprintf(filter, sizeof(filter), + "(&(|(&(objectClass=%s)(objectClass=%s))(objectClass=%s))(%s=%s))", + obj_class, posix_obj_class, LDAP_OBJ_IDMAP_ENTRY, + get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID ), + sid_str); + } + else { + type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER ); + obj_class = LDAP_OBJ_GROUPMAP; + posix_obj_class = LDAP_OBJ_POSIXGROUP; + suffix = lp_ldap_group_suffix(); + snprintf(filter, sizeof(filter), + "(&(|(objectClass=%s)(objectClass=%s))(%s=%s))", + obj_class, LDAP_OBJ_IDMAP_ENTRY, + get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID ), + sid_str); } - count = ldap_count_entries(ldap_state.ldap_struct, result); + + attr_list = get_attr_list( sidmap_attr_list ); + rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) + goto out; + + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); + + /* fall back to looking up an idmap entry if we didn't find anything under the idmap + user or group suffix */ + if (count == 0) { - pstr_sprintf(filter, - "(&(objectClass=idmapEntry)(ntSid=%s))", sid_str); + ldap_msgfree(result); + + snprintf(filter, sizeof(filter), "(&(objectClass=%s)(%s=%s))", + LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_SID, sid_str); - rc = ldap_idmap_search(&ldap_state, lp_ldap_suffix(), - LDAP_SCOPE_SUBTREE, filter, - attrs, 0, &result); - if (rc != LDAP_SUCCESS) { - goto out; - } - count = ldap_count_entries(ldap_state.ldap_struct, result); - } + suffix = lp_ldap_idmap_suffix(); - /* our search filters may 2 objects in the case that a user and group - rid are the same */ - if (count != 1 && count != 2) { - DEBUG(0, - ("ldap_get_id_from_sid: incorrect number of objects\n")); + rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + + if (rc != LDAP_SUCCESS) + goto out; + + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); + } + + if ( count > 1 ) { + DEBUG(0, ("ldap_get_id_from_sid: search %s returned more than on entry!\n", + filter)); goto out; } - entry = ldap_first_entry(ldap_state.ldap_struct, result); - if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) { - entry = ldap_next_entry(ldap_state.ldap_struct, entry); - - if (!ldap_idmap_attribute(&ldap_state, entry, type, id_str)) { - int i; - - for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) { - ret = ldap_allocate_id(id, *id_type); - if (NT_STATUS_IS_OK(ret)) { - break; - } - } - if (NT_STATUS_IS_OK(ret)) { - ret = ldap_set_mapping(sid, *id, *id_type); - } else { - DEBUG(0,("ldap_allocate_id: cannot acquire id" - " lock\n")); - } - } else { - if ((*id_type & ID_USERID)) { + /* we might have an existing entry to work with so pull out the requested information */ + + if ( count ) { + entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result); + + dn = ldap_get_dn(ldap_state.smbldap_state->ldap_struct, result); + DEBUG(10, ("Found mapping entry at dn=%s, looking for %s\n", dn, type)); + + if ( smbldap_get_single_attribute(ldap_state.smbldap_state->ldap_struct, entry, type, id_str) ) + { + if ( (*id_type & ID_USERID) ) id->uid = strtoul(id_str, NULL, 10); - } else { + else id->gid = strtoul(id_str, NULL, 10); - } + ret = NT_STATUS_OK; + goto out; } - } else { - if ((*id_type & ID_USERID)) { - id->uid = strtoul(id_str, NULL, 10); - } else { - id->gid = strtoul(id_str, NULL, 10); + } + + if (!(*id_type & ID_QUERY_ONLY)) { + /* if entry == NULL, and we are asked to - allocate a new id */ + int i; + + for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) + { + ret = ldap_allocate_id(id, *id_type); + if ( NT_STATUS_IS_OK(ret) ) + break; + } + + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(0,("ldap_allocate_id: cannot acquire id lock!\n")); + goto out; } - ret = NT_STATUS_OK; + + ret = ldap_set_mapping(sid, *id, *id_type); + } else { + /* no match, and not adding one */ + ret = NT_STATUS_UNSUCCESSFUL; } + out: + free_attr_list( attr_list ); + if (result) + ldap_msgfree(result); + if (dn) + ldap_memfree(dn); + return ret; } -/* This function cannot be called to modify a mapping, only set a new one */ -static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type) +/*********************************************************************** + This function cannot be called to modify a mapping, only set a new one + + This takes a possible pointer to the existing entry for the UID or SID + involved. +***********************************************************************/ + +static NTSTATUS ldap_set_mapping_internals(const DOM_SID *sid, unid_t id, + int id_type, const char *ldap_dn, + LDAPMessage *entry) { - pstring dn, sid_str, id_str; - const char *type = (id_type & ID_USERID) ? "uidNumber" : "gidNumber"; - LDAPMod *mods[3]; - LDAPMod mod[2]; - char *val[4]; - int rc; - int attempts = 0; + char *dn = NULL; + pstring id_str; + fstring type; + LDAPMod **mods = NULL; + int rc = -1; + int ldap_op; + fstring sid_string; + char **values = NULL; + int i; + + sid_to_string( sid_string, sid ); + + if (ldap_dn) { + DEBUG(10, ("Adding new IDMAP mapping on DN: %s", ldap_dn)); + ldap_op = LDAP_MOD_REPLACE; + dn = strdup(ldap_dn); + } else { + ldap_op = LDAP_MOD_ADD; + asprintf(&dn, "%s=%s,%s", get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID), + sid_string, lp_ldap_idmap_suffix()); + } + + if (!dn) { + DEBUG(0, ("ldap_set_mapping_internals: out of memory allocating DN!\n")); + return NT_STATUS_NO_MEMORY; + } - pstr_sprintf(id_str, "%ud", ((id_type & ID_USERID) ? id.uid : id.gid)); - sid_to_string(sid_str, sid); - pstr_sprintf(dn, "%s=%ud,%s", type, ((id_type & ID_USERID) ? id.uid : id.gid), lp_ldap_suffix()); - mod[0].mod_op = LDAP_MOD_REPLACE; - mod[0].mod_type = strdup(type); - val[0] = id_str; val[1] = NULL; - mod[0].mod_values = val; - - mod[1].mod_op = LDAP_MOD_REPLACE; - mod[1].mod_type = strdup("ntSid"); - val[2] = sid_str; val[3] = NULL; - mod[1].mod_values = val + 2; - - mods[0] = mod; mods[1] = mod + 1; mods[2] = NULL; - - do { - if ((rc = ldap_idmap_retry_open(&ldap_state, &attempts)) != - LDAP_SUCCESS) continue; + if ( id_type & ID_USERID ) + fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER ) ); + else + fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER ) ); + + snprintf(id_str, sizeof(id_str), "%u", ((id_type & ID_USERID) ? id.uid : id.gid)); + + if (entry) + values = ldap_get_values(ldap_state.smbldap_state->ldap_struct, entry, "objectClass"); + + if (values) { + BOOL found_idmap = False; + for (i=0; values[i]; i++) { + if (StrCaseCmp(values[i], LDAP_OBJ_IDMAP_ENTRY) == 0) { + found_idmap = True; + break; + } + } + if (!found_idmap) + smbldap_set_mod( &mods, LDAP_MOD_ADD, + "objectClass", LDAP_OBJ_IDMAP_ENTRY ); + } else { + smbldap_set_mod( &mods, LDAP_MOD_ADD, + "objectClass", LDAP_OBJ_IDMAP_ENTRY ); + } + + smbldap_make_mod( ldap_state.smbldap_state->ldap_struct, + entry, &mods, type, id_str ); + + smbldap_make_mod( ldap_state.smbldap_state->ldap_struct, + entry, &mods, + get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID), + sid_string ); + + /* There may well be nothing at all to do */ + if (mods) { + switch(ldap_op) + { + case LDAP_MOD_ADD: + smbldap_set_mod( &mods, LDAP_MOD_ADD, + "objectClass", LDAP_OBJ_SID_ENTRY ); + rc = smbldap_add(ldap_state.smbldap_state, dn, mods); + break; + case LDAP_MOD_REPLACE: + rc = smbldap_modify(ldap_state.smbldap_state, dn, mods); + break; + } - rc = ldap_modify_s(ldap_state.ldap_struct, dn, mods); - } while ((rc == LDAP_SERVER_DOWN) && (attempts <= 8)); + ldap_mods_free( mods, True ); + } else { + rc = LDAP_SUCCESS; + } if (rc != LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(ldap_state.smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, + &ld_error); + DEBUG(0,("ldap_set_mapping_internals: Failed to %s mapping from %s to %u [%s]\n", + (ldap_op == LDAP_MOD_ADD) ? "add" : "replace", + sid_string, (unsigned int)((id_type & ID_USERID) ? id.uid : id.gid), type)); + DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n", ld_error ? ld_error : "(NULL)", ldap_err2string (rc))); return NT_STATUS_UNSUCCESSFUL; } + + DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to %d [%s]\n", + sid_string, ((id_type & ID_USERID) ? id.uid : id.gid), type)); return NT_STATUS_OK; } +/*********************************************************************** + This function cannot be called to modify a mapping, only set a new one +***********************************************************************/ + +static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + char *dn = NULL; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + const char *type; + const char *obj_class; + const char *posix_obj_class; + const char *suffix; + fstring sid_str; + fstring id_str; + pstring filter; + char **attr_list; + int rc; + int count; + + /* try for a samba user or group mapping (looking for an entry with a SID) */ + if ( id_type & ID_USERID ) { + obj_class = LDAP_OBJ_SAMBASAMACCOUNT; + suffix = lp_ldap_suffix(); + type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER ); + posix_obj_class = LDAP_OBJ_POSIXACCOUNT; + snprintf(id_str, sizeof(id_str), "%u", id.uid ); + } + else { + obj_class = LDAP_OBJ_GROUPMAP; + suffix = lp_ldap_group_suffix(); + type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER ); + posix_obj_class = LDAP_OBJ_POSIXGROUP; + snprintf(id_str, sizeof(id_str), "%u", id.gid ); + } + + sid_to_string(sid_str, sid); + snprintf(filter, sizeof(filter), + "(|" + "(&(|(objectClass=%s)(|(objectClass=%s)(objectClass=%s)))(%s=%s))" + "(&(objectClass=%s)(%s=%s))" + ")", + /* objectClasses that might contain a SID */ + LDAP_OBJ_SID_ENTRY, LDAP_OBJ_IDMAP_ENTRY, obj_class, + get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID ), + sid_str, + + /* objectClasses that might contain a Unix UID/GID */ + posix_obj_class, + /* Unix UID/GID specifier*/ + type, + /* actual ID */ + id_str); + + attr_list = get_attr_list( sidmap_attr_list ); + rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE, + filter, attr_list, 0, &result); + free_attr_list( attr_list ); + + if (rc != LDAP_SUCCESS) + goto out; + + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); + + /* fall back to looking up an idmap entry if we didn't find anything under the idmap + user or group suffix */ + + if (count == 1) { + entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result); + + dn = ldap_get_dn(ldap_state.smbldap_state->ldap_struct, result); + DEBUG(10, ("Found partial mapping entry at dn=%s, looking for %s\n", dn, type)); + + ret = ldap_set_mapping_internals(sid, id, id_type, dn, entry); + + goto out; + } else if (count > 1) { + DEBUG(0, ("Too many entries trying to find DN to attach ldap \n")); + goto out; + } + + ret = ldap_set_mapping_internals(sid, id, id_type, NULL, NULL); + +out: + if (result) + ldap_msgfree(result); + if (dn) + ldap_memfree(dn); + + return ret; +} /***************************************************************************** Initialise idmap database. *****************************************************************************/ -static NTSTATUS ldap_idmap_init(void) +static NTSTATUS ldap_idmap_init( char *params ) { - /* We wait for the first search request before we try to connect to - the LDAP server. We may want to connect upon initialization though - -- aliguori */ + fstring filter; + int rc; + char **attr_list; + LDAPMessage *result = NULL; + LDAPMod **mods = NULL; + int count; + NTSTATUS nt_status; + + ldap_state.mem_ctx = talloc_init("idmap_ldap"); + if (!ldap_state.mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + /* assume location is the only parameter */ + if (!NT_STATUS_IS_OK(nt_status = + smbldap_init(ldap_state.mem_ctx, params, + &ldap_state.smbldap_state))) { + talloc_destroy(ldap_state.mem_ctx); + return nt_status; + } + + /* see if the idmap suffix and sub entries exists */ + + snprintf( filter, sizeof(filter), "(objectclass=%s)", LDAP_OBJ_IDPOOL ); + + attr_list = get_attr_list( idpool_attr_list ); + rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(), + LDAP_SCOPE_SUBTREE, filter, attr_list, 0, &result); + free_attr_list ( attr_list ); + + if (rc != LDAP_SUCCESS) + return NT_STATUS_UNSUCCESSFUL; + + count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result); + + if ( count > 1 ) { + DEBUG(0,("ldap_idmap_init: multiple entries returned from %s (base == %s)\n", + filter, lp_ldap_idmap_suffix() )); + return NT_STATUS_UNSUCCESSFUL; + } + else if (count == 0) { + uid_t luid, huid; + gid_t lgid, hgid; + fstring uid_str, gid_str; + + if ( !lp_idmap_uid(&luid, &huid) || !lp_idmap_gid( &lgid, &hgid ) ) { + DEBUG(0,("ldap_idmap_init: idmap uid/gid parameters not specified\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + snprintf( uid_str, sizeof(uid_str), "%d", luid ); + snprintf( gid_str, sizeof(gid_str), "%d", lgid ); + + smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDPOOL ); + smbldap_set_mod( &mods, LDAP_MOD_ADD, + get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER), uid_str ); + smbldap_set_mod( &mods, LDAP_MOD_ADD, + get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER), gid_str ); + + rc = smbldap_modify(ldap_state.smbldap_state, lp_ldap_idmap_suffix(), mods); + } + return NT_STATUS_OK; } -/* End the LDAP session */ +/***************************************************************************** + End the LDAP session +*****************************************************************************/ + static NTSTATUS ldap_idmap_close(void) { - if (ldap_state.ldap_struct != NULL) { - ldap_unbind_ext(ldap_state.ldap_struct, NULL, NULL); - ldap_state.ldap_struct = NULL; - } + + smbldap_free_struct(&(ldap_state).smbldap_state); + talloc_destroy(ldap_state.mem_ctx); DEBUG(5,("The connection to the LDAP server was closed\n")); /* maybe free the results here --metze */ @@ -823,6 +955,8 @@ static void ldap_idmap_status(void) static struct idmap_methods ldap_methods = { ldap_idmap_init, + ldap_allocate_rid, + ldap_allocate_id, ldap_get_sid_from_id, ldap_get_id_from_sid, ldap_set_mapping, @@ -833,6 +967,5 @@ static struct idmap_methods ldap_methods = { NTSTATUS idmap_ldap_init(void) { - DEBUG(0,("idmap_reg_ldap: no LDAP support\n")); return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", &ldap_methods); } diff --git a/source3/sam/idmap_tdb.c b/source3/sam/idmap_tdb.c index 31c12241bf..7f8dce1f1a 100644 --- a/source3/sam/idmap_tdb.c +++ b/source3/sam/idmap_tdb.c @@ -45,44 +45,126 @@ static struct idmap_state { gid_t gid_low, gid_high; /* Range of gids to allocate */ } idmap_state; -/* Allocate either a user or group id from the pool */ +/********************************************************************** + Return the TDB_CONTEXT* for winbindd_idmap. I **really** feel + dirty doing this, but not so dirty that I want to create another + tdb +***********************************************************************/ + +TDB_CONTEXT *idmap_tdb_handle( void ) +{ + if ( idmap_tdb ) + return idmap_tdb; + + return NULL; +} + +/********************************************************************** + allocate a new RID; We don't care if is a user or group +**********************************************************************/ + +static NTSTATUS db_allocate_rid(uint32 *rid, int rid_type) +{ + uint32 lowrid, highrid; + uint32 tmp_rid; + + /* can't handle group rids right now. This is such a mess.... */ + + if ( rid_type == GROUP_RID_TYPE ) + return NT_STATUS_UNSUCCESSFUL; + + /* cannot fail since idmap is only called winbindd */ + + idmap_get_free_rid_range( &lowrid, &highrid ); + + tmp_rid = lowrid; + + if ( !tdb_change_uint32_atomic(idmap_tdb, "RID_COUNTER", &tmp_rid, RID_MULTIPLIER) ) { + DEBUG(3,("db_allocate_rid: Failed to locate next rid record in idmap db\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + if ( tmp_rid > highrid ) { + DEBUG(0, ("db_allocate_rid: no RIDs available!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + *rid = tmp_rid; + + return NT_STATUS_OK; +} + +/********************************************************************** + Allocate either a user or group id from the pool +**********************************************************************/ + static NTSTATUS db_allocate_id(unid_t *id, int id_type) { + BOOL ret; int hwm; - if (!id) return NT_STATUS_INVALID_PARAMETER; + if (!id) + return NT_STATUS_INVALID_PARAMETER; /* Get current high water mark */ switch (id_type & ID_TYPEMASK) { case ID_USERID: + if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) { return NT_STATUS_INTERNAL_DB_ERROR; } + /* check it is in the range */ if (hwm > idmap_state.uid_high) { DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %u)\n", idmap_state.uid_high)); return NT_STATUS_UNSUCCESSFUL; } - (*id).uid = hwm++; + /* fetch a new id and increment it */ + ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, &hwm, 1); + if (!ret) { + DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* recheck it is in the range */ + if (hwm > idmap_state.uid_high) { + DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %u)\n", idmap_state.uid_high)); + return NT_STATUS_UNSUCCESSFUL; + } + + (*id).uid = hwm; + DEBUG(10,("db_allocate_id: ID_USERID (*id).uid = %d\n", (unsigned int)hwm)); - /* Store new high water mark */ - tdb_store_int32(idmap_tdb, HWM_USER, hwm); break; case ID_GROUPID: if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) { return NT_STATUS_INTERNAL_DB_ERROR; } + /* check it is in the range */ if (hwm > idmap_state.gid_high) { DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %u)\n", idmap_state.gid_high)); return NT_STATUS_UNSUCCESSFUL; } - (*id).gid = hwm++; + /* fetch a new id and increment it */ + ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, &hwm, 1); + + if (!ret) { + DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* recheck it is in the range */ + if (hwm > idmap_state.gid_high) { + DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %u)\n", idmap_state.gid_high)); + return NT_STATUS_UNSUCCESSFUL; + } + + (*id).gid = hwm; + DEBUG(10,("db_allocate_id: ID_GROUPID (*id).gid = %d\n", (unsigned int)hwm)); - /* Store new high water mark */ - tdb_store_int32(idmap_tdb, HWM_GROUP, hwm); break; default: return NT_STATUS_INVALID_PARAMETER; @@ -92,13 +174,14 @@ static NTSTATUS db_allocate_id(unid_t *id, int id_type) } /* Get a sid from an id */ -static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type) +static NTSTATUS internal_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type) { TDB_DATA key, data; fstring keystr; NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - if (!sid) return NT_STATUS_INVALID_PARAMETER; + if (!sid) + return NT_STATUS_INVALID_PARAMETER; switch (id_type & ID_TYPEMASK) { case ID_USERID: @@ -114,10 +197,13 @@ static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type) key.dptr = keystr; key.dsize = strlen(keystr) + 1; + DEBUG(10,("internal_get_sid_from_id: fetching record %s\n", keystr )); + data = tdb_fetch(idmap_tdb, key); if (data.dptr) { if (string_to_sid(sid, data.dptr)) { + DEBUG(10,("internal_get_sid_from_id: fetching record %s -> %s\n", keystr, data.dptr )); ret = NT_STATUS_OK; } SAFE_FREE(data.dptr); @@ -126,14 +212,15 @@ static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type) return ret; } -/* Get an id from a sid */ -static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) +/* Error codes for get_id_from_sid */ +enum getidfromsiderr { GET_ID_FROM_SID_OK = 0, GET_ID_FROM_SID_NOTFOUND, GET_ID_FROM_SID_WRONG_TYPE, GET_ID_FROM_SID_ERR }; + +static enum getidfromsiderr internal_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) { - TDB_DATA data, key; + enum getidfromsiderr ret = GET_ID_FROM_SID_ERR; fstring keystr; - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - - if (!sid || !id || !id_type) return NT_STATUS_INVALID_PARAMETER; + TDB_DATA key, data; + int type = *id_type & ID_TYPEMASK; /* Check if sid is present in database */ sid_to_string(keystr, sid); @@ -141,71 +228,186 @@ static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) key.dptr = keystr; key.dsize = strlen(keystr) + 1; + DEBUG(10,("internal_get_id_from_sid: fetching record %s of type 0x%x\n", keystr, type )); + data = tdb_fetch(idmap_tdb, key); + if (!data.dptr) { + DEBUG(10,("internal_get_id_from_sid: record %s not found\n", keystr )); + return GET_ID_FROM_SID_NOTFOUND; + } else { + DEBUG(10,("internal_get_id_from_sid: record %s -> %s\n", keystr, data.dptr )); + } - if (data.dptr) { - int type = *id_type & ID_TYPEMASK; + if (type == ID_EMPTY || type == ID_USERID) { fstring scanstr; + /* Parse and return existing uid */ + fstrcpy(scanstr, "UID %d"); + + if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) { + /* uid ok? */ + if (type == ID_EMPTY) { + *id_type = ID_USERID; + } + DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n", + (type == ID_EMPTY) ? "ID_EMPTY" : "ID_USERID", + keystr, data.dptr )); + ret = GET_ID_FROM_SID_OK; + } else { + ret = GET_ID_FROM_SID_WRONG_TYPE; + } + } + + if ((ret != GET_ID_FROM_SID_OK) && (type == ID_EMPTY || type == ID_GROUPID)) { + fstring scanstr; + /* Parse and return existing gid */ + fstrcpy(scanstr, "GID %d"); + + if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) { + /* gid ok? */ + if (type == ID_EMPTY) { + *id_type = ID_GROUPID; + } + DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n", + (type == ID_EMPTY) ? "ID_EMPTY" : "ID_GROUPID", + keystr, data.dptr )); + ret = GET_ID_FROM_SID_OK; + } else { + ret = GET_ID_FROM_SID_WRONG_TYPE; + } + } + + SAFE_FREE(data.dptr); - if (type == ID_EMPTY || type == ID_USERID) { - /* Parse and return existing uid */ - fstrcpy(scanstr, "UID %d"); + return ret; +} - if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) { - /* uid ok? */ - if (type == ID_EMPTY) { - *id_type = ID_USERID; - } - ret = NT_STATUS_OK; - goto idok; - } +/* Get a sid from an id */ +static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type_in) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + enum getidfromsiderr iderr; + int id_type = id_type_in & ID_TYPEMASK; + unid_t id_tmp = id; + int id_type_tmp = id_type; + + DEBUG(10,("db_get_sid_from_id: id_type_in = 0x%x\n", id_type_in)); + + ret = internal_get_sid_from_id(sid, id, id_type); + if (!NT_STATUS_IS_OK(ret)) { + return ret; + } + + iderr = internal_get_id_from_sid(&id_tmp, &id_type_tmp, sid); + if (iderr != GET_ID_FROM_SID_OK) { + return NT_STATUS_UNSUCCESSFUL; + } + if (id_type_tmp != id_type) { + return NT_STATUS_UNSUCCESSFUL; + } else if (id_type == ID_USERID) { + if (id_tmp.uid != id.uid) { + return NT_STATUS_UNSUCCESSFUL; + } + } else if (id_type == ID_GROUPID) { + if (id_tmp.gid != id.gid) { + return NT_STATUS_UNSUCCESSFUL; } + } else { + return NT_STATUS_UNSUCCESSFUL; + } + return ret; +} +/* Get an id from a sid */ +static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + enum getidfromsiderr iderr; - if (type == ID_EMPTY || type == ID_GROUPID) { - /* Parse and return existing gid */ - fstrcpy(scanstr, "GID %d"); + DEBUG(10,("db_get_id_from_sid\n")); - if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) { - /* gid ok? */ - if (type == ID_EMPTY) { - *id_type = ID_GROUPID; - } - ret = NT_STATUS_OK; + if (!sid || !id || !id_type) + return NT_STATUS_INVALID_PARAMETER; + + iderr = internal_get_id_from_sid(id, id_type, sid); + if (iderr == GET_ID_FROM_SID_OK) { + DOM_SID sid_tmp; + ret = internal_get_sid_from_id(&sid_tmp, *id, *id_type); + if (NT_STATUS_IS_OK(ret)) { + if (!sid_equal(&sid_tmp, sid)) { + return NT_STATUS_UNSUCCESSFUL; } } -idok: - SAFE_FREE(data.dptr); + } else if (iderr == GET_ID_FROM_SID_WRONG_TYPE) { + /* We found a record but not the type we wanted. + * This is an error, not an opportunity to overwrite... + * JRA. + */ + return NT_STATUS_UNSUCCESSFUL; + } - } else if (!(*id_type & ID_NOMAP) && + if (!(*id_type & ID_QUERY_ONLY) && (iderr != GET_ID_FROM_SID_OK) && (((*id_type & ID_TYPEMASK) == ID_USERID) || (*id_type & ID_TYPEMASK) == ID_GROUPID)) { + TDB_DATA sid_data; + TDB_DATA ugid_data; + fstring sid_string; + + sid_to_string(sid_string, sid); + + sid_data.dptr = sid_string; + sid_data.dsize = strlen(sid_string)+1; + + /* Lock the record for this SID. */ + if (tdb_chainlock(idmap_tdb, sid_data) != 0) { + DEBUG(10,("db_get_id_from_sid: failed to lock record %s. Error %s\n", + sid_string, tdb_errorstr(idmap_tdb) )); + return NT_STATUS_UNSUCCESSFUL; + } - /* Allocate a new id for this sid */ - ret = db_allocate_id(id, *id_type); - if (NT_STATUS_IS_OK(ret)) { - fstring keystr2; + do { + fstring ugid_str; + /* Allocate a new id for this sid */ + ret = db_allocate_id(id, *id_type); + if (!NT_STATUS_IS_OK(ret)) + break; + + /* Store the UID side */ /* Store new id */ if (*id_type & ID_USERID) { - slprintf(keystr2, sizeof(keystr2), "UID %d", (*id).uid); + slprintf(ugid_str, sizeof(ugid_str), "UID %d", (*id).uid); } else { - slprintf(keystr2, sizeof(keystr2), "GID %d", (*id).gid); + slprintf(ugid_str, sizeof(ugid_str), "GID %d", (*id).gid); } + + ugid_data.dptr = ugid_str; + ugid_data.dsize = strlen(ugid_str) + 1; - data.dptr = keystr2; - data.dsize = strlen(keystr2) + 1; + DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n", + ugid_data.dptr, sid_data.dptr )); - if (tdb_store(idmap_tdb, key, data, TDB_REPLACE) == -1) { - /* TODO: print tdb error !! */ - return NT_STATUS_UNSUCCESSFUL; + if (tdb_store(idmap_tdb, ugid_data, sid_data, TDB_INSERT) != -1) { + ret = NT_STATUS_OK; + break; } - if (tdb_store(idmap_tdb, data, key, TDB_REPLACE) == -1) { + if (tdb_error(idmap_tdb) != TDB_ERR_EXISTS) + DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) )); + ret = NT_STATUS_UNSUCCESSFUL; + } while (tdb_error(idmap_tdb) == TDB_ERR_EXISTS); + + if (NT_STATUS_IS_OK(ret)) { + + DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n", + sid_data.dptr, ugid_data.dptr )); + + if (tdb_store(idmap_tdb, sid_data, ugid_data, TDB_REPLACE) == -1) { + DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) )); /* TODO: print tdb error !! */ + tdb_chainunlock(idmap_tdb, sid_data); return NT_STATUS_UNSUCCESSFUL; } - - ret = NT_STATUS_OK; } + + tdb_chainunlock(idmap_tdb, sid_data); } return ret; @@ -217,7 +419,10 @@ static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type) fstring ksidstr; fstring kidstr; - if (!sid) return NT_STATUS_INVALID_PARAMETER; + DEBUG(10,("db_set_mapping: id_type = 0x%x\n", id_type)); + + if (!sid) + return NT_STATUS_INVALID_PARAMETER; sid_to_string(ksidstr, sid); @@ -238,32 +443,51 @@ static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type) /* *DELETE* prevoius mappings if any. * This is done both SID and [U|G]ID passed in */ + /* Lock the record for this SID. */ + if (tdb_chainlock(idmap_tdb, ksid) != 0) { + DEBUG(10,("db_set_mapping: failed to lock record %s. Error %s\n", + ksidstr, tdb_errorstr(idmap_tdb) )); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(10,("db_set_mapping: fetching %s\n", ksid.dptr)); + data = tdb_fetch(idmap_tdb, ksid); if (data.dptr) { + DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, ksid.dptr )); tdb_delete(idmap_tdb, data); tdb_delete(idmap_tdb, ksid); + SAFE_FREE(data.dptr); } data = tdb_fetch(idmap_tdb, kid); if (data.dptr) { + DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, kid.dptr )); tdb_delete(idmap_tdb, data); tdb_delete(idmap_tdb, kid); + SAFE_FREE(data.dptr); } if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) { DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb))); + tdb_chainunlock(idmap_tdb, ksid); return NT_STATUS_UNSUCCESSFUL; } if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) { DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb))); + tdb_chainunlock(idmap_tdb, ksid); return NT_STATUS_UNSUCCESSFUL; } + + tdb_chainunlock(idmap_tdb, ksid); + DEBUG(10,("db_set_mapping: stored %s -> %s and %s -> %s\n", ksid.dptr, kid.dptr, kid.dptr, ksid.dptr )); return NT_STATUS_OK; } /***************************************************************************** Initialise idmap database. *****************************************************************************/ -static NTSTATUS db_idmap_init(void) + +static NTSTATUS db_idmap_init( char *params ) { SMB_STRUCT_STAT stbuf; char *tdbfile = NULL; @@ -271,30 +495,22 @@ static NTSTATUS db_idmap_init(void) BOOL tdb_is_new = False; /* use the old database if present */ - if (!file_exist(lock_path("idmap.tdb"), &stbuf)) { - if (file_exist(lock_path("winbindd_idmap.tdb"), &stbuf)) { - DEBUG(0, ("idmap_init: using winbindd_idmap.tdb file!\n")); - tdbfile = strdup(lock_path("winbindd_idmap.tdb")); - if (!tdbfile) { - DEBUG(0, ("idmap_init: out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } - } else { - tdb_is_new = True; - } - } + tdbfile = strdup(lock_path("winbindd_idmap.tdb")); if (!tdbfile) { - tdbfile = strdup(lock_path("idmap.tdb")); - if (!tdbfile) { - DEBUG(0, ("idmap_init: out of memory!\n")); - return NT_STATUS_NO_MEMORY; - } + DEBUG(0, ("idmap_init: out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + if (!file_exist(tdbfile, &stbuf)) { + tdb_is_new = True; } - /* Open tdb cache */ + DEBUG(10,("db_idmap_init: Opening tdbfile %s\n", tdbfile )); + + /* Open idmap repository */ if (!(idmap_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, - 0600))) { + 0644))) { DEBUG(0, ("idmap_init: Unable to open idmap database\n")); SAFE_FREE(tdbfile); return NT_STATUS_UNSUCCESSFUL; @@ -302,16 +518,20 @@ static NTSTATUS db_idmap_init(void) SAFE_FREE(tdbfile); - /* check against earlier versions */ if (tdb_is_new) { - /* TODO: delete the file if this fail */ + /* the file didn't existed before opening it, let's + * store idmap version as nobody else yet opened and + * stored it. I do not like this method but didn't + * found a way to understand if an opened tdb have + * been just created or not --- SSS */ tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION); - } else { - version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); - if (version != IDMAP_VERSION) { - DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n")); - return NT_STATUS_INTERNAL_DB_ERROR; - } + } + + /* check against earlier versions */ + version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); + if (version != IDMAP_VERSION) { + DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n")); + return NT_STATUS_INTERNAL_DB_ERROR; } /* Create high water marks for group and user id */ @@ -424,9 +644,11 @@ static void db_idmap_status(void) /* Display complete mapping of users and groups to rids */ } -struct idmap_methods db_methods = { +static struct idmap_methods db_methods = { db_idmap_init, + db_allocate_rid, + db_allocate_id, db_get_sid_from_id, db_get_id_from_sid, db_set_mapping, @@ -435,9 +657,7 @@ struct idmap_methods db_methods = { }; -NTSTATUS idmap_reg_tdb(struct idmap_methods **meth) +NTSTATUS idmap_tdb_init(void) { - *meth = &db_methods; - - return NT_STATUS_OK; + return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods); } diff --git a/source3/sam/idmap_util.c b/source3/sam/idmap_util.c index 8c3a378832..f767cc898c 100644 --- a/source3/sam/idmap_util.c +++ b/source3/sam/idmap_util.c @@ -22,10 +22,54 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_IDMAP +/********************************************************************** +**********************************************************************/ + +BOOL idmap_get_free_ugid_range(uint32 *low, uint32 *high) +{ + uid_t u_low, u_high; + gid_t g_low, g_high; + + if (!lp_idmap_uid(&u_low, &u_high) || !lp_idmap_gid(&g_low, &g_high)) { + return False; + } + + *low = (u_low < g_low) ? u_low : g_low; + *high = (u_high < g_high) ? u_high : g_high; + + return True; +} /****************************************************************** - * Get the free RID base if idmap is configured, otherwise return 0 - ******************************************************************/ + Get the the non-algorithmic RID range if idmap range are defined +******************************************************************/ + +BOOL idmap_get_free_rid_range(uint32 *low, uint32 *high) +{ + uint32 id_low, id_high; + + if (!lp_enable_rid_algorithm()) { + *low = BASE_RID; + *high = (uint32)-1; + } + + if (!idmap_get_free_ugid_range(&id_low, &id_high)) { + return False; + } + + *low = fallback_pdb_uid_to_user_rid(id_low); + if (fallback_pdb_user_rid_to_uid((uint32)-1) < id_high) { + *high = (uint32)-1; + } else { + *high = fallback_pdb_uid_to_user_rid(id_high); + } + + return True; +} + +/********************************************************************** + Get the free RID base if idmap is configured, otherwise return 0 +**********************************************************************/ uint32 idmap_get_free_rid_base(void) { @@ -36,6 +80,9 @@ uint32 idmap_get_free_rid_base(void) return 0; } +/********************************************************************** +**********************************************************************/ + BOOL idmap_check_ugid_is_in_free_range(uint32 id) { uint32 low, high; @@ -49,6 +96,9 @@ BOOL idmap_check_ugid_is_in_free_range(uint32 id) return True; } +/********************************************************************** +**********************************************************************/ + BOOL idmap_check_rid_is_in_free_range(uint32 rid) { uint32 low, high; @@ -56,13 +106,20 @@ BOOL idmap_check_rid_is_in_free_range(uint32 rid) if (!idmap_get_free_rid_range(&low, &high)) { return False; } + if (rid < algorithmic_rid_base()) { + return True; + } + if (rid < low || rid > high) { return False; } + return True; } -/* if it is a foreign SID or if the SID is in the free range, return true */ +/********************************************************************** + if it is a foreign SID or if the SID is in the free range, return true +**********************************************************************/ BOOL idmap_check_sid_is_in_free_range(const DOM_SID *sid) { @@ -80,182 +137,70 @@ BOOL idmap_check_sid_is_in_free_range(const DOM_SID *sid) return True; } -/****************************************************************** - * Get the the non-algorithmic RID range if idmap range are defined - ******************************************************************/ - -BOOL idmap_get_free_rid_range(uint32 *low, uint32 *high) -{ - uint32 id_low, id_high; - - if (lp_idmap_only()) { - *low = BASE_RID; - *high = (uint32)-1; - } - - if (!idmap_get_free_ugid_range(&id_low, &id_high)) { - return False; - } - - *low = fallback_pdb_uid_to_user_rid(id_low); - if (fallback_pdb_user_rid_to_uid((uint32)-1) < id_high) { - *high = (uint32)-1; - } else { - *high = fallback_pdb_uid_to_user_rid(id_high); - } - - return True; -} - -BOOL idmap_get_free_ugid_range(uint32 *low, uint32 *high) -{ - uid_t u_low, u_high; - gid_t g_low, g_high; - - if (!lp_idmap_uid(&u_low, &u_high) || !lp_idmap_gid(&g_low, &g_high)) { - return False; - } - if (u_low < g_low) { - *low = u_low; - } else { - *low = g_low; - } - if (u_high < g_high) { - *high = g_high; - } else { - *high = u_high; - } - return True; -} - /***************************************************************** - *THE CANONICAL* convert uid_t to SID function. - check idmap if uid is in idmap range, otherwise falls back to - the legacy algorithmic mapping. - A special cache is used for uids that maps to Wellknown SIDs Returns SID pointer. *****************************************************************/ -NTSTATUS uid_to_sid(DOM_SID *sid, uid_t uid) +NTSTATUS idmap_uid_to_sid(DOM_SID *sid, uid_t uid) { - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; unid_t id; int flags; - DEBUG(10,("uid_to_sid: uid = [%d]\n", uid)); + DEBUG(10,("idmap_uid_to_sid: uid = [%d]\n", uid)); flags = ID_USERID; - if (!lp_idmap_only() && !idmap_check_ugid_is_in_free_range(uid)) { - flags |= ID_NOMAP; - } - id.uid = uid; - if (NT_STATUS_IS_ERR(ret = idmap_get_sid_from_id(sid, id, flags))) { - DEBUG(10, ("uid_to_sid: Failed to map uid = [%u]\n", (unsigned int)uid)); - if (flags & ID_NOMAP) { - sid_copy(sid, get_global_sam_sid()); - sid_append_rid(sid, fallback_pdb_uid_to_user_rid(uid)); - - DEBUG(10,("uid_to_sid: Fall back to algorithmic mapping: %u -> %s\n", (unsigned int)uid, sid_string_static(sid))); - ret = NT_STATUS_OK; - } - } - - return ret; + + return idmap_get_sid_from_id(sid, id, flags); } /***************************************************************** - *THE CANONICAL* convert gid_t to SID function. - check idmap if gid is in idmap range, otherwise falls back to - the legacy algorithmic mapping. Group mapping is used for gids that maps to Wellknown SIDs Returns SID pointer. *****************************************************************/ -NTSTATUS gid_to_sid(DOM_SID *sid, gid_t gid) +NTSTATUS idmap_gid_to_sid(DOM_SID *sid, gid_t gid) { - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; unid_t id; int flags; - DEBUG(10,("gid_to_sid: gid = [%d]\n", gid)); + DEBUG(10,("idmap_gid_to_sid: gid = [%d]\n", gid)); flags = ID_GROUPID; - if (!lp_idmap_only() && !idmap_check_ugid_is_in_free_range(gid)) { - flags |= ID_NOMAP; +#if 0 /* JERRY */ + if (!idmap_check_ugid_is_in_free_range(gid)) { + flags |= ID_QUERY_ONLY; } - +#endif id.gid = gid; - if (NT_STATUS_IS_ERR(ret = idmap_get_sid_from_id(sid, id, flags))) { - DEBUG(10, ("gid_to_sid: Failed to map gid = [%u]\n", (unsigned int)gid)); - if (flags & ID_NOMAP) { - sid_copy(sid, get_global_sam_sid()); - sid_append_rid(sid, pdb_gid_to_group_rid(gid)); - - DEBUG(10,("gid_to_sid: Fall back to algorithmic mapping: %u -> %s\n", (unsigned int)gid, sid_string_static(sid))); - ret = NT_STATUS_OK; - } - } - - return ret; + return idmap_get_sid_from_id(sid, id, flags); } /***************************************************************** - *THE CANONICAL* convert SID to uid function. if it is a foreign sid or it is in idmap rid range check idmap, otherwise falls back to the legacy algorithmic mapping. - A special cache is used for uids that maps to Wellknown SIDs Returns True if this name is a user sid and the conversion was done correctly, False if not. *****************************************************************/ -NTSTATUS sid_to_uid(const DOM_SID *sid, uid_t *uid) +NTSTATUS idmap_sid_to_uid(const DOM_SID *sid, uid_t *uid, uint32 flags) { NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - BOOL fallback = False; unid_t id; - int flags; - - DEBUG(10,("sid_to_uid: sid = [%s]\n", sid_string_static(sid))); - - flags = ID_USERID; - if (!lp_idmap_only()) { - if (!idmap_check_sid_is_in_free_range(sid)) { - flags |= ID_NOMAP; - fallback = True; - } - } - if (NT_STATUS_IS_OK(idmap_get_id_from_sid(&id, &flags, sid))) { + DEBUG(10,("idmap_sid_to_uid: sid = [%s]\n", sid_string_static(sid))); - DEBUG(10,("sid_to_uid: uid = [%d]\n", id.uid)); + flags |= ID_USERID; + ret = idmap_get_id_from_sid(&id, &flags, sid); + + if ( NT_STATUS_IS_OK(ret) ) { + DEBUG(10,("idmap_sid_to_uid: uid = [%d]\n", id.uid)); *uid = id.uid; - ret = NT_STATUS_OK; - - } else if (fallback) { - uint32 rid; - - if (!sid_peek_rid(sid, &rid)) { - DEBUG(10,("sid_to_uid: invalid SID!\n")); - ret = NT_STATUS_INVALID_PARAMETER; - goto done; - } - - DEBUG(10,("sid_to_uid: Fall back to algorithmic mapping\n")); - - if (!fallback_pdb_rid_is_user(rid)) { - DEBUG(3, ("sid_to_uid: SID %s is *NOT* a user\n", sid_string_static(sid))); - ret = NT_STATUS_UNSUCCESSFUL; - } else { - *uid = fallback_pdb_user_rid_to_uid(rid); - DEBUG(10,("sid_to_uid: mapping: %s -> %u\n", sid_string_static(sid), (unsigned int)(*uid))); - ret = NT_STATUS_OK; - } - } + } -done: return ret; + } /***************************************************************** @@ -267,56 +212,59 @@ done: was done correctly, False if not. *****************************************************************/ -NTSTATUS sid_to_gid(const DOM_SID *sid, gid_t *gid) +NTSTATUS idmap_sid_to_gid(const DOM_SID *sid, gid_t *gid, uint32 flags) { NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - BOOL fallback = False; unid_t id; - int flags; DEBUG(10,("sid_to_gid: sid = [%s]\n", sid_string_static(sid))); - flags = ID_GROUPID; - if (!lp_idmap_only()) { - if (!idmap_check_sid_is_in_free_range(sid)) { - flags |= ID_NOMAP; - fallback = True; - } - } + flags |= ID_GROUPID; - if (NT_STATUS_IS_OK(idmap_get_id_from_sid(&id, &flags, sid))) { - - DEBUG(10,("sid_to_gid: gid = [%d]\n", id.gid)); + ret = idmap_get_id_from_sid(&id, &flags, sid); + + if ( NT_STATUS_IS_OK(ret) ) + { + DEBUG(10,("idmap_sid_to_gid: gid = [%d]\n", id.gid)); *gid = id.gid; - ret = NT_STATUS_OK; + } - } else if (fallback) { - uint32 rid; + return ret; +} - if (!sid_peek_rid(sid, &rid)) { - DEBUG(10,("sid_to_uid: invalid SID!\n")); - ret = NT_STATUS_INVALID_PARAMETER; - goto done; - } - DEBUG(10,("sid_to_gid: Fall back to algorithmic mapping\n")); +/*************************************************************************** + Check first, call set_mapping if it doesn't already exist. +***************************************************************************/ - if (fallback_pdb_rid_is_user(rid)) { - DEBUG(3, ("sid_to_gid: SID %s is *NOT* a group\n", sid_string_static(sid))); - ret = NT_STATUS_UNSUCCESSFUL; - } else { - *gid = pdb_group_rid_to_gid(rid); - DEBUG(10,("sid_to_gid: mapping: %s -> %u\n", sid_string_static(sid), (unsigned int)(*gid))); - ret = NT_STATUS_OK; +static NTSTATUS wellknown_id_init(DOM_SID *sid, unid_t id, int flags) +{ + unid_t storedid; + int qflags = flags | ID_QUERY_ONLY; + + if (!NT_STATUS_IS_OK(idmap_get_id_from_sid(&storedid, &qflags, sid))) { + return idmap_set_mapping(sid, id, flags); + } else { + if (flags == ID_USERID && id.uid != storedid.uid) { + DEBUG(0,("wellknown_id_init: WARNING ! Stored uid %u for SID %s is not the same as the requested uid %u\n", + (unsigned int)storedid.uid, sid_string_static(sid), (unsigned int)id.uid )); + DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n")); + return idmap_set_mapping(sid, id, flags); + } else if (flags == ID_GROUPID && id.gid != storedid.gid) { + DEBUG(0,("wellknown_id_init: WARNING ! Stored gid %u for SID %s is not the same as the requested gid %u\n", + (unsigned int)storedid.gid, sid_string_static(sid), (unsigned int)id.gid )); + DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n")); + return idmap_set_mapping(sid, id, flags); } } - -done: - return ret; + return NT_STATUS_OK; } -/* Initialize idmap withWellknown SIDs like Guest, that are necessary - * to make samba run properly */ +/*************************************************************************** + Initialize idmap withWellknown SIDs like Guest, that are necessary + to make samba run properly. +***************************************************************************/ + BOOL idmap_init_wellknown_sids(void) { const char *guest_account = lp_guestaccount(); @@ -325,7 +273,7 @@ BOOL idmap_init_wellknown_sids(void) int num_entries=0; DOM_SID sid; unid_t id; - int flags; + fstring sid_string; if (!(guest_account && *guest_account)) { DEBUG(1, ("NULL guest account!?!?\n")); @@ -337,39 +285,52 @@ BOOL idmap_init_wellknown_sids(void) return False; } - flags = ID_USERID; + /* Fill in the SID for the guest account. */ id.uid = pass->pw_uid; sid_copy(&sid, get_global_sam_sid()); sid_append_rid(&sid, DOMAIN_USER_RID_GUEST); - if (NT_STATUS_IS_ERR(idmap_set_mapping(&sid, id, flags))) { + + if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) { + DEBUG(0, ("Failed to setup UID mapping for GUEST (%s) to (%u)\n", + sid_to_string(sid_string, &sid), (unsigned int)id.uid)); + passwd_free(&pass); + return False; + } + + /* check if DOMAIN_GROUP_RID_GUESTS SID is set, if not store the + * guest account gid as mapping */ + id.gid = pass->pw_gid; + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, DOMAIN_GROUP_RID_GUESTS); + if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_GROUPID))) { + DEBUG(0, ("Failed to setup GID mapping for Group DOMAIN GUESTS (%s) to (%u)\n", + sid_to_string(sid_string, &sid), (unsigned int)id.gid)); passwd_free(&pass); return False; } + passwd_free(&pass); /* now fill in group mappings */ - if(pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) { + if(pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED)) { int i; for (i = 0; i < num_entries; i++) { id.gid = map[i].gid; - idmap_set_mapping(&(map[i].sid), id, ID_GROUPID); + wellknown_id_init(&map[i].sid, id, ID_GROUPID); } + SAFE_FREE(map); } - /* check if DOMAIN_GROUP_RID_GUESTS SID is set, if not store the - * guest account gid as mapping */ - flags = ID_GROUPID | ID_NOMAP; + /* Fill in the SID for the administrator account. */ + id.uid = 0; sid_copy(&sid, get_global_sam_sid()); - sid_append_rid(&sid, DOMAIN_GROUP_RID_GUESTS); - if (NT_STATUS_IS_ERR(idmap_get_id_from_sid(&id, &flags, &sid))) { - flags = ID_GROUPID; - id.gid = pass->pw_gid; - if (NT_STATUS_IS_ERR(idmap_set_mapping(&sid, id, flags))) { - passwd_free(&pass); - return False; - } + sid_append_rid(&sid, DOMAIN_USER_RID_ADMIN); + + if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) { + DEBUG(0, ("Failed to setup UID mapping for ADMINISTRATOR (%s) to (%u)\n", + sid_to_string(sid_string, &sid), (unsigned int)id.uid)); + return False; } - passwd_free(&pass); return True; } |