1#!/bin/sh 2# 3# Copyright (c) 2020 The DragonFly Project. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in 14# the documentation and/or other materials provided with the 15# distribution. 16# 3. Neither the name of The DragonFly Project nor the names of its 17# contributors may be used to endorse or promote products derived 18# from this software without specific, prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 26# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 30# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31# SUCH DAMAGE. 32# 33 34# Exit if any untested command fails in non-interactive mode 35set -e 36# Exit when an undefined variable is referenced 37set -u 38 39# Usage: add_users <etcdir> <master.passwd> <group> 40# 41# Add new users and groups in <etcdir> according to the given <master.passwd> 42# and <group> files. 43# 44# NOTE: Existing users and groups are not modified. 45# 46# NOTE: There is circular dependence: 'groupadd' requires the members 47# already exist, while 'useradd' requires the group exists. 48# So first assign new users to the 'nogroup' group, and then make 49# adjustments after group creation. 50# 51add_users() { 52 local etcdir="$1" 53 local fpasswd="$2" 54 local fgroup="$3" 55 local _name _pw _uid _gid _gids item 56 local _class _change _expire _gecos _home _shell _members 57 58 echo "===> Adding new users ..." 59 _gids="" 60 while IFS=':' read -r _name _pw _uid _gid _class \ 61 _change _expire _gecos _home _shell; do 62 case ${_name} in 63 '' | \#*) continue ;; 64 esac 65 if pw -V ${etcdir} usershow ${_name} -q >/dev/null; then 66 continue 67 fi 68 echo " * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}" 69 # NOTE: The shell field can be empty (e.g., user 'toor') and 70 # would default to '/bin/sh'. 71 # NOTE: Use '-o' option to allow to create user of duplicate 72 # UID, which is required by the 'toor' user (same UID 73 # as 'root'). 74 pw -V ${etcdir} useradd ${_name} \ 75 -o \ 76 -u ${_uid} \ 77 -g nogroup \ 78 -d "${_home}" \ 79 -s "${_shell}" \ 80 -L "${_class}" \ 81 -c "${_gecos}" 82 _gids="${_gids} ${_name}:${_gid}" 83 done < ${fpasswd} 84 85 echo "===> Adding new groups ..." 86 while IFS=':' read -r _name _pw _gid _members; do 87 case ${_name} in 88 '' | \#*) continue ;; 89 esac 90 if pw -V ${etcdir} groupshow ${_name} -q >/dev/null; then 91 continue 92 fi 93 echo " * ${_name}: ${_gid}, ${_members}" 94 pw -V ${etcdir} groupadd ${_name} -g ${_gid} -M "${_members}" 95 done < ${fgroup} 96 97 echo "===> Adjusting the group of new users ..." 98 for item in ${_gids}; do 99 _name=${item%:*} 100 _gid=${item#*:} 101 echo " * ${_name}: ${_gid}" 102 pw -V ${etcdir} usermod ${_name} -g ${_gid} 103 done 104} 105 106# Usage: update_user <user> <etcdir> <master.passwd> 107# 108# Update an existing user in <etcdir> according to the given <master.passwd>. 109# 110update_user() { 111 local user="$1" 112 local etcdir="$2" 113 local fpasswd="$3" 114 local _line 115 local _name _pw _uid _gid _class _change _expire _gecos _home _shell 116 117 _line=$(grep "^${user}:" ${fpasswd}) || true 118 if [ -z "${_line}" ]; then 119 echo "ERROR: no such user '${user}'" >&2 120 exit 1 121 fi 122 123 echo "${_line}" | { 124 IFS=':' read -r _name _pw _uid _gid _class \ 125 _change _expire _gecos _home _shell 126 echo "===> Updating user ${user} ..." 127 echo " * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}" 128 pw -V ${etcdir} usermod ${user} \ 129 -u ${_uid} \ 130 -g ${_gid} \ 131 -d ${_home} \ 132 -s ${_shell} \ 133 -L "${_class}" \ 134 -c "${_gecos}" 135 } 136} 137 138# Usage: update_group <group> <etcdir> <group> 139# 140# Update an existing group in <etcdir> according to the given <group> file. 141# 142update_group() { 143 local group="$1" 144 local etcdir="$2" 145 local fgroup="$3" 146 local _line 147 local _name _pw _gid _members 148 149 _line=$(grep "^${group}:" ${fgroup}) || true 150 if [ -z "${_line}" ]; then 151 echo "ERROR: no such group '${group}'" >&2 152 exit 1 153 fi 154 155 echo "${_line}" | { 156 IFS=':' read -r _name _pw _gid _members 157 echo "===> Updating group ${group} ..." 158 echo " * ${_name}: ${_gid}, ${_members}" 159 pw -V ${etcdir} groupmod ${group} -g ${_gid} -M "${_members}" 160 } 161} 162 163usage() { 164 cat > /dev/stderr << _EOF_ 165Add/update users and groups. 166 167Usage: ${0##*/} -d <etc-dir> -g <group-file> -p <master.passwd-file> 168 [-G group] [-U user] 169 170_EOF_ 171 172 exit 1 173} 174 175ETC_DIR= 176GROUP_FILE= 177PASSWD_FILE= 178UPDATE_GROUP= 179UPDATE_USER= 180 181while getopts :d:G:g:hp:U: opt; do 182 case ${opt} in 183 d) 184 ETC_DIR=${OPTARG} 185 ;; 186 G) 187 UPDATE_GROUP=${OPTARG} 188 ;; 189 g) 190 GROUP_FILE=${OPTARG} 191 ;; 192 p) 193 PASSWD_FILE=${OPTARG} 194 ;; 195 U) 196 UPDATE_USER=${OPTARG} 197 ;; 198 h | \? | :) 199 usage 200 ;; 201 esac 202done 203 204shift $((OPTIND - 1)) 205[ $# -eq 0 ] || usage 206[ -n "${ETC_DIR}" ] || usage 207[ -n "${GROUP_FILE}" ] || usage 208[ -n "${PASSWD_FILE}" ] || usage 209 210if [ -z "${UPDATE_GROUP}" ] && [ -z "${UPDATE_USER}" ]; then 211 add_users "${ETC_DIR}" "${PASSWD_FILE}" "${GROUP_FILE}" 212else 213 if [ -n "${UPDATE_GROUP}" ]; then 214 update_group "${UPDATE_GROUP}" "${ETC_DIR}" "${GROUP_FILE}" 215 fi 216 if [ -n "${UPDATE_USER}" ]; then 217 update_user "${UPDATE_USER}" "${ETC_DIR}" "${PASSWD_FILE}" 218 fi 219fi 220