#!/bin/sh set -e # reset flags apache_reload_needed="" runmode="normal" # subfolder name defaults (edit /etc/local/users.conf to override) mac="mac" # Optimized for sharing through AppleShare (netatalk) pc="pc" # Optimized for sharing though SMB/CIFS (Samba) xchange="xchange" # Readable by group # 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" QUOTASOFT="0" QUOTAHARD="0" # Relax permissions # Currently allows group write access to root of mac shares RELAXEDPERMS='' ### 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 #TODO: Add conversion like below, and change remaining script to new variable names #[ -n "$XDIR" ] && xchange_sharedroot="$XDIR" # exit silently if this system lacks required hints [ -r /etc/local/volumes ] && . /etc/local/volumes || exit 0 XDIRREAL="$XDIR/users/root" if [ -n "$XCHANGE" ]; then if [ ! -d "$XDIR" ]; then echo "XDIR doesn't exist. Ignoring XCHANGE!" XCHANGE="" fi fi if [ $# -gt 0 ]; then USERS=$* else # USERS=`getent passwd | awk -F: '{print $1}'` echo "uid required!" exit 1 fi [ -n "$NETATALK_HOME" ] && mac="$NETATALK_HOME" [ -n "$SAMBA_HOME" ] && pc="$SAMBA_HOME" [ -n "$XCHANGE_HOME" ] && xchange="$XCHANGE_HOME" echo "Setting up additional folders and permissions..." for user in $USERS; do uid="`getent passwd \"$user\" | awk -F: '{print $3}' | head -n 1`" HOME="`getent passwd \"$user\" | awk -F: '{print $6}' | head -n 1`" groups="`groups \"$user\"`" if [ -z "$HOME" ]; then echo "User $user doesn't exist. Ignoring..." continue fi # Ignore non-human accounts silently [ "$uid" -ge "$FIRST_UID" -a "$uid" -le "$LAST_UID" ] || continue [ -d "$HOME" ] || continue # [ -L "$HOME" ] && continue echo -n "$user" # if [ -x /etc/local/quota.sh ]; then # /etc/local/quota.sh "$user" # fi quotasoft_override='' quotahard_override='' for quotaoverride in $QUOTAOVERRIDES; do for group in $groups; do if [ "$quotaoverride" = "$group" ]; then eval quotasoft_override=\"\$QUOTASOFT_${quotaoverride}\" eval quotahard_override=\"\$QUOTAHARD_${quotaoverride}\" break 2 fi done done quotasoft="${quotasoft_override:-$QUOTASOFT}" quotahard="${quotahard_override:-$QUOTAHARD}" for quotahome in $QUOTAHOMES; do if [ -n "$NEW_QUOTA" ]; then setquota "$user" "$quotasoft" "$quotahard" 0 0 "$quotahome" else setquota "$user" "$quotahome" "$quotasoft" "$quotahard" 0 0 fi done if [ -n "$NETATALK" ]; then mkdir -p "$HOME/$mac" fi if [ -n "$SAMBA" ]; then mkdir -p "$HOME/$pc" fi if [ -n "$XCHANGE" ]; then mkdir -p "$XDIRREAL/$user" fi if [ -n "$PUBLIC" ]; then mkdir -p "$HOME/public_html" fi #TODO: Enable this only when option implemented to do it non-interactively # echo # dirty hack: better if being able to lower verbosity of localuserconfig # su -s /bin/bash -c localuserconfig "$user" if [ "$do_server" = "yes" ] && [ -r "$server_conf" ] && [ -f "$HOME/$server_userconf" ]; then server_username="$(grep '^username' \"$HOME/$server_userconf\" | awk -F= '{print $2}' | head -n 1 | awk '{print $1}')" if [ -n "$server_username" ]; then if grep -q "^volume $user " "$server_conf"; then perl -pi -e "s|^volume $user .*|volume $user smb $server_name $server_username $HOME/$server_root uid=$user,gid=$user - -|" "$server_conf" else echo "volume $user smb $server_name $server_username $HOME/$server_root uid=$user,gid=$user - -" >> "$server_conf" fi fi fi chown "$user": "$HOME" chmod u=rwX,go=rX "$HOME" # Mail handling maildir="$MAILDIR" if [ -f "$HOME/.procmailrc" ]; then # Drop simple maildir-enabling procmail file when default if [ -n "$MAILDIR" ] && [ "`md5sum \"$HOME/.procmailrc\" | awk '{print $1}'`" = "03ea802caaa5ce6f2a9be8d56eaf8ff5" ]; then rm "$HOME/.procmailrc" else chown "$user": "$HOME/.procmailrc" chmod 0640 "$HOME/.procmailrc" # Check if this one account exceptionally uses maildir if [ -z "$maildir" ] && egrep -q '^DEFAULT=\$HOME/Maildir/$' "$HOME/.procmailrc"; then maildir="yes" fi fi fi if [ -n "$maildir" ]; then mkdir -p "$HOME/Maildir/cur" "$HOME/Maildir/new" "$HOME/Maildir/tmp" chown -R "$user": "$HOME/Maildir" chmod -R u=rw,go=,u+X "$HOME/Maildir" else mkdir -p "$HOME/mail" if [ -n "$USE_MBOX" ]; then touch "$HOME/mail/mbox" elif [ -f "$HOME/mail/mbox" ] && [ ! -s "$HOME/mail/mbox" ]; then rm -f "$HOME/mail/mbox" fi 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 fi mailspool="/var/spool/mail" if [ -d "/var/mail" ]; then mailspool="/var/mail" fi if [ -f "$mailspool/$user" ]; then if [ -n "$maildir" ] && [ ! -s "$mailspool/$user" ]; then rm "$mailspool/$user" else chown "$user":mail "$mailspool/$user" chmod ug=rw,o= "$mailspool/$user" fi fi if [ -f "$HOME/.forward" ]; then chown "$user": "$HOME/.forward" chmod 0640 "$HOME/.forward" 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" ]; then chown -R "$user": "$HOME/$mac" chmod -R u=rw,g=r,o=,ug+X "$HOME/$mac" rm -rf "$HOME/$mac/Network Trash Folder" mkdir "$HOME/$mac/Network Trash Folder" chown nobody: "$HOME/$mac/Network Trash Folder" chmod a= "$HOME/$mac/Network Trash Folder" fi # PC dir permissions if [ -d "$HOME/$pc" ]; then chown -R "$user": "$HOME/$pc" chmod -R u=rw,g=r,o=,ug+X "$HOME/$pc" fi #FIXME: something is wrong with prefixing "x" here... # Exchange dir permissions if [ -d "$XDIRREAL/$user" ]; then chown -R "$user":users "$XDIRREAL/$user" chmod -R g=r,g+X "$XDIRREAL/$user" if [ -e "x$HOME/$xchange" ]; then if [ -L "x$HOME/$xchange" ]; then ln -sf "$XDIRREAL/$user $HOME/$xchange" else echo "ERROR: \"$HOME/$xchange\" exists already. Leaving it as is..." fi else ln -s "$XDIRREAL/$user" "$HOME/$xchange" fi fi # Public dir permissions for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./public\(_.*\)?'`; do chown -R "$user": "$HOME/$dir" chmod -R u+rX,go=r,go+X "$HOME/$dir" if [ -n "$NETATALK" ]; then rm -rf "$HOME/$dir/Network Trash Folder" mkdir "$HOME/$dir/Network Trash Folder" chown nobody: "$HOME/$dir/Network Trash Folder" chmod a= "$HOME/$dir/Network Trash Folder" fi done # Shared dirs are writable by own primary group for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./shared\(_.*\)?'`; do chgrp -R "$user" "$HOME/$dir" chmod -R ug=rw,o=,ug+X,g+s "$HOME/$dir" if [ -n "$NETATALK" ]; then rm -rf "$HOME/$dir/Network Trash Folder" mkdir "$HOME/$dir/Network Trash Folder" chown nobody: "$HOME/$dir/Network Trash Folder" chmod a= "$HOME/$dir/Network Trash Folder" fi done # Private dirs are readable by own primary group for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./private\(_.*\)?$'`; do chown -R "$user": "$HOME/$dir" chmod -R u+rX,g=r,g+X,o= "$HOME/$dir" done # Secret dirs are accessible only by self for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./secret\(_.*\)?$'`; do chown -R "$user": "$HOME/$dir" chmod -R u+rX,go= "$HOME/$dir" done # 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 and rwgroup members must be member of both groups #FIXME: Use the below instead, and replace occurrences of "$thisdir" with "$HOME/$dir". #for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./shares\..*'`; do 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' if [ -n "$RELAXEDPERMS" ]; then dirs_group_rw_update='.AppleDouble' else dirs_group_ro_update='.AppleDouble' fi 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_rw_update/$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" # /shares. chown "$user": "$thisdir" chmod a=rX "$thisdir" find "$thisdir" -mindepth 1 -maxdepth 1 -type d -print | (while read thisdir; do # /shares./ 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 # /shares.// 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 # /shares./// sharename="`basename \"$thisdir\"`" chown "$user":"$rwgroup" "$thisdir" if [ -n "$RELAXEDPERMS" ]; then chmod ug=rw,o=r,a+X,g+s "$thisdir" else chmod u=rw,go=r,a+X,g+s "$thisdir" fi ifs="$IFS" # Set default permissions find "$thisdir" -mindepth 1 -maxdepth 1 -print | (while read thisitem; do # /shares.////* thisparentdir="`basename \"$thisitem\"`" IFS="/"; for exception in $exceptions; do IFS="$ifs"; if [ "$thisparentdir" = "$exception" ]; then continue 2 fi done chgrp -R "$rwgroup" "$thisitem" chmod -R ug=rw,o=r,a+X,g+s "$thisitem" 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_rw_update; do IFS="$ifs"; if [ -e "$thisdir/$dir" ]; then chmod ug=rw,o=r,a+X,g+s "$thisdir/$dir" fi done 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) # Ftp shares permissions for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex "^\./ftp_$user$"`; do chgrp -R "$user" "$HOME/$dir" chmod -R ug=rw,o=r,a+X,g+s "$HOME/$dir" rm -rf "$HOME/$dir/Network Trash Folder" mkdir "$HOME/$dir/Network Trash Folder" chown nobody: "$HOME/$dir/Network Trash Folder" chmod a= "$HOME/$dir/Network Trash Folder" done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex "^\./ftp_${user}_ro$"`; do chown -R "$user": "$HOME/$dir" chmod -R u=rw,go=r,a+X "$HOME/$dir" rm -rf "$HOME/$dir/Network Trash Folder" mkdir "$HOME/$dir/Network Trash Folder" chown nobody: "$HOME/$dir/Network Trash Folder" chmod a= "$HOME/$dir/Network Trash Folder" done # Web shares permissions for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./web_.*'`; do chown -R "$user": "$HOME/$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 "$HOME/$dir" # leftover from ancient times with another policy if [ $NETATALK ]; then rm -rf "$HOME/$dir/Network Trash Folder" fi done # Web shares permissions for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./websites$'`; do chown root: "$HOME/$dir" chmod a=r,u+w,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 2 -maxdepth 2 -type d -regex '^\./websites/.*'`; do chown -R "$user": "$HOME/$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 "$HOME/$dir" # leftover from ancient times with another policy if [ $NETATALK ]; then rm -rf "$HOME/$dir/Network Trash Folder" fi done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./webscripts$'`; do chown root: "$HOME/$dir" chmod a=r,u+w,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 2 -maxdepth 2 -type d -regex '^\./webscripts/.*'`; do chown -R $user: "$HOME/$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 "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./webdata$'`; do chown "$user": "$HOME/$dir" chmod a=r,u+w,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 2 -maxdepth 2 -type d -regex '^\./webdata/.*'`; do chown -R "$user": "$HOME/$dir" chmod -R u=rw,go=,u+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./webshareddata$'`; do chown "$user": "$HOME/$dir" chmod a=r,u+w,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 2 -maxdepth 2 -type d -regex '^\./webshareddata/.*'`; do chown -R "$user:" "$HOME/$dir" chmod -R u=rw,go=r,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./webphpsites$'`; do chown root: "$HOME/$dir" chmod u=rw,go=r,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 2 -maxdepth 2 -type d -regex '^\./webphpsites/.*'`; do chown -R "$user":www-data "$HOME/$dir" # chmod -R ug=rw,o=r,a+X $dir chmod -R ug=rw,o=,ug+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./webphpdata$'`; do chown root: "$HOME/$dir" chmod a=r,u+w,a+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 2 -maxdepth 2 -type d -regex '^\./webphpdata/.*'`; do chown -R "$user":www-data "$HOME/$dir" chmod -R ug=rw,o=,ug+X "$HOME/$dir" done for dir in `cd "$HOME" && find . -mindepth 1 -maxdepth 1 -type d -regex '^\./weblogs$'`; do chown -R "$user": "$HOME/$dir" chmod -R u=rw,g=r,o=,ug+X "$HOME/$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 apache_reload_needed="1" fi fi fi fi echo "." done if [ $XCHANGE ]; then for USER in $(ls $XDIRREAL); do id $user >/dev/null 2>&1 || rm -rf $XDIRREAL/$user done fi if [ "$apache_reload_needed" ]; then apache_do_reload="" case runmode in interactive) echo -n "Apache config changed. Reload Apache now (Y/n)? " read apache_reload case $apache_reload in y|Y|"") apache_do_reload="1" ;; esac ;; force) apache_do_reload="1" ;; *) echo "Apache config has changed. Remember to reload Apache...!" ;; esac if "$apache_do_reload" ]; then /etc/init.d/apache force-reload fi fi