#!/bin/sh # # /usr/local/bin/localuserconfig # Copyright 2004-2006 Jonas Smedegaard # # $Id: localuserconfig,v 1.8 2006-07-16 12:29:16 jonas Exp $ # # Set/change local account settings # # TODO: Use the following syntax to add info to .smbpasswd # grep -q SomeOption ~/file || echo "line with SomeOption and such" >> ~/file # # TODO: Move relevant stuff from user-init and make it use this instead # TODO: Use getopt, and implement --help # TODO: Implement runmodes noninteractive and force (in addition to the default: interactive) # TODO: Runmode noninteractive by default if no tty attached # TODO: Implement verbosity levels (0=quiet 1=oneliner 2=default 3=chatty 4=debug) set -e # config (edit /etc/local/users.conf to override) # which are user accounts (adduser values are used if empty) first_uid="" last_uid="" do_quota="no" # Manage disk quota do_distrib="no" # Distributed shares (software archive) do_personal="no" # Personal shares (mac, pc, public_html) do_xchange="no" # Group-readable shares do_public="no" # Public share (web homepage) do_mac="no" # AppleShare-optimized share (netatalk) do_pc="no" # SMB-ptimized share (Samba) do_server="no" # Personal share on remote SMB server quota_roots="" # space-delimited list of disk devices quota_soft="100000" quota_hard="1000000" quota_newstyle="yes" # Woody used a different syntax... xchange_root="xchange" xchange_sharedroot="/home/XCHANGE" mac_root="mac" pc_root="pc" server_name="SERVER" # SMB name of remote server server_desc="remote server" server_root="server" server_conf="/etc/security/pam_mount.conf" server_userconf=".winpassword" ### No servicable parts below this line! ### if [ -e /etc/adduser.conf ]; then . /etc/adduser.conf else echo "/etc/adduser.conf missing. Exiting..." exit 1 fi [ -r /etc/local/users.conf ] && . /etc/local/users.conf # Flags used internally root_required="no" runmode="interactive" user="`id -u -n`" uid="`id -u`" HOME="`getent passwd \"$user\" | awk -F: '{print $6}' | head -n 1`" #TODO: Allow root to pass the above as options # Safety checks (disable silently - the user can't correct it anyway) [ "$do_mac" = "yes" -a -n "$mac_root" ] || do_mac="no" [ "$do_pc" = "yes" -a -n "$pc_root" ] || do_pc="no" [ "$do_xchange" = "yes" -a -n "$xchange_root" ] || do_xchange="no" [ "$do_server" = "yes" -a -n "$server_root" ] || do_server="no" #TODO: do a loop instead for the above # only do user accounts (checked against /etc/adduser.conf values) [ -n "$first_uid" ] || first_uid="$FIRST_UID" [ -n "$last_uid" ] || last_uid="$LAST_UID" [ "$uid" -ge "$FIRST_UID" -a "$uid" -le "$LAST_UID" ] || exit 0 #TODO: check if above is numeric #TODO: Always use adduser values as strict outer limits if [ -z "$HOME" ]; then echo "Error: Unable to resolve home of user $user!" exit 1 fi if [ ! -d "$HOME" ]; then echo "Error: Home of user $user doesn't exist: \"$HOME\"!" exit 1 fi if [ "$do_server" = "yes" ]; then server_edit="1" if [ -r "$HOME/$server_userconf" ]; then server_username="$(grep '^username' $HOME/$server_userconf | awk -F= '{print $2}' | head -n 1 | awk '{print $1}')" server_password="$(grep '^password' $HOME/$server_userconf | awk -F= '{print $2}' | head -n 1 | awk '{print $1}')" fi if [ -n "$server_username" ]; then server_edit="" echo -n "Account info for $server_desc already exists. Edit (y/N)? " read server_overwrite case $server_overwrite in y|Y) server_edit="1" ;; esac else server_username=$user fi if [ "$server_edit" = "1" ]; then if [ -n "$server_username" ]; then echo -n "Username on $server_desc ($server_username): " else echo -n "Username on $server_desc: " fi read server_username_new if [ -n "$server_username_new" ]; then server_username="$server_username_new" fi echo -n "Password on $server_desc: " read -s server_password echo echo -n "Password once more: " read -s server_password_again echo if [ -n "$server_username" -a "$server_password" = "$server_password_again" ]; then echo "username = $server_username" > $HOME/$server_userconf echo "password = $server_password" >> $HOME/$server_userconf chmod 0600 $HOME/$server_userconf else echo "Setting server login info failed: problem getting either username or password" fi fi if grep -v -q "^volume $user " "$server_conf"; then root_required="yes" fi fi #if [ "$root_required" = "yes" ]; then # echo "Note: Some of the current setup requires action by systems administrator" #TODO: Re-enable this, but only when able to suppress by root #TODO: send email to root, unless invoked by root #TODO: Collect actual items needing attention, to print and include in email #fi exit 0 #FIXME: The remaining is to be implemented (lifted from user-init) #if [ -x /etc/local/quota.sh ]; then # /etc/local/quota.sh $user # fi [ $quota_soft ] || quota_soft="0" [ $quota_hard ] || quota_hard="0" for quota_root in $quota_roots; do if [ $quota_newstyle ]; then setquota $user $quota_soft $quota_hard 0 0 $quota_root else setquota $user $quota_root $quota_soft $quota_hard 0 0 fi done mkdir -p $HOME/mail if [ "$USE_MBOX" ]; then touch $HOME/mail/mbox elif [ -f $HOME/mail/mbox -a ! -s $HOME/mail/mbox ]; then rm -f $HOME/mail/mbox fi if [ $NETATALK ]; then mkdir -p $HOME/$mac_root fi if [ $SAMBA ]; then mkdir -p $HOME/$pc_root fi if [ $XCHANGE ]; then mkdir -p $xchange_sharedroot/users/root/$user fi if [ $PUBLIC ]; then mkdir -p $HOME/public_html fi chown $user: $HOME chmod u=rwX,go=rX $HOME # Mail handling chown -R $user: $HOME/mail chmod -R u=rw,go=,u+X $HOME/mail if [ -f $HOME/.mailboxlist ]; then chown $user: $HOME/.mailboxlist chmod 0640 $HOME/.mailboxlist fi if [ -f $HOME/.forward ]; then chown $user: $HOME/.forward chmod 0640 $HOME/.forward fi if [ -f /var/mail/$user ]; then chown $user:mail /var/mail/$user chmod ug=rw,o= /var/mail/$user elif [ -f /var/spool/mail/$user ]; then chown $user:mail /var/spool/mail/$user chmod ug=rw,o= /var/spool/mail/$user fi # MySQL handling if [ -f $HOME/.my.cnf ]; then chown $user: $HOME/.my.cnf chmod 0600 $HOME/.my.cnf fi # Mac dir permissions if [ -d $HOME/$mac_root ]; then chown -R $user: $HOME/$mac_root chmod -R u=rw,g=r,o=,ug+X $HOME/$mac_root rm -rf $HOME/$mac_root/Network\ Trash\ Folder mkdir $HOME/$mac_root/Network\ Trash\ Folder chown nobody: $HOME/$mac_root/Network\ Trash\ Folder chmod a= $HOME/$mac_root/Network\ Trash\ Folder fi # PC dir permissions if [ -d $HOME/$pc_root ]; then chown -R $user: $HOME/$pc_root chmod -R u=rw,g=r,o=,ug+X $HOME/$pc_root fi # Exchange dir permissions if [ -d $xchange_sharedroot/users/root/$user ]; then chown -R $user:users $xchange_sharedroot/users/root/$user chmod -R g=r,g+X $xchange_sharedroot/users/root/$user if [ -e "x$HOME/$xchange_root" ]; then if [ -L "x$HOME/$xchange_root" ]; then ln -sf $xchange_sharedroot/users/root/$user $HOME/$xchange_root else echo "ERROR: $HOME/$xchange_root exists already. Leaving it as is..." fi else ln -s $xchange_sharedroot/users/root/$user $HOME/$xchange_root fi fi # Public dir permissions if [ -d $HOME/public_html ]; then chown -R $user: $HOME/public_html chmod -R u+rX,go=r,go+X $HOME/public_html if [ $NETATALK ]; then rm -rf $HOME/public_html/Network\ Trash\ Folder mkdir $HOME/public_html/Network\ Trash\ Folder chown nobody: $HOME/public_html/Network\ Trash\ Folder chmod a= $HOME/public_html/Network\ Trash\ Folder fi fi # Fileshares: /shares./// # : Either mac or win depending on which of netatalk and samba provides r/w access to the shares # : Group with write access to the share (usually the default group of the owner) # : Either rwgroup or secondary group with read-only access to the share # owner must be member of both groups, and rwgroup members must also be members of rogroup find "$HOME" -mindepth 1 -maxdepth 1 -type d -print | egrep "^$HOME/shares\." | (while read thisdir; do sharetype="`basename \"$thisdir\" | awk -F. '{print $2}'`" # Define dir and file exceptions case "$sharetype" in mac) dirs_world_rw_create='.AppleDB' dirs_group_rw_create='.AppleDesktop/Temporary Items/TheFindByContentFolder' dirs_group_ro_create='TheVolumeSettingsFolder' dirs_group_ro_update='.AppleDouble' files_group_ro_update=':2eDS_Store' dirs_no_access_purge='Network Trash Folder' ;; win) ;; *) continue ;; esac exceptions="$dirs_world_rw_create/$dirs_group_rw_create/$dirs_group_ro_create/$dirs_group_ro_update/$files_group_ro_update/$dirs_no_access_purge" exception_dirs_create="$dirs_world_rw_create/$dirs_group_rw_create/$dirs_group_ro_create" chown "$user": "$thisdir" chmod a=rX "$thisdir" find "$thisdir" -mindepth 1 -maxdepth 1 -type d -print | (while read thisdir; do rogroup="`basename \"$thisdir\"`" chown "$user":"$rogroup" "$thisdir" chmod ug=rX,o= "$thisdir" find "$thisdir" -mindepth 1 -maxdepth 1 -type d -print | (while read thisdir; do rwgroup="`basename \"$thisdir\"`" chown "$user":"$rwgroup" "$thisdir" chmod a=rX,g+s "$thisdir" find "$thisdir" -mindepth 1 -maxdepth 1 -type d -print | (while read thisdir; do sharename="`basename \"$thisdir\"`" chown "$user":"$rwgroup" "$thisdir" chmod u=rw,go=r,a+X,g+s "$thisdir" ifs="$IFS" # Set default permissions find "$thisdir" -mindepth 1 -maxdepth 1 -print | (while read thisdir; do item="`basename \"$thisdir\"`" IFS="/"; for exception in $exceptions; do IFS="$ifs"; if [ "$item" = "$exception" ]; then continue 2 fi done chgrp -R "$rwgroup" "$thisdir" chmod -R ug=rw,o=r,a+X,g+s "$thisdir" done) # Handle exception dirs to be created if not existing IFS="/"; for dir in $exception_dirs_create; do IFS="$ifs"; if [ ! -d "$thisdir/$dir" ]; then rm -f "$thisdir/$dir" fi if [ ! -e "$thisdir/$dir" ]; then mkdir "$thisdir/$dir" fi chown "$user":"$rwgroup" "$thisdir/$dir" done IFS="/"; for dir in $dirs_world_rw_create; do IFS="$ifs"; if [ "$rogroup" = "$rwgroup" ]; then chmod -R ug=rw,o=r,a+X,g+s "$thisdir/$dir" else chmod -R a=rw,a+X,g+s "$thisdir/$dir" fi done IFS="/"; for dir in $dirs_group_rw_create; do IFS="$ifs"; chmod -R ug=rw,o=r,a+X,g+s "$thisdir/$dir" done IFS="/"; for dir in $dirs_group_ro_create; do IFS="$ifs"; chmod -R u=rw,go=r,a+X,g+s "$thisdir/$dir" done # Handle exception dirs to be updated if already there IFS="/"; for dir in $dirs_group_ro_update; do IFS="$ifs"; if [ -e "$thisdir/$dir" ]; then chmod u=rw,go=r,a+X,g+s "$thisdir/$dir" fi done # Handle exception files to be updated if already there IFS="/"; for file in $files_group_ro_update; do IFS="$ifs"; if [ -e "$thisdir/$file" ]; then chmod u=rw,go=r,g+s "$thisdir/$file" fi done # Handle exception dirs to be purged and recreated IFS="/"; for dir in $dirs_no_access_purge; do IFS="$ifs"; rm -rf "$thisdir/$dir" mkdir -m a= "$thisdir/$dir" chown nobody: "$thisdir/$dir" done IFS="$ifs" done) done) done) done) # Deprecated share permissions for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/shares_win"`; do chgrp -R $user $dir chmod -R u=rw,g=rw,o=,ug+X,g+s $dir done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/shares_mac"`; do chgrp -R $user $dir chmod -R u=rw,g=rw,o=,ug+X,g+s $dir rm -rf $dir/Network\ Trash\ Folder mkdir $dir/Network\ Trash\ Folder chown nobody: $dir/Network\ Trash\ Folder chmod a= $dir/Network\ Trash\ Folder done # Ftp shares permissions for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/ftp_$USER$"`; do chgrp -R $user $dir chmod -R ug=rw,o=r,a+X,g+s $dir rm -rf $dir/Network\ Trash\ Folder mkdir $dir/Network\ Trash\ Folder chown nobody: $dir/Network\ Trash\ Folder chmod a= $dir/Network\ Trash\ Folder done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/ftp_${USER}_ro$"`; do chown -R $user: $dir chmod -R u=rw,go=r,a+X $dir rm -rf $dir/Network\ Trash\ Folder mkdir $dir/Network\ Trash\ Folder chown nobody: $dir/Network\ Trash\ Folder chmod a= $dir/Network\ Trash\ Folder done # Web shares permissions for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/web_"`; do chown -R $user: $dir # chmod -R u=rw,go=r,a+X $webdir #TODO: Only cgi scripts (.cgi and .pl) should be executable chmod -R u+rw,go+r,a+X $dir # leftover from ancient times with another policy if [ $NETATALK ]; then rm -rf $dir/Network\ Trash\ Folder fi done # Web shares permissions for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/websites"`; do chown root: $dir chmod a=r,u+w,a+X $dir done for dir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/websites/"`; do chown -R $user: $dir # chmod -R u=rw,go=r,a+X $webdir #TODO: Only cgi scripts (.cgi and .pl) should be executable chmod -R u+rw,go+r,a+X $dir # leftover from ancient times with another policy if [ $NETATALK ]; then rm -rf $dir/Network\ Trash\ Folder fi done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/webscripts"`; do chown root: $dir chmod a=r,u+w,a+X $dir done for dir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/webscripts/"`; do chown -R $user: $dir # chmod -R u=rw,go=r,a+X $webdir #TODO: Only cgi scripts (.cgi and .pl) should be executable chmod -R u+rw,go+r,a+X $dir done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/webdata"`; do chown $user: $dir chmod a=r,u+w,a+X $dir done for dir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/webdata/"`; do chown -R $user: $dir chmod -R u=rw,go=,u+X $dir done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/webshareddata"`; do chown $user: $dir chmod a=r,u+w,a+X $dir done for dir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/webshareddata/"`; do chown -R $user: $dir chmod -R u=rw,go=r,a+X $dir done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/webphpsites"`; do chown root: $dir chmod u=rw,go=r,a+X $dir done for dir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/webphpsites/"`; do chown -R $user:www-data $dir # chmod -R ug=rw,o=r,a+X $dir chmod -R ug=rw,o=,ug+X $dir done for dir in `find $HOME -mindepth 1 -maxdepth 1 -type d | egrep "^$HOME/webphpdata"`; do chown root: $dir chmod a=r,u+w,a+X $dir done for dir in `find $HOME -mindepth 2 -maxdepth 2 -type d | egrep "^$HOME/webphpdata/"`; do chown -R $user:www-data $dir chmod -R ug=rw,o=,ug+X $dir done # Dummy user restrictions if [ -n "$REALUSERS_GROUPNAME" -a -n "$DUMMYSHAREDIR" -a -n "$DUMMYSHAREOWNER" -a -n "$DUMMYSHARENAME" ]; then [ -e $DUMMYSHAREDIR/$user ] \ || mkdir $DUMMYSHAREDIR/$user chown $DUMMYSHAREOWNER: $DUMMYSHAREDIR/$user chmod u=rw,go=r,a+X $DUMMYSHAREDIR/$user if [ -e $HOME/$DUMMYSHARENAME ]; then if [ -L $HOME/$DUMMYSHARENAME ]; then ln -sf $DUMMYSHAREDIR/$user $HOME/$DUMMYSHARENAME chown $user: $HOME/$DUMMYSHARENAME else echo "WARNING: $HOME/$DUMMYSHAREDIR exists already. Leaving it as is..." fi else ln -s $DUMMYSHAREDIR/$user $HOME/$DUMMYSHARENAME chown $user: $HOME/$DUMMYSHARENAME fi if [ -n "$DUMMYAPACHECFG" -a -n "$DUMMYAPACHESHAREDIR" ]; then if [ -f /etc/apache/include.d/$DUMMYAPACHECFG -a -x /etc/init.d/apache ]; then if [ -e /etc/apache/include.d/$DUMMYAPACHECFG-$user ]; then echo "/etc/apache/include.d/$DUMMYAPACHECFG-$user exists already. Ignoring..." else echo "# Created automatically by adduser.local require user $user " \ > /etc/apache/include.d/$DUMMYAPACHECFG-$user root_required="yes" fi fi fi fi