From 16ff7b26f6b9d288cbd1d39e075b637e24da13a6 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Wed, 9 Jul 2003 16:44:47 +0000 Subject: Large set of changes to add UNIX account/group management to winbindd. See README.idmap-and-winbind-changes for details. (This used to be commit 1111bc7b0c7165e1cdf8d90eb49f4c368d2eded6) --- source3/nsswitch/winbindd_acct.c | 946 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 946 insertions(+) create mode 100644 source3/nsswitch/winbindd_acct.c (limited to 'source3/nsswitch/winbindd_acct.c') diff --git a/source3/nsswitch/winbindd_acct.c b/source3/nsswitch/winbindd_acct.c new file mode 100644 index 0000000000..7f4353c0ee --- /dev/null +++ b/source3/nsswitch/winbindd_acct.c @@ -0,0 +1,946 @@ +/* + Unix SMB/CIFS implementation. + + Winbind account management functions + + Copyright (C) by Gerald (Jerry) Carter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +#define WBKEY_PASSWD "WBA_PASSWD" +#define WBKEY_GROUP "WBA_GROUP" + +#define NUM_PW_FIELDS 7 +#define NUM_GRP_FIELDS 4 + +/* Globals */ + +static TDB_CONTEXT *account_tdb; + +extern userdom_struct current_user_info; + +/***************************************************************************** + Initialise auto-account database. +*****************************************************************************/ + +static BOOL winbindd_accountdb_init(void) +{ + /* see if we've already opened the tdb */ + + if ( account_tdb ) + return True; + + /* Nope. Try to open it */ + + if (!(account_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0, + TDB_DEFAULT, O_RDWR | O_CREAT, 0600))) + { + /* last chance -- maybe idmap has already opened it */ + if ( !(account_tdb = idmap_tdb_handle()) ) { + + DEBUG(0, ("winbindd_idmap_init: Unable to open idmap database\n")); + return False; + } + } + + /* yeah! */ + + return True; +} + +/********************************************************************** + Convert a string in /etc/passwd format to a struct passwd* entry +**********************************************************************/ + +static WINBINDD_PW* string2passwd( char *string ) +{ + static WINBINDD_PW pw; + char *p, *str; + char *fields[NUM_PW_FIELDS]; + int i; + + if ( !string ) + return NULL; + + ZERO_STRUCTP( &pw ); + + DEBUG(10,("string2passwd: converting \"%s\"\n", string)); + + ZERO_STRUCT( fields ); + + for ( i=0, str=string; ipw_name ) + return NULL; + + DEBUG(10,("passwd2string: converting passwd struct for %s\n", + pw->pw_name)); + + ret = snprintf( string, sizeof(string), "%s:%s:%d:%d:%s:%s:%s", + pw->pw_name, + pw->pw_passwd ? pw->pw_passwd : "x", + pw->pw_uid, + pw->pw_gid, + pw->pw_gecos, + pw->pw_dir, + pw->pw_shell ); + + if ( ret < 0 ) { + DEBUG(0,("passwd2string: snprintf() failed!\n")); + return NULL; + } + + return string; +} + +/********************************************************************** + Convert a string in /etc/group format to a struct group* entry +**********************************************************************/ + +static WINBINDD_GR* string2group( char *string ) +{ + static WINBINDD_GR grp; + char *p, *str; + char *fields[NUM_GRP_FIELDS]; + int i; + char **gr_members = NULL; + int num_gr_members = 0; + + if ( !string ) + return NULL; + + ZERO_STRUCTP( &grp ); + + DEBUG(10,("string2group: converting \"%s\"\n", string)); + + ZERO_STRUCT( fields ); + + for ( i=0, str=string; igr_name ) + return NULL; + + DEBUG(10,("group2string: converting passwd struct for %s\n", + grp->gr_name)); + + if ( grp->num_gr_mem ) { + int idx = 0; + + member = grp->gr_mem[0]; + size = 0; + num_members = 0; + + while ( member ) { + size += strlen(member) + 1; + num_members++; + member = grp->gr_mem[num_members]; + } + + gr_mem_str = smb_xmalloc(size); + + for ( i=0; igr_mem[i] ); + idx += strlen(grp->gr_mem[i]) + 1; + } + /* add trailing NULL (also removes trailing ',' */ + gr_mem_str[size-1] = '\0'; + } + else { + /* no members */ + gr_mem_str = smb_xmalloc(sizeof(fstring)); + fstrcpy( gr_mem_str, "" ); + } + + ret = snprintf( string, sizeof(string)-1, "%s:%s:%d:%s", + grp->gr_name, + grp->gr_passwd ? grp->gr_passwd : "*", + grp->gr_gid, + gr_mem_str ); + + SAFE_FREE( gr_mem_str ); + + if ( ret < 0 ) { + DEBUG(0,("group2string: snprintf() failed!\n")); + return NULL; + } + + return string; +} + +/********************************************************************** +**********************************************************************/ + +static char* acct_userkey_byname( const char *name ) +{ + static fstring key; + + snprintf( key, sizeof(key), "%s/NAME/%s", WBKEY_PASSWD, name ); + + return key; +} + +/********************************************************************** +**********************************************************************/ + +static char* acct_userkey_byuid( uid_t uid ) +{ + static fstring key; + + snprintf( key, sizeof(key), "%s/UID/%d", WBKEY_PASSWD, uid ); + + return key; +} + +/********************************************************************** +**********************************************************************/ + +static char* acct_groupkey_byname( const char *name ) +{ + static fstring key; + + snprintf( key, sizeof(key), "%s/NAME/%s", WBKEY_GROUP, name ); + + return key; +} + +/********************************************************************** +**********************************************************************/ + +static char* acct_groupkey_bygid( gid_t gid ) +{ + static fstring key; + + snprintf( key, sizeof(key), "%s/GID/%d", WBKEY_GROUP, gid ); + + return key; +} + +/********************************************************************** +**********************************************************************/ + +WINBINDD_PW* wb_getpwnam( const char * name ) +{ + char *keystr; + TDB_DATA data; + static WINBINDD_PW *pw; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_getpwnam: Failed to open winbindd account db\n")); + return NULL; + } + + + keystr = acct_userkey_byname( name ); + + data = tdb_fetch_by_string( account_tdb, keystr ); + + pw = NULL; + + if ( data.dptr ) { + pw = string2passwd( data.dptr ); + SAFE_FREE( data.dptr ); + } + + DEBUG(5,("wb_getpwnam: %s user (%s)\n", + (pw ? "Found" : "Did not find"), name )); + + return pw; +} + +/********************************************************************** +**********************************************************************/ + +WINBINDD_PW* wb_getpwuid( const uid_t uid ) +{ + char *keystr; + TDB_DATA data; + static WINBINDD_PW *pw; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_getpwuid: Failed to open winbindd account db\n")); + return NULL; + } + + data = tdb_fetch_by_string( account_tdb, acct_userkey_byuid(uid) ); + if ( !data.dptr ) { + DEBUG(4,("wb_getpwuid: failed to locate uid == %d\n", uid)); + return NULL; + } + keystr = acct_userkey_byname( data.dptr ); + + SAFE_FREE( data.dptr ); + + data = tdb_fetch_by_string( account_tdb, keystr ); + + pw = NULL; + + if ( data.dptr ) { + pw = string2passwd( data.dptr ); + SAFE_FREE( data.dptr ); + } + + DEBUG(5,("wb_getpwuid: %s user (uid == %d)\n", + (pw ? "Found" : "Did not find"), uid )); + + return pw; +} + +/********************************************************************** +**********************************************************************/ + +BOOL wb_storepwnam( const WINBINDD_PW *pw ) +{ + char *namekey, *uidkey; + TDB_DATA data; + char *str; + int ret = 0; + fstring username; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n")); + return False; + } + + namekey = acct_userkey_byname( pw->pw_name ); + + /* lock the main entry first */ + + if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) { + DEBUG(0,("wb_storepwnam: Failed to lock %s\n", namekey)); + return False; + } + + str = passwd2string( pw ); + + data.dptr = str; + data.dsize = strlen(str) + 1; + + if ( (tdb_store_by_string(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) { + DEBUG(0,("wb_storepwnam: Failed to store \"%s\"\n", str)); + ret = -1; + goto done; + } + + /* store the uid index */ + + uidkey = acct_userkey_byuid(pw->pw_uid); + + fstrcpy( username, pw->pw_name ); + data.dptr = username; + data.dsize = strlen(username) + 1; + + if ( (tdb_store_by_string(account_tdb, uidkey, data, TDB_REPLACE)) == -1 ) { + DEBUG(0,("wb_storepwnam: Failed to store uid key \"%s\"\n", str)); + tdb_delete_by_string(account_tdb, namekey); + ret = -1; + goto done; + } + + DEBUG(10,("wb_storepwnam: Success -> \"%s\"\n", str)); + +done: + tdb_unlock_bystring( account_tdb, namekey ); + + return ( ret == 0 ); +} + +/********************************************************************** +**********************************************************************/ + +WINBINDD_GR* wb_getgrnam( const char * name ) +{ + char *keystr; + TDB_DATA data; + static WINBINDD_GR *grp; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_getgrnam: Failed to open winbindd account db\n")); + return NULL; + } + + + keystr = acct_groupkey_byname( name ); + + data = tdb_fetch_by_string( account_tdb, keystr ); + + grp = NULL; + + if ( data.dptr ) { + grp = string2group( data.dptr ); + SAFE_FREE( data.dptr ); + } + + DEBUG(5,("wb_getgrnam: %s group (%s)\n", + (grp ? "Found" : "Did not find"), name )); + + return grp; +} + +/********************************************************************** +**********************************************************************/ + +WINBINDD_GR* wb_getgrgid( gid_t gid ) +{ + char *keystr; + TDB_DATA data; + static WINBINDD_GR *grp; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_getgrgid: Failed to open winbindd account db\n")); + return NULL; + } + + data = tdb_fetch_by_string( account_tdb, acct_groupkey_bygid(gid) ); + if ( !data.dptr ) { + DEBUG(4,("wb_getgrgid: failed to locate gid == %d\n", gid)); + return NULL; + } + keystr = acct_groupkey_byname( data.dptr ); + + SAFE_FREE( data.dptr ); + + data = tdb_fetch_by_string( account_tdb, keystr ); + + grp = NULL; + + if ( data.dptr ) { + grp = string2group( data.dptr ); + SAFE_FREE( data.dptr ); + } + + DEBUG(5,("wb_getgrgid: %s group (gid == %d)\n", + (grp ? "Found" : "Did not find"), gid )); + + return grp; +} + +/********************************************************************** +**********************************************************************/ + +BOOL wb_storegrnam( const WINBINDD_GR *grp ) +{ + char *namekey, *gidkey; + TDB_DATA data; + char *str; + int ret = 0; + fstring groupname; + + if ( !account_tdb && !winbindd_accountdb_init() ) { + DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n")); + return False; + } + + namekey = acct_groupkey_byname( grp->gr_name ); + + /* lock the main entry first */ + + if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) { + DEBUG(0,("wb_storegrnam: Failed to lock %s\n", namekey)); + return False; + } + + str = group2string( grp ); + + data.dptr = str; + data.dsize = strlen(str) + 1; + + if ( (tdb_store_by_string(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) { + DEBUG(0,("wb_storegrnam: Failed to store \"%s\"\n", str)); + ret = -1; + goto done; + } + + /* store the gid index */ + + gidkey = acct_groupkey_bygid(grp->gr_gid); + + fstrcpy( groupname, grp->gr_name ); + data.dptr = groupname; + data.dsize = strlen(groupname) + 1; + + if ( (tdb_store_by_string(account_tdb, gidkey, data, TDB_REPLACE)) == -1 ) { + DEBUG(0,("wb_storegrnam: Failed to store gid key \"%s\"\n", str)); + tdb_delete_by_string(account_tdb, namekey); + ret = -1; + goto done; + } + + DEBUG(10,("wb_storegrnam: Success -> \"%s\"\n", str)); + +done: + tdb_unlock_bystring( account_tdb, namekey ); + + return ( ret == 0 ); +} + +/********************************************************************** +**********************************************************************/ + +static BOOL wb_addgrpmember( WINBINDD_GR *grp, const char *user ) +{ + int i; + char **members; + + if ( !grp || !user ) + return False; + + for ( i=0; inum_gr_mem; i++ ) { + if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 ) + return True; + } + + /* add one new slot and keep an extra for the terminating NULL */ + members = Realloc( grp->gr_mem, (grp->num_gr_mem+2)*sizeof(char*) ); + if ( !members ) + return False; + + grp->gr_mem = members; + grp->gr_mem[grp->num_gr_mem++] = smb_xstrdup(user); + grp->gr_mem[grp->num_gr_mem] = NULL; + + return True; +} + +/********************************************************************** +**********************************************************************/ + +static BOOL wb_delgrpmember( WINBINDD_GR *grp, const char *user ) +{ + int i; + BOOL found = False; + + if ( !grp || !user ) + return False; + + for ( i=0; inum_gr_mem && !found; i++ ) { + if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 ) + found = True; + } + + 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--; + + return True; +} + +/********************************************************************** +**********************************************************************/ + +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; +} + +/********************************************************************** + Create a new "UNIX" user for the system given a username +**********************************************************************/ + +enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state) +{ + char *user, *group; + unid_t id; + WINBINDD_PW pw; + WINBINDD_GR *wb_grp; + struct group *unix_grp; + gid_t primary_gid; + + if ( !state->privileged ) { + DEBUG(2, ("winbindd_create_user: non-privileged access denied!\n")); + return WINBINDD_ERROR; + } + + /* Ensure null termination */ + state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0'; + state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0'; + + user = state->request.data.acct_mgt.username; + group = state->request.data.acct_mgt.groupname; + + DEBUG(3, ("[%5d]: create_user user=>(%s), group=>(%s)\n", + state->pid, user, group)); + + if ( !*group ) + group = lp_template_primary_group(); + + /* validate the primary group + 1) lookup in local tdb first + 2) call getgrnam() as a last resort */ + + if ( (wb_grp=wb_getgrnam(group)) != NULL ) { + primary_gid = wb_grp->gr_gid; + free_winbindd_gr( wb_grp ); + } + else if ( (unix_grp=sys_getgrnam(group)) != NULL ) { + primary_gid = unix_grp->gr_gid; + } + else { + DEBUG(2,("winbindd_create_user: Cannot validate gid for group (%s)\n", group)); + return WINBINDD_ERROR; + } + + /* get a new uid */ + + if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_USERID)) ) { + DEBUG(0,("winbindd_create_user: idmap_allocate_id() failed!\n")); + return WINBINDD_ERROR; + } + + /* The substitution of %U and %D in the 'template homedir' is done + by lp_string() calling standard_sub_basic(). */ + + fstrcpy( current_user_info.smb_name, user ); + sub_set_smb_name( user ); + fstrcpy( current_user_info.domain, get_global_sam_name() ); + + /* fill in the passwd struct */ + + fstrcpy( pw.pw_name, user ); + fstrcpy( pw.pw_passwd, "x" ); + fstrcpy( pw.pw_gecos, user); + fstrcpy( pw.pw_dir, lp_template_homedir() ); + fstrcpy( pw.pw_shell, lp_template_shell() ); + + pw.pw_uid = id.uid; + pw.pw_gid = primary_gid; + + return ( wb_storepwnam(&pw) ? WINBINDD_OK : WINBINDD_ERROR ); +} + +/********************************************************************** + Create a new "UNIX" group for the system given a username +**********************************************************************/ + +enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state) +{ + char *group; + unid_t id; + WINBINDD_GR grp; + + if ( !state->privileged ) { + DEBUG(2, ("winbindd_create_group: non-privileged access denied!\n")); + return WINBINDD_ERROR; + } + + /* Ensure null termination */ + state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0'; + group = state->request.data.acct_mgt.groupname; + + DEBUG(3, ("[%5d]: create_group (%s)\n", state->pid, group)); + + /* get a new uid */ + + if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_GROUPID)) ) { + DEBUG(0,("winbindd_create_group: idmap_allocate_id() failed!\n")); + return WINBINDD_ERROR; + } + + /* fill in the group struct */ + + fstrcpy( grp.gr_name, group ); + fstrcpy( grp.gr_passwd, "*" ); + + grp.gr_gid = id.gid; + grp.gr_mem = NULL; /* start with no members */ + grp.num_gr_mem = 0; + + return ( wb_storegrnam(&grp) ? WINBINDD_OK : WINBINDD_ERROR ); +} + +/********************************************************************** + Add a user to the membership for a group. +**********************************************************************/ + +enum winbindd_result winbindd_add_user_to_group(struct winbindd_cli_state *state) +{ + WINBINDD_PW *pw; + WINBINDD_GR *grp; + char *user, *group; + BOOL ret; + + if ( !state->privileged ) { + DEBUG(2, ("winbindd_add_user_to_group: non-privileged access denied!\n")); + return WINBINDD_ERROR; + } + + /* Ensure null termination */ + state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0'; + state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0'; + group = state->request.data.acct_mgt.groupname; + user = state->request.data.acct_mgt.username; + + DEBUG(3, ("[%5d]: add_user_to_group add %s to %s\n", state->pid, + user, group)); + + /* make sure it is a valid user */ + + if ( !(pw = wb_getpwnam( user )) ) { + DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n")); + return WINBINDD_ERROR; + } + + /* make sure it is a valid group */ + + if ( !(grp = wb_getgrnam( group )) ) { + DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n")); + return WINBINDD_ERROR; + } + + if ( !wb_addgrpmember( grp, user ) ) + return WINBINDD_ERROR; + + ret = wb_storegrnam(grp); + + free_winbindd_gr( grp ); + + return ( ret ? WINBINDD_OK : WINBINDD_ERROR ); +} + +/********************************************************************** + Remove a user from the membership of a group +**********************************************************************/ + +enum winbindd_result winbindd_remove_user_from_group(struct winbindd_cli_state *state) +{ + WINBINDD_GR *grp; + char *user, *group; + BOOL ret; + + if ( !state->privileged ) { + DEBUG(2, ("winbindd_remove_user_from_group: non-privileged access denied!\n")); + return WINBINDD_ERROR; + } + + /* Ensure null termination */ + state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0'; + state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0'; + group = state->request.data.acct_mgt.groupname; + user = state->request.data.acct_mgt.username; + + DEBUG(3, ("[%5d]: remove_user_to_group delete %s from %s\n", state->pid, + user, group)); + + /* don't worry about checking the username since we're removing it anyways */ + + /* make sure it is a valid group */ + + if ( !(grp = wb_getgrnam( group )) ) { + DEBUG(4,("winbindd_remove_user_to_group: Cannot remove a user to a non-extistent group\n")); + return WINBINDD_ERROR; + } + + if ( !wb_delgrpmember( grp, user ) ) + return WINBINDD_ERROR; + + ret = wb_storegrnam(grp); + + free_winbindd_gr( grp ); + + return ( ret ? WINBINDD_OK : WINBINDD_ERROR ); +} + +/********************************************************************** + Set the primary group membership of a user +**********************************************************************/ + +enum winbindd_result winbindd_set_user_primary_group(struct winbindd_cli_state *state) +{ + WINBINDD_PW *pw; + WINBINDD_GR *grp; + char *user, *group; + + if ( !state->privileged ) { + DEBUG(2, ("winbindd_set_user_primary_group: non-privileged access denied!\n")); + return WINBINDD_ERROR; + } + + /* Ensure null termination */ + state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0'; + state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0'; + group = state->request.data.acct_mgt.groupname; + user = state->request.data.acct_mgt.username; + + DEBUG(3, ("[%5d]: set_user_primary_group group %s for user %s\n", state->pid, + group, user)); + + /* make sure it is a valid user */ + + if ( !(pw = wb_getpwnam( user )) ) { + DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n")); + return WINBINDD_ERROR; + } + + /* make sure it is a valid group */ + + if ( !(grp = wb_getgrnam( group )) ) { + DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n")); + return WINBINDD_ERROR; + } + + pw->pw_gid = grp->gr_gid; + + free_winbindd_gr( grp ); + + return ( wb_storepwnam(pw) ? WINBINDD_OK : WINBINDD_ERROR ); +} + +/********************************************************************** + Set the primary group membership of a user +**********************************************************************/ + +enum winbindd_result winbindd_delete_user(struct winbindd_cli_state *state) +{ + return WINBINDD_ERROR; +} + +/********************************************************************** + Set the primary group membership of a user +**********************************************************************/ + +enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state) +{ + return WINBINDD_ERROR; +} + + -- cgit