summaryrefslogtreecommitdiff
path: root/source3/sam
diff options
context:
space:
mode:
Diffstat (limited to 'source3/sam')
-rw-r--r--source3/sam/idmap.c15
-rw-r--r--source3/sam/idmap_ldap.c283
-rw-r--r--source3/sam/idmap_tdb.c41
-rw-r--r--source3/sam/idmap_util.c114
4 files changed, 450 insertions, 3 deletions
diff --git a/source3/sam/idmap.c b/source3/sam/idmap.c
index 1db89eba24..7a8f270e15 100644
--- a/source3/sam/idmap.c
+++ b/source3/sam/idmap.c
@@ -252,7 +252,7 @@ NTSTATUS idmap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
}
/**************************************************************************
- Get ID from SID. This can create a mapping for a SID to a POSIX id.
+ Alloocate a new UNIX uid/gid
**************************************************************************/
NTSTATUS idmap_allocate_id(unid_t *id, int id_type)
@@ -265,6 +265,19 @@ NTSTATUS idmap_allocate_id(unid_t *id, int 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.
diff --git a/source3/sam/idmap_ldap.c b/source3/sam/idmap_ldap.c
index 2901b1fc49..9a1ee039d0 100644
--- a/source3/sam/idmap_ldap.c
+++ b/source3/sam/idmap_ldap.c
@@ -42,7 +42,11 @@ struct ldap_idmap_state {
struct smbldap_state *smbldap_state;
TALLOC_CTX *mem_ctx;
- /* struct ldap_idmap_state *prev, *next; */
+ uint32 low_allocated_user_rid;
+ uint32 high_allocated_user_rid;
+ uint32 low_allocated_group_rid;
+ uint32 high_allocated_group_rid;
+
};
#define LDAP_MAX_ALLOC_ID 128 /* number tries while allocating
@@ -56,6 +60,282 @@ static NTSTATUS ldap_set_mapping_internals(const DOM_SID *sid, unid_t id, int id
static NTSTATUS ldap_idmap_close(void);
+/**********************************************************************
+ Even if the sambaDomain attribute in LDAP tells us that this RID is
+ safe to use, always check before use.
+*********************************************************************/
+
+static BOOL sid_in_use(struct ldap_idmap_state *state,
+ const DOM_SID *sid, int *error)
+{
+ fstring filter;
+ fstring sid_string;
+ LDAPMessage *result = NULL;
+ int count;
+ int rc;
+ char *sid_attr[] = {LDAP_ATTRIBUTE_SID, NULL};
+
+ slprintf(filter, sizeof(filter)-1, "(%s=%s)", LDAP_ATTRIBUTE_SID, sid_to_string(sid_string, sid));
+
+ rc = smbldap_search_suffix(state->smbldap_state,
+ filter, sid_attr, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to check if sid %s is alredy in use: %s\n",
+ sid_string, ld_error));
+ SAFE_FREE(ld_error);
+
+ *error = rc;
+ return True;
+ }
+
+ if ((count = ldap_count_entries(state->smbldap_state->ldap_struct, result)) > 0) {
+ DEBUG(3, ("Sid %s already in use - trying next RID\n",
+ sid_string));
+ ldap_msgfree(result);
+ return True;
+ }
+
+ ldap_msgfree(result);
+
+ /* good, sid is not in use */
+ return False;
+}
+
+/**********************************************************************
+ Set the new nextRid attribute, and return one we can use.
+
+ This also checks that this RID is actually free - in case the admin
+ manually stole it :-).
+*********************************************************************/
+static NTSTATUS ldap_next_rid(struct ldap_idmap_state *state, uint32 *rid,
+ int rid_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc;
+ LDAPMessage *domain_result = NULL;
+ LDAPMessage *entry = NULL;
+ char *dn;
+ LDAPMod **mods = NULL;
+ fstring old_rid_string;
+ fstring next_rid_string;
+ fstring algorithmic_rid_base_string;
+ uint32 next_rid;
+ uint32 alg_rid_base;
+ int attempts = 0;
+ char *ld_error = NULL;
+
+ while (attempts < 10)
+ {
+ if (!NT_STATUS_IS_OK(ret = smbldap_search_domain_info(state->smbldap_state,
+ &domain_result, get_global_sam_name(), True)))
+ {
+ return ret;
+ }
+
+ entry = ldap_first_entry(state->smbldap_state->ldap_struct, domain_result);
+ if (!entry) {
+ DEBUG(0, ("Could not get domain info entry\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if ((dn = 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 (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))
+ {
+
+ alg_rid_base = (uint32)atol(algorithmic_rid_base_string);
+ } else {
+ alg_rid_base = algorithmic_rid_base();
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string)-1, "%d", alg_rid_base);
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string);
+ }
+
+ next_rid = 0;
+
+ if (alg_rid_base > BASE_RID) {
+ /* we have a non-default 'algorithmic rid base', so we have 'low' rids that we
+ can allocate to new users */
+ if (smbldap_get_single_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;
+ }
+
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ next_rid_string);
+ }
+
+ if (!next_rid) { /* not got one already */
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ if (smbldap_get_single_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... */
+
+ 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;
+ }
+ }
+
+ 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 (!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;
+ }
+
+ if (!string_to_sid(&dom_sid, domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ ldap_memfree(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+ ldap_memfree(dn);
+ ldap_msgfree(domain_result);
+
+ sid_copy(&sid, &dom_sid);
+ sid_append_rid(&sid, *rid);
+
+ /* check RID is not in use */
+ if (sid_in_use(state, &sid, &error)) {
+ if (error) {
+ return ret;
+ }
+ continue;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to modify rid: %s\n", ld_error ? ld_error : "(NULL"));
+ SAFE_FREE(ld_error);
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+
+ ldap_memfree(dn);
+ dn = NULL;
+
+ ldap_msgfree(domain_result);
+ domain_result = NULL;
+
+ {
+ /* Sleep for a random timeout */
+ unsigned sleeptime = (sys_random()*sys_getpid()*attempts);
+ attempts += 1;
+
+ sleeptime %= 100;
+ msleep(sleeptime);
+ }
+ }
+
+ DEBUG(0, ("Failed to set new RID\n"));
+ return ret;
+}
+
+
+/*****************************************************************************
+ Allocate a new RID
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_rid(uint32 *rid, int rid_type)
+{
+ return ldap_next_rid( &ldap_state, rid, rid_type );
+}
+
/*****************************************************************************
Allocate a new uid or gid
*****************************************************************************/
@@ -675,6 +955,7 @@ 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,
diff --git a/source3/sam/idmap_tdb.c b/source3/sam/idmap_tdb.c
index 4643b7db59..7f8dce1f1a 100644
--- a/source3/sam/idmap_tdb.c
+++ b/source3/sam/idmap_tdb.c
@@ -59,7 +59,45 @@ TDB_CONTEXT *idmap_tdb_handle( void )
return NULL;
}
-/* Allocate either a user or group id from the pool */
+/**********************************************************************
+ 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;
@@ -609,6 +647,7 @@ static void db_idmap_status(void)
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,
diff --git a/source3/sam/idmap_util.c b/source3/sam/idmap_util.c
index 94de30a5ce..f767cc898c 100644
--- a/source3/sam/idmap_util.c
+++ b/source3/sam/idmap_util.c
@@ -22,6 +22,120 @@
#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 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)
+{
+ uint32 low, high;
+ if (idmap_get_free_rid_range(&low, &high)) {
+ return low;
+ }
+ return 0;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_ugid_is_in_free_range(uint32 id)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_ugid_range(&low, &high)) {
+ return False;
+ }
+ if (id < low || id > high) {
+ return False;
+ }
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_rid_is_in_free_range(uint32 rid)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_rid_range(&low, &high)) {
+ return False;
+ }
+ if (rid < algorithmic_rid_base()) {
+ return True;
+ }
+
+ if (rid < low || rid > high) {
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ if it is a foreign SID or if the SID is in the free range, return true
+**********************************************************************/
+
+BOOL idmap_check_sid_is_in_free_range(const DOM_SID *sid)
+{
+ if (sid_compare_domain(get_global_sam_sid(), sid) == 0) {
+
+ uint32 rid;
+
+ if (sid_peek_rid(sid, &rid)) {
+ return idmap_check_rid_is_in_free_range(rid);
+ }
+
+ return False;
+ }
+
+ return True;
+}
/*****************************************************************
Returns SID pointer.