From 03d5867d529f126da368ebda70bf2d997aa602e0 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Fri, 11 Jul 2003 05:33:40 +0000 Subject: moving more code around. * move rid allocation into IDMAP. See comments in _api_samr_create_user() * add winbind delete user/group functions I'm checking this in to sync up with everyone. But I'm going to split the add a separate winbindd_allocate_rid() function for systems that have an 'add user script' but need idmap to give them a RID. Life would be so much simplier without 'enable rid algorithm'. The current RID allocation is horrible due to this one fact. Tested idmap_tdb but not idmap_ldap yet. Will do that tomorrow. Nothing has changed in the way a samba domain is represented, stored, or search in the directory so things should be ok with previous installations. going to bed now. (This used to be commit 0463045cc7ff177fab44b25faffad5bf7140244d) --- source3/auth/auth_util.c | 6 +- source3/groupdb/mapping.c | 6 +- source3/include/idmap.h | 1 + source3/lib/smbldap.c | 148 ++++++++++++ source3/nsswitch/wb_client.c | 74 +++++- source3/nsswitch/wbinfo.c | 82 +++++-- source3/nsswitch/winbindd_acct.c | 226 ++++++++++++++++-- source3/nsswitch/winbindd_nss.h | 2 + source3/nsswitch/winbindd_user.c | 13 ++ source3/passdb/passdb.c | 171 +++----------- source3/passdb/pdb_ldap.c | 489 +-------------------------------------- source3/passdb/pdb_smbpasswd.c | 112 ++------- source3/passdb/pdb_tdb.c | 77 ++---- source3/rpc_server/srv_samr_nt.c | 35 ++- source3/sam/idmap.c | 15 +- source3/sam/idmap_ldap.c | 283 +++++++++++++++++++++- source3/sam/idmap_tdb.c | 41 +++- source3/sam/idmap_util.c | 114 +++++++++ source3/utils/net_rpc_samsync.c | 4 +- source3/utils/pdbedit.c | 2 +- 20 files changed, 1059 insertions(+), 842 deletions(-) (limited to 'source3') diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c index 51cd1994f9..3f9c120cfb 100644 --- a/source3/auth/auth_util.c +++ b/source3/auth/auth_util.c @@ -56,10 +56,12 @@ static int smb_create_user(const char *domain, const char *unix_username, const /**************************************************************************** Add and Delete UNIX users on demand, based on NTSTATUS codes. + We don't care about RID's here so ignore. ****************************************************************************/ void auth_add_user_script(const char *domain, const char *username) { + uint32 rid; /* * User validated ok against Domain controller. * If the admin wants us to try and create a UNIX @@ -75,8 +77,10 @@ void auth_add_user_script(const char *domain, const char *username) However, a host set for 'security = server' might run winbindd for account allocation */ - if ( !winbind_create_user(username) ) + if ( !winbind_create_user(username, NULL) ) { DEBUG(5,("auth_add_user_script: winbindd_create_user() failed\n")); + rid = 0; + } } } diff --git a/source3/groupdb/mapping.c b/source3/groupdb/mapping.c index e769b4dd9d..8a6f514860 100644 --- a/source3/groupdb/mapping.c +++ b/source3/groupdb/mapping.c @@ -756,7 +756,7 @@ int smb_create_group(char *unix_group, gid_t *new_gid) /* Try winbindd */ - if ( winbind_create_group( unix_group ) ) { + if ( winbind_create_group( unix_group, NULL ) ) { DEBUG(3,("smb_create_group: winbindd created the group (%s)\n", unix_group)); return 0; @@ -783,15 +783,13 @@ int smb_delete_group(char *unix_group) DEBUG(3,("smb_delete_group: Running the command `%s' gave %d\n",del_script,ret)); return ret; } -#if 0 + if ( winbind_delete_group( unix_group ) ) { DEBUG(3,("smb_delete_group: winbindd deleted the group (%s)\n", unix_group)); return 0; } -#endif - return -1; } diff --git a/source3/include/idmap.h b/source3/include/idmap.h index 7da3718d19..ae7e4e5101 100644 --- a/source3/include/idmap.h +++ b/source3/include/idmap.h @@ -42,6 +42,7 @@ struct idmap_methods { /* Called when backend is first loaded */ NTSTATUS (*init)( char *params ); + NTSTATUS (*allocate_rid)(uint32 *rid, int rid_type); NTSTATUS (*allocate_id)(unid_t *id, int id_type); NTSTATUS (*get_sid_from_id)(DOM_SID *sid, unid_t id, int id_type); NTSTATUS (*get_id_from_sid)(unid_t *id, int *id_type, const DOM_SID *sid); diff --git a/source3/lib/smbldap.c b/source3/lib/smbldap.c index cba73d386c..21e3383acd 100644 --- a/source3/lib/smbldap.c +++ b/source3/lib/smbldap.c @@ -1110,3 +1110,151 @@ NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, const char *location, struct smbldap_ return NT_STATUS_OK; } +/********************************************************************** + Add the sambaDomain to LDAP, so we don't have to search for this stuff + again. This is a once-add operation for now. + + TODO: Add other attributes, and allow modification. +*********************************************************************/ +static NTSTATUS add_new_domain_info(struct smbldap_state *ldap_state, + const char *domain_name) +{ + fstring sid_string; + fstring algorithmic_rid_base_string; + pstring filter, dn; + LDAPMod **mods = NULL; + int rc; + int ldap_op; + LDAPMessage *result = NULL; + int num_result; + char **attr_list; + + slprintf (filter, sizeof (filter) - 1, "(&(%s=%s)(objectclass=%s))", + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), + domain_name, LDAP_OBJ_DOMINFO); + + attr_list = get_attr_list( dominfo_attr_list ); + rc = smbldap_search_suffix(ldap_state, filter, attr_list, &result); + free_attr_list( attr_list ); + + if (rc != LDAP_SUCCESS) { + return NT_STATUS_UNSUCCESSFUL; + } + + num_result = ldap_count_entries(ldap_state->ldap_struct, result); + + if (num_result > 1) { + DEBUG (0, ("More than domain with that name exists: bailing out!\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Check if we need to add an entry */ + DEBUG(3,("Adding new domain\n")); + ldap_op = LDAP_MOD_ADD; + + snprintf(dn, sizeof(dn), "%s=%s,%s", get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), + domain_name, lp_ldap_suffix()); + + /* Free original search */ + ldap_msgfree(result); + + /* make the changes - the entry *must* not already have samba attributes */ + smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), + domain_name); + + /* If we don't have an entry, then ask secrets.tdb for what it thinks. + It may choose to make it up */ + + sid_to_string(sid_string, get_global_sam_sid()); + smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID), sid_string); + + slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string) - 1, "%i", algorithmic_rid_base()); + smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE), + algorithmic_rid_base_string); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_DOMINFO); + + switch(ldap_op) + { + case LDAP_MOD_ADD: + rc = smbldap_add(ldap_state, dn, mods); + break; + case LDAP_MOD_REPLACE: + rc = smbldap_modify(ldap_state, dn, mods); + break; + default: + DEBUG(0,("Wrong LDAP operation type: %d!\n", ldap_op)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (rc!=LDAP_SUCCESS) { + char *ld_error = NULL; + ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(1,("failed to %s domain dn= %s with: %s\n\t%s\n", + ldap_op == LDAP_MOD_ADD ? "add" : "modify", + dn, ldap_err2string(rc), + ld_error?ld_error:"unknown")); + SAFE_FREE(ld_error); + + ldap_mods_free(mods, True); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(2,("added: domain = %s in the LDAP database\n", domain_name)); + ldap_mods_free(mods, True); + return NT_STATUS_OK; +} + +/********************************************************************** +Search for the domain info entry +*********************************************************************/ +NTSTATUS smbldap_search_domain_info(struct smbldap_state *ldap_state, + LDAPMessage ** result, const char *domain_name, + BOOL try_add) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + pstring filter; + int rc; + char **attr_list; + int count; + + snprintf(filter, sizeof(filter)-1, "(&(objectClass=%s)(%s=%s))", + LDAP_OBJ_DOMINFO, + get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), + domain_name); + + DEBUG(2, ("Searching for:[%s]\n", filter)); + + + attr_list = get_attr_list( dominfo_attr_list ); + rc = smbldap_search_suffix(ldap_state, filter, attr_list , result); + free_attr_list( attr_list ); + + if (rc != LDAP_SUCCESS) { + DEBUG(2,("Problem during LDAPsearch: %s\n", ldap_err2string (rc))); + DEBUG(2,("Query was: %s, %s\n", lp_ldap_suffix(), filter)); + } else if (ldap_count_entries(ldap_state->ldap_struct, *result) < 1) { + DEBUG(3, ("Got no domain info entries for domain\n")); + ldap_msgfree(*result); + *result = NULL; + if (try_add && NT_STATUS_IS_OK(ret = add_new_domain_info(ldap_state, domain_name))) { + return smbldap_search_domain_info(ldap_state, result, domain_name, False); + } + else { + DEBUG(0, ("Adding domain info for %s failed with %s\n", + domain_name, nt_errstr(ret))); + return ret; + } + } else if ((count = ldap_count_entries(ldap_state->ldap_struct, *result)) > 1) { + DEBUG(0, ("Got too many (%d) domain info entries for domain %s\n", + count, domain_name)); + ldap_msgfree(*result); + *result = NULL; + return ret; + } else { + return NT_STATUS_OK; + } + + return ret; +} + diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c index 26a0e58191..eb9a7e9995 100644 --- a/source3/nsswitch/wb_client.c +++ b/source3/nsswitch/wb_client.c @@ -371,7 +371,7 @@ BOOL winbind_ping( void ) Ask winbindd to create a local user **********************************************************************/ -BOOL winbind_create_user( const char *name ) +BOOL winbind_create_user( const char *name, uint32 *rid ) { struct winbindd_request request; struct winbindd_response response; @@ -384,6 +384,11 @@ BOOL winbind_create_user( const char *name ) return False; DEBUG(10,("winbind_create_user: %s\n", name)); + + /* see if the caller wants a new RID returned */ + + if ( rid ) + request.flags = WBFLAG_ALLOCATE_RID; fstrcpy( request.data.acct_mgt.username, name ); fstrcpy( request.data.acct_mgt.groupname, "" ); @@ -392,6 +397,9 @@ BOOL winbind_create_user( const char *name ) result = winbindd_request( WINBINDD_CREATE_USER, &request, &response); + if ( rid ) + *rid = response.data.rid; + return result == NSS_STATUS_SUCCESS; } @@ -399,7 +407,7 @@ BOOL winbind_create_user( const char *name ) Ask winbindd to create a local group **********************************************************************/ -BOOL winbind_create_group( const char *name ) +BOOL winbind_create_group( const char *name, uint32 *rid ) { struct winbindd_request request; struct winbindd_response response; @@ -413,12 +421,20 @@ BOOL winbind_create_group( const char *name ) DEBUG(10,("winbind_create_group: %s\n", name)); + /* see if the caller wants a new RID returned */ + + if ( rid ) + request.flags = WBFLAG_ALLOCATE_RID; + fstrcpy( request.data.acct_mgt.groupname, name ); ZERO_STRUCT(response); result = winbindd_request( WINBINDD_CREATE_GROUP, &request, &response); + if ( rid ) + *rid = response.data.rid; + return result == NSS_STATUS_SUCCESS; } @@ -510,4 +526,58 @@ BOOL winbind_set_user_primary_group( const char *user, const char *group ) } +/********************************************************************** + Ask winbindd to remove a user from its lists of accounts +**********************************************************************/ + +BOOL winbind_delete_user( const char *user ) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + if ( !lp_winbind_enable_local_accounts() ) + return False; + + if ( !user ) + return False; + + DEBUG(10,("winbind_delete_user: user (%s)\n", user)); + + fstrcpy( request.data.acct_mgt.username, user ); + + ZERO_STRUCT(response); + + result = winbindd_request( WINBINDD_DELETE_USER, &request, &response); + + return result == NSS_STATUS_SUCCESS; +} + +/********************************************************************** + Ask winbindd to remove a group from its lists of accounts +**********************************************************************/ + +BOOL winbind_delete_group( const char *group ) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + if ( !lp_winbind_enable_local_accounts() ) + return False; + + if ( !group ) + return False; + + DEBUG(10,("winbind_delete_group: group (%s)\n", group)); + + fstrcpy( request.data.acct_mgt.groupname, group ); + + ZERO_STRUCT(response); + + result = winbindd_request( WINBINDD_DELETE_GROUP, &request, &response); + + return result == NSS_STATUS_SUCCESS; +} + diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index 6ebf6effa7..f533799370 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -526,16 +526,36 @@ static BOOL wbinfo_create_user(char *username) ZERO_STRUCT(request); ZERO_STRUCT(response); + request.flags = WBFLAG_ALLOCATE_RID; fstrcpy(request.data.acct_mgt.username, username); result = winbindd_request(WINBINDD_CREATE_USER, &request, &response); - if (response.data.auth.nt_status) - d_printf("error code was %s (0x%x)\nerror messsage was: %s\n", - response.data.auth.nt_status_string, - response.data.auth.nt_status, - response.data.auth.error_string); + if ( result == NSS_STATUS_SUCCESS ) + d_printf("New RID is %d\n", response.data.rid); + + return result == NSS_STATUS_SUCCESS; +} +/****************************************************************** + remove a winbindd user +******************************************************************/ + +static BOOL wbinfo_delete_user(char *username) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.acct_mgt.username, username); + + result = winbindd_request(WINBINDD_DELETE_USER, &request, &response); + return result == NSS_STATUS_SUCCESS; } @@ -558,12 +578,28 @@ static BOOL wbinfo_create_group(char *groupname) result = winbindd_request(WINBINDD_CREATE_GROUP, &request, &response); - if (response.data.auth.nt_status) - d_printf("error code was %s (0x%x)\nerror messsage was: %s\n", - response.data.auth.nt_status_string, - response.data.auth.nt_status, - response.data.auth.error_string); + return result == NSS_STATUS_SUCCESS; +} +/****************************************************************** + remove a winbindd group +******************************************************************/ + +static BOOL wbinfo_delete_group(char *groupname) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.acct_mgt.groupname, groupname); + + result = winbindd_request(WINBINDD_DELETE_GROUP, &request, &response); + return result == NSS_STATUS_SUCCESS; } @@ -614,12 +650,6 @@ static BOOL wbinfo_add_user_to_group(char *string) result = winbindd_request(WINBINDD_ADD_USER_TO_GROUP, &request, &response); - if (response.data.auth.nt_status) - d_printf("error code was %s (0x%x)\nerror messsage was: %s\n", - response.data.auth.nt_status_string, - response.data.auth.nt_status, - response.data.auth.error_string); - return result == NSS_STATUS_SUCCESS; } @@ -647,12 +677,6 @@ static BOOL wbinfo_remove_user_from_group(char *string) result = winbindd_request(WINBINDD_REMOVE_USER_FROM_GROUP, &request, &response); - if (response.data.auth.nt_status) - d_printf("error code was %s (0x%x)\nerror messsage was: %s\n", - response.data.auth.nt_status_string, - response.data.auth.nt_status, - response.data.auth.error_string); - return result == NSS_STATUS_SUCCESS; } @@ -851,7 +875,9 @@ int main(int argc, char **argv) { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" }, { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" }, { "create-user", 'c', POPT_ARG_STRING, &string_arg, 'c', "Create a local user account", "name" }, + { "delete-user", 'x', POPT_ARG_STRING, &string_arg, 'x', "Delete a local user account", "name" }, { "create-group", 'C', POPT_ARG_STRING, &string_arg, 'C', "Create a local group", "name" }, + { "delete-group", 'X', POPT_ARG_STRING, &string_arg, 'X', "Delete a local group", "name" }, { "add-to-group", 'o', POPT_ARG_STRING, &string_arg, 'o', "Add user to group", "user:group" }, { "del-from-group", 'O', POPT_ARG_STRING, &string_arg, 'O', "Remove user from group", "user:group" }, { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" }, @@ -1036,6 +1062,18 @@ int main(int argc, char **argv) goto done; } break; + case 'x': + if ( !wbinfo_delete_user(string_arg) ) { + d_printf("Could not delete user account\n"); + goto done; + } + break; + case 'X': + if ( !wbinfo_delete_group(string_arg) ) { + d_printf("Could not delete group\n"); + goto done; + } + break; case 'P': if (!wbinfo_ping()) { d_printf("could not ping winbindd!\n"); diff --git a/source3/nsswitch/winbindd_acct.c b/source3/nsswitch/winbindd_acct.c index 2a3a6eb2bb..a1cd1d5f19 100644 --- a/source3/nsswitch/winbindd_acct.c +++ b/source3/nsswitch/winbindd_acct.c @@ -37,6 +37,29 @@ static TDB_CONTEXT *account_tdb; extern userdom_struct current_user_info; +struct _check_primary_grp { + gid_t gid; + BOOL found; +}; + +/********************************************************************** +**********************************************************************/ + +static void free_winbindd_gr( WINBINDD_GR *grp ) +{ + int i; + + if ( !grp ) + return; + + for ( i=0; inum_gr_mem; i++ ) + SAFE_FREE( grp->gr_mem[i] ); + + SAFE_FREE( grp->gr_mem ); + + return; +} + /***************************************************************************** Initialise auto-account database. *****************************************************************************/ @@ -649,8 +672,17 @@ static BOOL wb_delgrpmember( WINBINDD_GR *grp, const char *user ) if ( !found ) return False; - memmove( grp->gr_mem[i], grp->gr_mem[i+1], sizeof(char*)*(grp->num_gr_mem-(i+1)) ); - grp->num_gr_mem--; + /* still some remaining members */ + + if ( grp->num_gr_mem > 1 ) { + memmove( grp->gr_mem[i], grp->gr_mem[i+1], sizeof(char*)*(grp->num_gr_mem-(i+1)) ); + grp->num_gr_mem--; + } + else { /* last one */ + free_winbindd_gr( grp ); + grp->gr_mem = NULL; + grp->num_gr_mem = 0; + } return True; } @@ -658,34 +690,59 @@ static BOOL wb_delgrpmember( WINBINDD_GR *grp, const char *user ) /********************************************************************** **********************************************************************/ -static void free_winbindd_gr( WINBINDD_GR *grp ) +static int cleangroups_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) { - int i; - - if ( !grp ) - return; + int len; + fstring key; + char *name = (char*)state; + + snprintf( key, sizeof(key), "%s/NAME", WBKEY_GROUP ); + len = strlen(key); + + /* if this is a group entry then, check the members */ + + if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) { + WINBINDD_GR *grp; - for ( i=0; inum_gr_mem; i++ ) - SAFE_FREE( grp->gr_mem[i] ); + if ( !(grp = string2group( dbuf.dptr )) ) { + DEBUG(0,("cleangroups_traverse_fn: Failure to parse [%s]\n", + dbuf.dptr)); + return 0; + } + + /* just try to delete the user and rely on wb_delgrpmember() + to tell you whether or not the group changed. This is more + effecient than testing group membership first since the + checks for deleting a user from a group is essentially the + same as checking if he/she is a member */ + + if ( wb_delgrpmember( grp, name ) ) { + DEBUG(10,("cleanupgroups_traverse_fn: Removed user (%s) from group (%s)\n", + name, grp->gr_name)); + wb_storegrnam( grp ); + } + + free_winbindd_gr( grp ); + } - SAFE_FREE( grp->gr_mem ); - - return; + return 0; } /********************************************************************** **********************************************************************/ -static BOOL wb_delete_user( const char *name) +static BOOL wb_delete_user( WINBINDD_PW *pw) { char *namekey; + char *uidkey; if ( !account_tdb && !winbindd_accountdb_init() ) { - DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n")); + DEBUG(0,("wb_delete_user: Failed to open winbindd account db\n")); return False; } - namekey = acct_userkey_byname( name ); + namekey = acct_userkey_byname( pw->pw_name ); /* lock the main entry first */ @@ -694,20 +751,101 @@ static BOOL wb_delete_user( const char *name) return False; } + /* remove user from all groups */ + + tdb_traverse(account_tdb, cleangroups_traverse_fn, (void *)pw->pw_name); + + /* remove the user */ + uidkey = acct_userkey_byuid( pw->pw_uid ); tdb_delete_bystring( account_tdb, namekey ); + tdb_delete_bystring( account_tdb, uidkey ); + tdb_unlock_bystring( account_tdb, namekey ); return True; } +/********************************************************************** +**********************************************************************/ + +static int isprimarygroup_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, + TDB_DATA dbuf, void *params) +{ + int len; + fstring key; + struct _check_primary_grp *check = (struct _check_primary_grp*)params; + + snprintf( key, sizeof(key), "%s/NAME", WBKEY_PASSWD ); + len = strlen(key); + + /* if this is a group entry then, check the members */ + + if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) { + WINBINDD_PW *pw;; + + if ( !(pw = string2passwd( dbuf.dptr )) ) { + DEBUG(0,("isprimarygroup_traverse_fn: Failure to parse [%s]\n", + dbuf.dptr)); + return 0; + } + + if ( check->gid == pw->pw_gid ) { + check->found = True; + return 1; + } + } + + return 0; +} + /********************************************************************** **********************************************************************/ -static BOOL wb_delete_group( const char *name) +static BOOL wb_delete_group( WINBINDD_GR *grp ) { - return False; + struct _check_primary_grp check; + char *namekey; + char *gidkey; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_delete_group: Failed to open winbindd account db\n")); + return False; + } + + /* lock the main entry first */ + + namekey = acct_groupkey_byname( grp->gr_name ); + if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) { + DEBUG(0,("wb_delete_group: Failed to lock %s\n", namekey)); + return False; + } + + /* is this group the primary group for any user? If + so deny delete */ + + check.found = False; + tdb_traverse(account_tdb, isprimarygroup_traverse_fn, (void *)&check); + + if ( check.found ) { + DEBUG(4,("wb_delete_group: Cannot delete group (%s) since it " + "is the primary group for some users\n", grp->gr_name)); + return False; + } + + /* We're clear. Delete the group */ + + DEBUG(5,("wb_delete_group: Removing group (%s)\n", grp->gr_name)); + + gidkey = acct_groupkey_bygid( grp->gr_gid ); + + tdb_delete_bystring( account_tdb, namekey ); + tdb_delete_bystring( account_tdb, gidkey ); + + tdb_unlock_bystring( account_tdb, namekey ); + + return True; } /********************************************************************** @@ -722,6 +860,8 @@ enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state) WINBINDD_GR *wb_grp; struct group *unix_grp; gid_t primary_gid; + uint32 flags = state->request.flags; + uint32 rid; if ( !state->privileged ) { DEBUG(2, ("winbindd_create_user: non-privileged access denied!\n")); @@ -781,8 +921,27 @@ enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state) pw.pw_uid = id.uid; pw.pw_gid = primary_gid; + + /* store the new entry */ + + if ( !wb_storepwnam(&pw) ) + return WINBINDD_ERROR; + + /* do we need a new RID? */ + + if ( flags & WBFLAG_ALLOCATE_RID ) { + if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, USER_RID_TYPE)) ) { + DEBUG(0,("winbindd_create_user: RID allocation failure! Cannot create user (%s)\n", + user)); + wb_delete_user( &pw ); + + return WINBINDD_ERROR; + } + + state->response.data.rid = rid; + } - return ( wb_storepwnam(&pw) ? WINBINDD_OK : WINBINDD_ERROR ); + return WINBINDD_OK; } /********************************************************************** @@ -794,6 +953,8 @@ enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state) char *group; unid_t id; WINBINDD_GR grp; + uint32 flags = state->request.flags; + uint32 rid; if ( !state->privileged ) { DEBUG(2, ("winbindd_create_group: non-privileged access denied!\n")); @@ -821,8 +982,25 @@ enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state) grp.gr_gid = id.gid; grp.gr_mem = NULL; /* start with no members */ grp.num_gr_mem = 0; + + if ( !wb_storegrnam(&grp) ) + return WINBINDD_ERROR; + + /* do we need a new RID? */ + + if ( flags & WBFLAG_ALLOCATE_RID ) { + if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, GROUP_RID_TYPE)) ) { + DEBUG(0,("winbindd_create_group: RID allocation failure! Cannot create group (%s)\n", + group)); + wb_delete_group( &grp ); + + return WINBINDD_ERROR; + } + + state->response.data.rid = rid; + } - return ( wb_storegrnam(&grp) ? WINBINDD_OK : WINBINDD_ERROR ); + return WINBINDD_OK; } /********************************************************************** @@ -989,8 +1167,7 @@ enum winbindd_result winbindd_delete_user(struct winbindd_cli_state *state) return WINBINDD_ERROR; } - - return ( wb_delete_user(user) ? WINBINDD_OK : WINBINDD_ERROR ); + return ( wb_delete_user(pw) ? WINBINDD_OK : WINBINDD_ERROR ); } /********************************************************************** @@ -1001,6 +1178,7 @@ enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state) { WINBINDD_GR *grp; char *group; + BOOL ret; if ( !state->privileged ) { DEBUG(2, ("winbindd_delete_group: non-privileged access denied!\n")); @@ -1016,13 +1194,15 @@ enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state) /* make sure it is a valid group */ if ( !(grp = wb_getgrnam( group )) ) { - DEBUG(4,("winbindd_delete_user: Cannot delete a non-existent group\n")); + DEBUG(4,("winbindd_delete_group: Cannot delete a non-existent group\n")); return WINBINDD_ERROR; } + ret = wb_delete_group(grp); + free_winbindd_gr( grp ); - return ( wb_delete_group(group) ? WINBINDD_OK : WINBINDD_ERROR ); + return ( ret ? WINBINDD_OK : WINBINDD_ERROR ); } diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index a2d9e82c7c..c4407bbe31 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -148,6 +148,7 @@ typedef struct winbindd_gr { #define WBFLAG_PAM_LMKEY 0x0008 #define WBFLAG_PAM_CONTACT_TRUSTDOM 0x0010 #define WBFLAG_QUERY_ONLY 0x0020 +#define WBFLAG_ALLOCATE_RID 0x0040 /* Winbind request structure */ @@ -257,6 +258,7 @@ struct winbindd_response { char nt_session_key[16]; char first_8_lm_hash[8]; } auth; + uint32 rid; /* create user or group */ } data; /* Variable length return data */ diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c index 7c95ba8470..c49c41687b 100644 --- a/source3/nsswitch/winbindd_user.c +++ b/source3/nsswitch/winbindd_user.c @@ -308,6 +308,19 @@ enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state) free_getent_state(state->getpwent_state); state->getpwent_state = NULL; } + +#if 0 /* JERRY */ + /* add any local users we have */ + + if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL ) + return WINBINDD_ERROR; + + ZERO_STRUCTP(domain_state); + + /* Add to list of open domains */ + + DLIST_ADD(state->getpwent_state, domain_state); +#endif /* Create sam pipes for each domain we know about */ diff --git a/source3/passdb/passdb.c b/source3/passdb/passdb.c index a2623ff3d7..408695d4bd 100644 --- a/source3/passdb/passdb.c +++ b/source3/passdb/passdb.c @@ -290,41 +290,37 @@ NTSTATUS pdb_init_sam_pw(SAM_ACCOUNT **new_sam_acct, const struct passwd *pwd) /************************************************************* Initialises a SAM_ACCOUNT ready to add a new account, based - on the unix user if possible. + on the UNIX user. Pass in a RID if you have one ************************************************************/ -NTSTATUS pdb_init_sam_new(SAM_ACCOUNT **new_sam_acct, const char *username) +NTSTATUS pdb_init_sam_new(SAM_ACCOUNT **new_sam_acct, const char *username, + uint32 rid) { - NTSTATUS nt_status = NT_STATUS_NO_MEMORY; - - struct passwd *pwd; + NTSTATUS nt_status = NT_STATUS_NO_MEMORY; + struct passwd *pwd; + BOOL ret; pwd = Get_Pwnam(username); - if (pwd) { - if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(new_sam_acct, pwd))) { - *new_sam_acct = NULL; - return nt_status; - } - } else { - DOM_SID g_sid; - if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(new_sam_acct))) { - *new_sam_acct = NULL; - return nt_status; - } - if (!pdb_set_username(*new_sam_acct, username, PDB_SET)) { - pdb_free_sam(new_sam_acct); - return nt_status; - } - - pdb_set_domain (*new_sam_acct, get_global_sam_name(), PDB_DEFAULT); - - /* set Domain Users by default ! */ - sid_copy(&g_sid, get_global_sam_sid()); - sid_append_rid(&g_sid, DOMAIN_GROUP_RID_USERS); - pdb_set_group_sid(*new_sam_acct, &g_sid, PDB_SET); + if (!pwd) + return NT_STATUS_NO_SUCH_USER; + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(new_sam_acct, pwd))) { + *new_sam_acct = NULL; + return nt_status; } - return NT_STATUS_OK; + + /* see if we need to generate a new rid using the 2.2 algorithm */ + if ( rid == 0 && lp_enable_rid_algorithm() ) { + DEBUG(10,("pdb_init_sam_new: no RID specified. Generating one via old algorithm\n")); + rid = fallback_pdb_uid_to_user_rid(pwd->pw_uid); + } + + /* set the new SID */ + + ret = pdb_set_user_sid_from_rid( *new_sam_acct, rid, PDB_SET ); + + return (ret ? NT_STATUS_OK : NT_STATUS_NO_SUCH_USER); } @@ -920,8 +916,8 @@ BOOL local_password_change(const char *user_name, int local_flags, pdb_free_sam(&sam_pass); if ((local_flags & LOCAL_ADD_USER) || (local_flags & LOCAL_DELETE_USER)) { - /* Might not exist in /etc/passwd */ - if (!NT_STATUS_IS_OK(pdb_init_sam_new(&sam_pass, user_name))) { + /* Might not exist in /etc/passwd. Use rid algorithm here */ + if (!NT_STATUS_IS_OK(pdb_init_sam_new(&sam_pass, user_name, 0))) { slprintf(err_str, err_str_len-1, "Failed initialise SAM_ACCOUNT for user %s.\n", user_name); return False; } @@ -1247,121 +1243,6 @@ BOOL local_sid_to_gid(gid_t *pgid, const DOM_SID *psid, enum SID_NAME_USE *name_ return True; } -/********************************************************************** -**********************************************************************/ - -BOOL pdb_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 pdb_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 (!pdb_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 pdb_get_free_rid_base(void) -{ - uint32 low, high; - if (pdb_get_free_rid_range(&low, &high)) { - return low; - } - return 0; -} - -/********************************************************************** -**********************************************************************/ - -BOOL pdb_check_ugid_is_in_free_range(uint32 id) -{ - uint32 low, high; - - if (!pdb_get_free_ugid_range(&low, &high)) { - return False; - } - if (id < low || id > high) { - return False; - } - return True; -} - -/********************************************************************** -**********************************************************************/ - -BOOL pdb_check_rid_is_in_free_range(uint32 rid) -{ - uint32 low, high; - - if (!pdb_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 pdb_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 pdb_check_rid_is_in_free_range(rid); - } - - return False; - } - - return True; -} - /********************************************************************** Marshall/unmarshall SAM_ACCOUNT structs. *********************************************************************/ diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c index 886fd809f3..e32f566a7d 100644 --- a/source3/passdb/pdb_ldap.c +++ b/source3/passdb/pdb_ldap.c @@ -96,15 +96,6 @@ struct ldapsam_privates { /* configuration items */ int schema_ver; - - BOOL permit_non_unix_accounts; - - uint32 low_allocated_user_rid; - uint32 high_allocated_user_rid; - - uint32 low_allocated_group_rid; - uint32 high_allocated_group_rid; - }; /********************************************************************** @@ -338,423 +329,7 @@ static NTSTATUS ldapsam_delete_entry(struct ldapsam_privates *ldap_state, ldap_memfree(dn); return NT_STATUS_OK; } - -/********************************************************************** - Add the sambaDomain to LDAP, so we don't have to search for this stuff - again. This is a once-add operation for now. - - TODO: Add other attributes, and allow modification. -*********************************************************************/ -static NTSTATUS add_new_domain_info(struct ldapsam_privates *ldap_state) -{ - fstring sid_string; - fstring algorithmic_rid_base_string; - pstring filter, dn; - LDAPMod **mods = NULL; - int rc; - int ldap_op; - LDAPMessage *result = NULL; - int num_result; - char **attr_list; - - slprintf (filter, sizeof (filter) - 1, "(&(%s=%s)(objectclass=%s))", - get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), - ldap_state->domain_name, LDAP_OBJ_DOMINFO); - - attr_list = get_attr_list( dominfo_attr_list ); - rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, - attr_list, &result); - free_attr_list( attr_list ); - - if (rc != LDAP_SUCCESS) { - return NT_STATUS_UNSUCCESSFUL; - } - - num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result); - - if (num_result > 1) { - DEBUG (0, ("More than domain with that name exists: bailing out!\n")); - ldap_msgfree(result); - return NT_STATUS_UNSUCCESSFUL; - } - - /* Check if we need to add an entry */ - DEBUG(3,("Adding new domain\n")); - ldap_op = LDAP_MOD_ADD; - - snprintf(dn, sizeof(dn), "%s=%s,%s", get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), - ldap_state->domain_name, lp_ldap_suffix()); - - /* Free original search */ - ldap_msgfree(result); - - /* make the changes - the entry *must* not already have samba attributes */ - smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), - ldap_state->domain_name); - - /* If we don't have an entry, then ask secrets.tdb for what it thinks. - It may choose to make it up */ - - sid_to_string(sid_string, get_global_sam_sid()); - smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID), sid_string); - - slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string) - 1, "%i", algorithmic_rid_base()); - smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE), - algorithmic_rid_base_string); - smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_DOMINFO); - - switch(ldap_op) - { - case LDAP_MOD_ADD: - rc = smbldap_add(ldap_state->smbldap_state, dn, mods); - break; - case LDAP_MOD_REPLACE: - rc = smbldap_modify(ldap_state->smbldap_state, dn, mods); - break; - default: - DEBUG(0,("Wrong LDAP operation type: %d!\n", ldap_op)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (rc!=LDAP_SUCCESS) { - char *ld_error = NULL; - ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, - &ld_error); - DEBUG(1, - ("failed to %s domain dn= %s with: %s\n\t%s\n", - ldap_op == LDAP_MOD_ADD ? "add" : "modify", - dn, ldap_err2string(rc), - ld_error?ld_error:"unknown")); - SAFE_FREE(ld_error); - - ldap_mods_free(mods, True); - return NT_STATUS_UNSUCCESSFUL; - } - - DEBUG(2,("added: domain = %s in the LDAP database\n", ldap_state->domain_name)); - ldap_mods_free(mods, True); - return NT_STATUS_OK; -} - -/********************************************************************** -Search for the domain info entry -*********************************************************************/ -static NTSTATUS ldapsam_search_domain_info(struct ldapsam_privates *ldap_state, - LDAPMessage ** result, BOOL try_add) -{ - NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - pstring filter; - int rc; - char **attr_list; - int count; - - snprintf(filter, sizeof(filter)-1, "(&(objectClass=%s)(%s=%s))", - LDAP_OBJ_DOMINFO, - get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN), - ldap_state->domain_name); - - DEBUG(2, ("Searching for:[%s]\n", filter)); - - - attr_list = get_attr_list( dominfo_attr_list ); - rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, - attr_list , result); - free_attr_list( attr_list ); - - if (rc != LDAP_SUCCESS) { - DEBUG(2,("Problem during LDAPsearch: %s\n", ldap_err2string (rc))); - DEBUG(2,("Query was: %s, %s\n", lp_ldap_suffix(), filter)); - } else if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, *result) < 1) { - DEBUG(3, ("Got no domain info entries for domain %s\n", - ldap_state->domain_name)); - ldap_msgfree(*result); - *result = NULL; - if (try_add && NT_STATUS_IS_OK(ret = add_new_domain_info(ldap_state))) { - return ldapsam_search_domain_info(ldap_state, result, False); - } else { - DEBUG(0, ("Adding domain info failed with %s\n", nt_errstr(ret))); - return ret; - } - } else if ((count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, *result)) > 1) { - DEBUG(0, ("Got too many (%d) domain info entries for domain %s\n", - count, ldap_state->domain_name)); - ldap_msgfree(*result); - *result = NULL; - return ret; - } else { - return NT_STATUS_OK; - } - - return ret; -} - -/********************************************************************** - 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 ldapsam_privates *ldap_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(ldap_state->smbldap_state, - filter, sid_attr, &result); - - if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - ldap_get_option(ldap_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(ldap_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 ldapsam_next_rid(struct ldapsam_privates *ldap_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; - - if ( ldap_state->schema_ver != SCHEMAVER_SAMBASAMACCOUNT ) { - DEBUG(0, ("Allocated RIDs require the %s objectclass used by 'ldapsam'\n", - LDAP_OBJ_SAMBASAMACCOUNT)); - return NT_STATUS_UNSUCCESSFUL; - } - - while (attempts < 10) - { - if (!NT_STATUS_IS_OK(ret = ldapsam_search_domain_info(ldap_state, &domain_result, True))) { - return ret; - } - - entry = ldap_first_entry(ldap_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(ldap_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(ldap_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(ldap_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(ldap_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(ldap_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(ldap_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 = ldap_state->low_allocated_user_rid; - } - break; - case GROUP_RID_TYPE: - if (smbldap_get_single_attribute(ldap_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 = ldap_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 > ldap_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(ldap_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 > ldap_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(ldap_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(ldap_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(ldap_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(ldap_state, &sid, &error)) { - if (error) { - return ret; - } - continue; - } - - return NT_STATUS_OK; - } - - ld_error = NULL; - ldap_get_option(ldap_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; -} + /* New Interface is being implemented here */ @@ -1192,34 +767,6 @@ static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state, DEBUG(2, ("Setting entry for user: %s\n", pdb_get_username(sampass))); - if (pdb_get_init_flags(sampass, PDB_USERSID) == PDB_DEFAULT) { - if (ldap_state->permit_non_unix_accounts) { - if (!NT_STATUS_IS_OK(ldapsam_next_rid(ldap_state, &rid, USER_RID_TYPE))) { - DEBUG(0, ("NO user RID specified on account %s, and " - "finding next available NUA RID failed, " - "cannot store!\n", - pdb_get_username(sampass))); - ldap_mods_free(*mods, True); - return False; - } - } else { - DEBUG(0, ("NO user RID specified on account %s, " - "cannot store!\n", pdb_get_username(sampass))); - ldap_mods_free(*mods, True); - return False; - } - - /* now that we have figured out the RID, always store it, as - the schema requires it (either as a SID or a RID) */ - - if (!pdb_set_user_sid_from_rid(sampass, rid, PDB_CHANGED)) { - DEBUG(0, ("Could not store RID back onto SAM_ACCOUNT for user %s!\n", - pdb_get_username(sampass))); - ldap_mods_free(*mods, True); - return False; - } - } - /* only update the RID if we actually need to */ if (need_update(sampass, PDB_USERSID)) { @@ -2733,10 +2280,8 @@ static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_met struct ldapsam_privates *ldap_state; uint32 alg_rid_base; pstring alg_rid_base_string; - uint32 low_idmap_uid, high_idmap_uid; - uint32 low_idmap_gid, high_idmap_gid; - LDAPMessage *result; - LDAPMessage *entry; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; DOM_SID ldap_domain_sid; DOM_SID secrets_domain_sid; pstring domain_sid_string; @@ -2748,14 +2293,17 @@ static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_met (*pdb_method)->name = "ldapsam"; ldap_state = (*pdb_method)->private_data; - ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT; - ldap_state->permit_non_unix_accounts = False; + ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT; /* Try to setup the Domain Name, Domain SID, algorithmic rid base */ - - if (!NT_STATUS_IS_OK(nt_status = ldapsam_search_domain_info(ldap_state, &result, True))) { + + nt_status = smbldap_search_domain_info(ldap_state->smbldap_state, &result, + ldap_state->domain_name, True); + + if ( !NT_STATUS_IS_OK(nt_status) ) { DEBUG(2, ("WARNING: Could not get domain info, nor add one to the domain\n")); - DEBUGADD(2, ("Continuing on regardless, will be unable to allocate new users/groups, and will risk BDCs having inconsistant SIDs\n")); + DEBUGADD(2, ("Continuing on regardless, will be unable to allocate new users/groups, " + "and will risk BDCs having inconsistant SIDs\n")); sid_copy(&ldap_state->domain_sid, get_global_sam_sid()); return NT_STATUS_OK; } @@ -2796,21 +2344,6 @@ static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_met } } ldap_msgfree(result); - - /* check for non-unix account ranges */ - - if (lp_idmap_uid(&low_idmap_uid, &high_idmap_uid) - && lp_idmap_gid(&low_idmap_gid, &high_idmap_gid)) - { - DEBUG(2, ("Enabling non-unix account ranges\n")); - - ldap_state->permit_non_unix_accounts = True; - - ldap_state->low_allocated_user_rid = fallback_pdb_uid_to_user_rid(low_idmap_uid); - ldap_state->high_allocated_user_rid = fallback_pdb_uid_to_user_rid(high_idmap_uid); - ldap_state->low_allocated_group_rid = pdb_gid_to_group_rid(low_idmap_gid); - ldap_state->high_allocated_group_rid = pdb_gid_to_group_rid(high_idmap_gid); - } return NT_STATUS_OK; } diff --git a/source3/passdb/pdb_smbpasswd.c b/source3/passdb/pdb_smbpasswd.c index 0e7dd77b40..055e8e71ba 100644 --- a/source3/passdb/pdb_smbpasswd.c +++ b/source3/passdb/pdb_smbpasswd.c @@ -1,10 +1,10 @@ /* * Unix SMB/CIFS implementation. * SMB parameters and setup - * Copyright (C) Andrew Tridgell 1992-1998 - * Modified by Jeremy Allison 1995. - * Modified by Gerald (Jerry) Carter 2000-2001 - * Modified by Andrew Bartlett 2002. + * Copyright (C) Andrew Tridgell 1992-1998 + * Modified by Jeremy Allison 1995. + * Modified by Gerald (Jerry) Carter 2000-2001,2003 + * Modified by Andrew Bartlett 2002. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free @@ -34,14 +34,13 @@ struct smb_passwd { - BOOL smb_userid_set; /* this is actually the unix uid_t */ - uint32 smb_userid; /* this is actually the unix uid_t */ + uint32 smb_userid; /* this is actually the unix uid_t */ const char *smb_name; /* username string */ - const unsigned char *smb_passwd; /* Null if no password */ + const unsigned char *smb_passwd; /* Null if no password */ const unsigned char *smb_nt_passwd; /* Null if no password */ - uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */ + uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */ time_t pass_last_set_time; /* password last set time */ }; @@ -61,12 +60,6 @@ struct smbpasswd_privates /* retrive-once info */ const char *smbpasswd_file; - - BOOL permit_non_unix_accounts; - - uid_t low_nua_userid; - uid_t high_nua_userid; - }; enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE }; @@ -591,28 +584,6 @@ static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, str /* Ok - entry doesn't exist. We can add it */ - /* Account not in /etc/passwd hack!!! */ - if (!newpwd->smb_userid_set) { - if (!smbpasswd_state->permit_non_unix_accounts) { - DEBUG(0, ("add_smbfilepwd_entry: cannot add account %s without unix identity\n", newpwd->smb_name)); - endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth)); - return False; - } - - if (max_found_uid < smbpasswd_state->low_nua_userid) { - newpwd->smb_userid = smbpasswd_state->low_nua_userid; - newpwd->smb_userid_set = True; - } else if (max_found_uid >= smbpasswd_state->high_nua_userid) { - DEBUG(0, ("add_smbfilepwd_entry: cannot add machine %s, no uids are free! \n", newpwd->smb_name)); - endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth)); - return False; - } else { - newpwd->smb_userid = max_found_uid + 1; - newpwd->smb_userid_set = True; - } - } - - /* Create a new smb passwd entry and set it to the given password. */ /* * The add user write needs to be atomic - so get the fd from @@ -1149,12 +1120,10 @@ static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampas DEBUG(0, ("Could not find gest account via getpwnam()! (%s)\n", lp_guestaccount())); return False; } - smb_pw->smb_userid_set = True; smb_pw->smb_userid=passwd->pw_uid; passwd_free(&passwd); } else if (fallback_pdb_rid_is_user(rid)) { - smb_pw->smb_userid_set = True; smb_pw->smb_userid=fallback_pdb_user_rid_to_uid(rid); } else { DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n")); @@ -1170,25 +1139,6 @@ static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampas smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass); smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass); -#if 0 - /* - * ifdef'out by JFM on 11/29/2001. - * this assertion is no longer valid - * and I don't understand the goal - * and doing the same thing with the group mapping code - * is hairy ! - * - * We just have the RID, in which SID is it valid ? - * our domain SID ? well known SID ? local SID ? - */ - - if (gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) { - DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n")); - DEBUG(0,("build_sam_pass: %d %d %d. \n", *gid, pdb_group_rid_to_gid(pdb_get_group_rid(sampass)), pdb_get_group_rid(sampass))); - return False; - } -#endif - return True; } @@ -1204,49 +1154,28 @@ static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n")); return False; } - - pwfile = getpwnam_alloc(pw_buf->smb_name); - if (pwfile == NULL) { - if ((smbpasswd_state->permit_non_unix_accounts) - && (pw_buf->smb_userid >= smbpasswd_state->low_nua_userid) - && (pw_buf->smb_userid <= smbpasswd_state->high_nua_userid)) { - pdb_set_user_sid_from_rid(sam_pass, fallback_pdb_uid_to_user_rid (pw_buf->smb_userid), PDB_SET); - - /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. - - This was down the bottom for machines, but it looks pretty good as - a general default for non-unix users. --abartlet 2002-01-08 - */ - pdb_set_group_sid_from_rid (sam_pass, DOMAIN_GROUP_RID_USERS, PDB_SET); - pdb_set_username (sam_pass, pw_buf->smb_name, PDB_SET); - pdb_set_domain (sam_pass, get_global_sam_name(), PDB_DEFAULT); + /* verify the user account exists */ - } else { - DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid %u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid)); - return False; - } - } else { - if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) { + if ( !(pwfile = getpwnam_alloc(pw_buf->smb_name)) ) { + DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid " + "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid)); return False; - } - - passwd_free(&pwfile); } + if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) + return False; + + passwd_free(&pwfile); + + /* set remaining fields */ + pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET); pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET); pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET); pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET); pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET); -#if 0 /* JERRY */ - /* the smbpasswd format doesn't have a must change time field, so - we can't get this right. The best we can do is to set this to - some time in the future. 21 days seems as reasonable as any other value :) - */ - pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE, PDB_DEFAULT); -#endif return True; } @@ -1558,11 +1487,6 @@ static NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_m (*pdb_method)->free_private_data = free_private_data; - if (lp_idmap_uid(&privates->low_nua_userid, &privates->high_nua_userid)) { - DEBUG(3, ("idmap uid range defined, non unix accounts enabled\n")); - privates->permit_non_unix_accounts = True; - } - return NT_STATUS_OK; } diff --git a/source3/passdb/pdb_tdb.c b/source3/passdb/pdb_tdb.c index a166697b4b..6f5d348ce1 100644 --- a/source3/passdb/pdb_tdb.c +++ b/source3/passdb/pdb_tdb.c @@ -1,11 +1,11 @@ /* * Unix SMB/CIFS implementation. * SMB parameters and setup - * Copyright (C) Andrew Tridgell 1992-1998 - * Copyright (C) Simo Sorce 2000-2002 - * Copyright (C) Gerald Carter 2000 - * Copyright (C) Jeremy Allison 2001 - * Copyright (C) Andrew Bartlett 2002 + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Simo Sorce 2000-2002 + * Copyright (C) Gerald Carter 2000 + * Copyright (C) Jeremy Allison 2001 + * Copyright (C) Andrew Bartlett 2002 * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free @@ -48,13 +48,6 @@ struct tdbsam_privates { /* retrive-once info */ const char *tdbsam_location; - - BOOL permit_non_unix_accounts; - - BOOL algorithmic_rids; - - uint32 low_nua_rid; - uint32 high_nua_rid; }; /*************************************************************** @@ -342,61 +335,31 @@ static BOOL tdb_update_sam(struct pdb_methods *my_methods, SAM_ACCOUNT* newpwd, fstring name; BOOL ret = True; uint32 user_rid; - BOOL tdb_ret; /* invalidate the existing TDB iterator if it is open */ + if (tdb_state->passwd_tdb) { tdb_close(tdb_state->passwd_tdb); tdb_state->passwd_tdb = NULL; } /* open the account TDB passwd*/ + pwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0600); - if (!pwd_tdb) - { - DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd (%s)!\n", tdb_state->tdbsam_location)); + + if (!pwd_tdb) { + DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd (%s)!\n", + tdb_state->tdbsam_location)); return False; } if (!pdb_get_group_rid(newpwd)) { - DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n",pdb_get_username(newpwd))); + DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n", + pdb_get_username(newpwd))); ret = False; goto done; } - /* if flag == TDB_INSERT then make up a new RID else throw an error. */ - if (!(user_rid = pdb_get_user_rid(newpwd))) { - if ((flag & TDB_INSERT) && tdb_state->permit_non_unix_accounts) { - uint32 lowrid, highrid; - if (!pdb_get_free_rid_range(&lowrid, &highrid)) { - /* should never happen */ - DEBUG(0, ("tdbsam: something messed up, no high/low rids but nua enabled ?!\n")); - ret = False; - goto done; - } - user_rid = lowrid; - tdb_ret = tdb_change_uint32_atomic(pwd_tdb, "RID_COUNTER", &user_rid, RID_MULTIPLIER); - if (!tdb_ret) { - ret = False; - goto done; - } - if (user_rid > highrid) { - DEBUG(0, ("tdbsam: no NUA rids available, cannot add user %s!\n", pdb_get_username(newpwd))); - ret = False; - goto done; - } - if (!pdb_set_user_sid_from_rid(newpwd, user_rid, PDB_CHANGED)) { - DEBUG(0, ("tdbsam: not able to set new allocated user RID into sam account!\n")); - ret = False; - goto done; - } - } else { - DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a RID\n",pdb_get_username(newpwd))); - ret = False; - goto done; - } - } - /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */ if ((data.dsize=init_buffer_from_sam (&buf, newpwd, False)) == -1) { DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n")); @@ -531,7 +494,6 @@ static NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_meth { NTSTATUS nt_status; struct tdbsam_privates *tdb_state; - uid_t low_nua_uid, high_nua_uid; if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) { return nt_status; @@ -569,19 +531,6 @@ static NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_meth (*pdb_method)->free_private_data = free_private_data; - if (lp_idmap_uid(&low_nua_uid, &high_nua_uid)) { - DEBUG(3, ("idmap uid range defined, non unix accounts enabled\n")); - - tdb_state->permit_non_unix_accounts = True; - - tdb_state->low_nua_rid=fallback_pdb_uid_to_user_rid(low_nua_uid); - - tdb_state->high_nua_rid=fallback_pdb_uid_to_user_rid(high_nua_uid); - - } else { - tdb_state->algorithmic_rids = True; - } - return NT_STATUS_OK; } diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index dfa3a8b62e..9324fd4765 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -2204,6 +2204,7 @@ NTSTATUS _api_samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_ uint32 acc_granted; SEC_DESC *psd; size_t sd_size; + uint32 new_rid = 0; /* check this, when giving away 'add computer to domain' privs */ uint32 des_access = GENERIC_RIGHTS_USER_ALL_ACCESS; @@ -2272,6 +2273,17 @@ NTSTATUS _api_samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_ pw = Get_Pwnam(account); + /********************************************************************* + * HEADS UP! If we have to create a new user account, we have to get + * a new RID from somewhere. This used to be done by the passdb + * backend. It has been moved into idmap now. Since idmap is now + * wrapped up behind winbind, this means you have to run winbindd if you + * want new accounts to get a new RID when "enable rid algorithm = no". + * Tough. We now have a uniform way of allocating RIDs regardless + * of what ever passdb backend people may use. + * --jerry (2003-07-10) + *********************************************************************/ + if ( !pw ) { /* * we can't check both the ending $ and the acb_info. @@ -2293,15 +2305,17 @@ NTSTATUS _api_samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_ } else /* no add user script -- ask winbindd to do it */ { - if ( !winbind_create_user( account ) ) - DEBUG(3,("_api_samr_create_user: winbind_create_user(%s) failed\n", account)); + if ( !winbind_create_user( account, &new_rid ) ) { + DEBUG(3,("_api_samr_create_user: winbind_create_user(%s) failed\n", + account)); + } } } - /* implicit call to getpwnam() next */ + /* implicit call to getpwnam() next. we have a valid SID coming out of this call */ - if ( !NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pass, account)) ) + if ( !NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pass, account, new_rid)) ) return nt_status; pdb_set_acct_ctrl(sam_pass, acb_info, PDB_CHANGED); @@ -3711,12 +3725,25 @@ static int smb_delete_user(const char *unix_user) pstring del_script; int ret; + /* try winbindd first since it is impossible to determine where + a user came from via NSS. Try the delete user script if this fails + meaning the user did not exist in winbindd's list of accounts */ + + if ( winbind_delete_user( unix_user ) ) { + DEBUG(3,("winbind_delete_user: removed user (%s)\n", unix_user)); + return 0; + } + + + /* fall back to 'delete user script' */ + pstrcpy(del_script, lp_deluser_script()); if (! *del_script) return -1; all_string_sub(del_script, "%u", unix_user, sizeof(pstring)); ret = smbrun(del_script,NULL); DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret)); + return ret; } 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. diff --git a/source3/utils/net_rpc_samsync.c b/source3/utils/net_rpc_samsync.c index 4b31c061f3..31535f7945 100644 --- a/source3/utils/net_rpc_samsync.c +++ b/source3/utils/net_rpc_samsync.c @@ -444,7 +444,9 @@ fetch_account_info(uint32 rid, SAM_ACCOUNT_INFO *delta) } else { DEBUG(8,("fetch_account_info: no add user/machine script. Asking winbindd\n")); - if ( !winbind_create_user( account ) ) + + /* don't need a RID allocated since the user already has a SID */ + if ( !winbind_create_user( account, NULL ) ) DEBUG(4,("fetch_account_info: winbind_create_user() failed\n")); } diff --git a/source3/utils/pdbedit.c b/source3/utils/pdbedit.c index b79972aa35..96d0d3c057 100644 --- a/source3/utils/pdbedit.c +++ b/source3/utils/pdbedit.c @@ -350,7 +350,7 @@ static int new_user (struct pdb_context *in, const char *username, NTSTATUS nt_status; char *password1, *password2, *staticpass; - if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pwent, username))) { + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pwent, username, 0))) { DEBUG(0, ("could not create account to add new user %s\n", username)); return -1; } -- cgit