#!/bin/bash # # Author: Georg Voell - georg.voell@standby.cloud # Version: @(#)user-management 3.2.0 12.08.2024 (c)2024 Standby.cloud # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # # This script can be used free of charge. Use it as is or customize as needed. It is not guaranteed to be # error free and you will not be reimbursed for any damage it may cause. # #@ Manages users within Linux. #@ #@Usage: user-management [options] [action] username [groupname] #@ Options: #@ -h, --help : Displays helptext. #@ -v, --version : Displays the version of the script. #@ -a, --all : Display all users. #@ -u, --uid : User ID. #@ -c, --comment : Comment. #@ -s, --shell : Login Shell. #@ -d, --home : Home directory. #@ -g, --groups : Additional groups (comma separated) or empty string to remove groups. #@ #@ Action: #@ show : Show users. #@ check : Check if user exists and has the given attributes - otherwise create or change. #@ lock : Lock user. #@ unlock: Unlock user. #@ delete: Delete user. #@ #@Examples: #@ user-management check oracle dba -c "Oracle user" #@ Create user 'oracle' (if it doesn't exist) and assign group 'dba'. #@ user-management delete oracle #@ Delete user 'oracle'. # # Exit codes: # 01: Unknown or wrong parameter and only LINUX supported and script needs to be executed with ROOT privileges. # 02: Unknown action. # 03: No username specified. # 99: User interrupt. # # See also: # **bootstrap**(1), **disk-management**(1), **key-management**(1), **install-scripts**(1) # # ToDo: # # Known bugs: # # Update history: # # V 3.0.0 15.07.2020 New version # V 3.0.1 16.05.2021 List all users # V 3.0.2 22.05.2021 More actions # V 3.0.3 04.06.2023 Minor bugfixes (create new user with specified home directory) # V 3.1.0 05.06.2023 New copyright # V 3.2.0 12.08.2024 New minor version # # Find executable bash library and source it lib=`which lib.bash 2>/dev/null | sed 's|^no 'lib.bash' in .*||'` if [ "$lib" != "" ]; then source "$lib" else progdir=`dirname "$0"` if [ -r "${progdir}/lib.bash" ]; then source "${progdir}/lib.bash" else echo "Unexpected error: Unable to locate bash library 'lib.bash'." exit 1 fi fi # Read configuration for users function ReadConfig() { local config="/etc/login.defs" if [ -r "$config" ]; then cat $config | tr -s ' ' ' ' | tr -s ' ' ' ' > $scratchfile uid_min=`grep "^UID_MIN " $scratchfile | cut -d' ' -f2` uid_max=`grep "^UID_MAX " $scratchfile | cut -d' ' -f2` create_home=`grep "^CREATE_HOME " $scratchfile | cut -d' ' -f2 | tolower` fi } # Display all regular users function DisplayUsers() { local username="" local pass="" local uid=0 local gid=0 local comment="" local home="" local shell="" local result="" local mgroup="" local ogroup="" local agroups="" local keys="" local token="" local key="" local name="" if [ -r "$passwdfile" ]; then printf "username\tpassword\tuid\tgid\tgroups\tcomment\thome\tshell\tkeys\n" > $scratchfile while IFS=':' read -r username pass uid gid comment home shell; do if [ "$showall" = true -o $uid -ge $uid_min -a $uid -le $uid_max ]; then if [ "$pass" = " " -o "$pass" = "" ]; then pass="None" elif [ "$pass" = "*" ]; then pass="Nologin" elif [ "$pass" != "x" ]; then result=`grep "^!.*"` if [ "$result" != "" ]; then pass="Locked" else pass="Set" fi else pass="Unknown" if [ -r "$shadowfile" ]; then result=`grep "^${username}:" $shadowfile` if [ "$result" != "" ]; then pass=`echo "$result" | cut -d':' -f2` if [ "$pass" = "!!" ]; then pass="None" elif [ "$pass" = '*' -o "$pass" = '!' ]; then pass="Nologin" else result=`echo "$pass" | grep "^!.*"` if [ "$result" != "" ]; then pass="Locked" else pass="Set" fi fi fi fi fi if [ "$comment" = "" ]; then comment="None" fi mgroup=`grep "^.*:.*:$gid:" $groupfile | cut -d':' -f1` ogroup=`grep "$username" $groupfile | grep -v "^${mgroup}:" | cut -d':' -f1` if [ "$ogroup" = "" ]; then if [ "$mgroup" = "" ]; then agroups="" else agroups="($mgroup)" fi else if [ "$mgroup" = "" ]; then agroups=`printf "$ogroup" | tr '\n' ','` else agroups=`printf "(${mgroup}),$ogroup" | tr '\n' ','` fi fi keys="" if [ ! -r "${home}/$keyfile" ]; then keys="None" else while IFS=' ' read -r token key name; do if [ "$token" = "ssh-rsa" ]; then if [ "$name" = "" ]; then name="Nameless" fi if [ "$keys" = "" ]; then keys="$name" else keys="${keys},$name" fi fi done < <(cat "${home}/$keyfile" | stripcomment | sed 's|^.*ssh-rsa |ssh-rsa |') if [ "$keys" = "" ]; then keys="None" fi fi printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$username" "$pass" "$uid" "$gid" "$agroups" "$comment" "$home" "$shell" "$keys" >> $scratchfile fi done < <(cat "$passwdfile" | stripcomment | grep .) if [ "$showall" = true ]; then printf "\nAll users:\n\n" else printf "\nRegular users:\n\n" fi print-table --import $scratchfile fi } function CreateUser() { local username=${1} local groupname=${2} local newhome="" local stat=0 if [ "$username" != "" -a "$groupname" != "" ]; then # If we don't have a specific homedir - use a default if [ "$home" = "" ]; then newhome="/home/$username" else newhome="$home" fi # Create the user if [ "$create_home" = "yes" ]; then useradd "$username" -g "$groupname" -m -d "$newhome" 2>/dev/null stat=$? else useradd "$username" -g "$groupname" -d "$newhome" 2>/dev/null stat=$? fi if [ $stat -gt 0 ]; then errormsg 0 "Unable to create user '$username'." else # Modify if [ "$uid" != "" ]; then usermod -u "$uid" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to set UID to '$uid' for user '$username'. Using default." fi fi if [ "$comment" != "" ]; then usermod -c "$comment" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to set comment to '$comment' for user '$username'." fi fi if [ "$shell" != "" ]; then usermod -s "$shell" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to set login shell to '$shell' for user '$username'." fi fi if [ "$addgroups" != "NoneSpecified" ]; then usermod -G "$addgroups" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to change secondary groups '$addgroups' for user '$username'." fi fi fi else errormsg 0 "Unexpected: Empty username or groupname." fi } function LockUser() { local username=${1} local userstr="" local result="" local pass="" local stat=0 if [ -r "$passwdfile" -a "$username" != "" ]; then userstr=`cat "$passwdfile" | stripcomment | grep "^${username}:"` if [ "$userstr" != "" ]; then if [ -r "$shadowfile" ]; then result=`grep "^${username}:" $shadowfile` if [ "$result" != "" ]; then pass=`echo "$result" | cut -d':' -f2` fi fi if [ "$pass" = "!!" ]; then errormsg 0 "Password not set for user '$username'." else usermod -L "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to lock user '$username'." fi fi else errormsg 0 "User '$username' does not exist." fi fi } function UnlockUser() { local username=${1} local userstr="" local stat=0 if [ -r "$passwdfile" -a "$username" != "" ]; then userstr=`cat "$passwdfile" | stripcomment | grep "^${username}:"` if [ "$userstr" != "" ]; then usermod -U "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to unlock user '$username'." fi else errormsg 0 "User '$username' does not exist." fi fi } function DeleteUser() { local username=${1} local userstr="" local stat=0 if [ -r "$passwdfile" -a "$username" != "" ]; then userstr=`cat "$passwdfile" | stripcomment | grep "^${username}:"` if [ "$userstr" != "" ]; then userdel -f -r "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to delete user '$username'." fi else errormsg 0 "User '$username' does not exist." fi fi } function CheckUser() { local username=${1} local groupname=${2} local userstr="" local groupstr="" local olduid="" local oldgid="" local oldcomment="" local oldhome="" local oldshell="" local gid=100 local stat=0 if [ -r "$passwdfile" -a -r "$groupfile" -a "$username" != "" ]; then userstr=`cat "$passwdfile" | stripcomment | grep "^${username}:"` if [ "$userstr" != "" ]; then # User exists # username pass uid gid comment home shell olduid=`echo "$userstr" | cut -d':' -f3` oldgid=`echo "$userstr" | cut -d':' -f4` oldcomment=`echo "$userstr" | cut -d':' -f5` oldhome=`echo "$userstr" | cut -d':' -f6` oldshell=`echo "$userstr" | cut -d':' -f7` if [ "$uid" != "" ]; then if [ "$olduid" != "$uid" ]; then usermod -u "$uid" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to change UID from '$olduid' to '$uid' for user '$username'." fi fi fi if [ "$groupname" != "" ]; then groupstr=`cat "$groupfile" | stripcomment | grep "^${groupname}:"` if [ "$groupstr" = "" ]; then # Group does not exists - create it first groupadd "$groupname" stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to create group '$groupname'. Using default group 'users'." groupname="users" fi groupstr=`cat "$groupfile" | stripcomment | grep "^${groupname}:"` fi if [ "$groupstr" != "" ]; then # groupname pass gid member gid=`echo "$groupstr" | cut -d':' -f3` if [ "$oldgid" != "$gid" ]; then usermod -g "$gid" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to change group '$groupname' from '$oldgid' to '$gid' for user '$username'." fi fi fi fi if [ "$comment" != "" ]; then if [ "$oldcomment" != "$comment" ]; then usermod -c "$comment" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to change comment from '$oldcomment' to '$comment' for user '$username'." fi fi fi if [ "$home" != "" ]; then if [ "$oldhome" != "$home" ]; then if [ "$create_home" = "yes" ]; then usermod -m -d "$home" "$username" 2>/dev/null stat=$? else usermod -d "$home" "$username" 2>/dev/null stat=$? fi if [ $stat -gt 0 ]; then errormsg 0 "Unable to change home dir from '$oldhome' to '$home' for user '$username'." fi fi fi if [ "$shell" != "" ]; then if [ "$oldshell" != "$shell" ]; then usermod -s "$shell" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to change login shell from '$oldshell' to '$shell' for user '$username'." fi fi fi if [ "$addgroups" != "NoneSpecified" ]; then usermod -G "$addgroups" "$username" 2>/dev/null stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to change secondary groups '$addgroups' for user '$username'." fi fi else # User does not exist yet if [ "$groupname" = "" ]; then # Default group for users groupname="users" fi groupstr=`cat "$groupfile" | stripcomment | grep "^${groupname}:"` if [ "$groupstr" = "" ]; then # Group does not exists - create it first groupadd "$groupname" stat=$? if [ $stat -gt 0 ]; then errormsg 0 "Unable to create group '$groupname'. Using default group 'users'." groupname="users" fi fi # Create user CreateUser "$username" "$groupname" fi fi } # Check if we are running on Linux and have root privileges. if [ "$OS" != "Linux" ]; then errstr="This script only supports LINUX." elif [ "$USER" != "root" ]; then # Try sudo check-sudo stat=$? if [ $stat -eq 0 ]; then printf 'sudo -n "%s"' "$script" > $scratchfile while [ $# -gt 0 ]; do pname=${1} printf ' "%s"' "$pname" >> $scratchfile shift done # Execute script chmod 700 $scratchfile $scratchfile stat=$? # Cleanup rm -f $scratchfile exit $stat else # Need root privileges errstr="Root privileges needed to continue." fi fi # Preset action="" param1="" param2="" comment="" shell="" home="" uid="" addgroups="NoneSpecified" showall=false uid_min=1000 uid_max=60000 create_home="yes" # Check parameters: Loop until all parameters are used up while [ $# -gt 0 ]; do pname=${1} case "$pname" in -v | --version) shift showversion=true ;; -h | --help) shift showhelp=true ;; -a | --all) shift showall=true ;; -u | --uid) shift if [ "$1" != "" ]; then uid=${1} result=`echo "$uid" | grep '^[0123456789]*$'` if [ "$result" = "" ]; then errstr="Id has to be a number." fi shift else errstr="Please specify an id after parameter '$pname'." fi ;; -c | --comment) shift if [ "$1" != "" ]; then comment=${1} shift else errstr="Please specify a comment after parameter '$pname'." fi ;; -s | --shell) shift if [ "$1" != "" ]; then shell=${1} result=`filecheck -x "$shell"` if [ "$result" = "" ]; then errstr="Login shell '$shell' does not exist or is not executable." fi shift else errstr="Please specify a comment after parameter '$pname'." fi ;; -d | --home) shift if [ "$1" != "" ]; then home=${1} shift else errstr="Please specify a directory after parameter '$pname'." fi ;; -g | --groups) shift paramck=`echo "$1" | grep '^-'` if [ "$paramck" = "" ]; then addgroups=${1} shift else errstr="Please specify none (empty string), one or more groups (comma separated) after parameter '$pname'." fi ;; *) shift paramck=`echo "$pname" | grep '^-'` # Keys don't begin with '-' if [ "$paramck" != "" ]; then errstr="Unknown option '$pname'." else if [ "$action" = "" ]; then action=`echo "$pname" | tolower` elif [ "$param1" = "" ]; then param1="$pname" elif [ "$param2" = "" ]; then param2="$pname" else errstr="Unknown additional parameter: '$pname'." fi fi esac done # Display help or error message DisplayHelp # Plausibilty check if [ "$action" = "" ]; then action="show" fi # Main passwdfile="/etc/passwd" shadowfile="/etc/shadow" gshadowfile="/etc/gshadow" groupfile="/etc/group" keyfile=".ssh/authorized_keys" if [ "$USER" = "root" ]; then ReadConfig case "$action" in show) DisplayUsers ;; check) if [ "$param1" = "" ]; then exitcode=3 errormsg $exitcode "No username specified." else CheckUser "$param1" "$param2" fi ;; lock) if [ "$param1" = "" ]; then exitcode=3 errormsg $exitcode "No username specified." else LockUser "$param1" fi ;; unlock) if [ "$param1" = "" ]; then exitcode=3 errormsg $exitcode "No username specified." else UnlockUser "$param1" fi ;; delete) if [ "$param1" = "" ]; then exitcode=3 errormsg $exitcode "No username specified." else DeleteUser "$param1" fi ;; *) exitcode=2 errormsg $exitcode "Unknown action: '$action'." esac fi # Cleanup and exit Cleanup exit $exitcode