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# 46add_users() { 47 local etcdir="$1" 48 local fpasswd="$2" 49 local fgroup="$3" 50 local _name _pw _uid _gid _gids _group item 51 local _class _change _expire _gecos _home _shell _members 52 53 echo "===> Adding new users ..." 54 _gids="" 55 while IFS=':' read -r _name _pw _uid _gid _class \ 56 _change _expire _gecos _home _shell; do 57 case ${_name} in 58 '' | \#*) continue ;; 59 esac 60 if pw -V ${etcdir} usershow ${_name} -q >/dev/null; then 61 continue 62 fi 63 echo " * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}" 64 65 _group=${_gid} 66 if ! pw -V ${etcdir} groupshow ${_gid} -q >/dev/null; then 67 # Primary group doesn't exist yet, so first assign to 68 # the 'nogroup' group, and then adjust it after 69 # creating the group. 70 _group="nogroup" 71 _gids="${_gids} ${_name}:${_gid}" 72 fi 73 74 # NOTE: The shell field can be empty (e.g., user 'toor') and 75 # would default to '/bin/sh'. 76 # NOTE: Use '-o' option to allow to create user of duplicate 77 # UID, which is required by the 'toor' user (same UID 78 # as 'root'). 79 pw -V ${etcdir} useradd ${_name} \ 80 -o \ 81 -u ${_uid} \ 82 -g ${_group} \ 83 -d "${_home}" \ 84 -s "${_shell}" \ 85 -L "${_class}" \ 86 -c "${_gecos}" 87 done < ${fpasswd} 88 89 echo "===> Adding new groups ..." 90 while IFS=':' read -r _name _pw _gid _members; do 91 case ${_name} in 92 '' | \#*) continue ;; 93 esac 94 if pw -V ${etcdir} groupshow ${_name} -q >/dev/null; then 95 continue 96 fi 97 echo " * ${_name}: ${_gid}, ${_members}" 98 pw -V ${etcdir} groupadd ${_name} -g ${_gid} -M "${_members}" 99 done < ${fgroup} 100 101 echo "===> Adjusting the group of new users ..." 102 for item in ${_gids}; do 103 _name=${item%:*} 104 _gid=${item#*:} 105 echo " * ${_name}: ${_gid}" 106 pw -V ${etcdir} usermod ${_name} -g ${_gid} 107 done 108} 109 110# Usage: update_user <user> <etcdir> <master.passwd> 111# 112# Update an existing user in <etcdir> according to the given <master.passwd>. 113# 114update_user() { 115 local user="$1" 116 local etcdir="$2" 117 local fpasswd="$3" 118 local _line 119 local _name _pw _uid _gid _class _change _expire _gecos _home _shell 120 121 _line=$(grep "^${user}:" ${fpasswd}) || true 122 if [ -z "${_line}" ]; then 123 echo "ERROR: no such user '${user}'" >&2 124 exit 1 125 fi 126 127 echo "${_line}" | { 128 IFS=':' read -r _name _pw _uid _gid _class \ 129 _change _expire _gecos _home _shell 130 echo "===> Updating user ${user} ..." 131 echo " * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}" 132 pw -V ${etcdir} usermod ${user} \ 133 -u ${_uid} \ 134 -g ${_gid} \ 135 -d ${_home} \ 136 -s ${_shell} \ 137 -L "${_class}" \ 138 -c "${_gecos}" 139 } 140} 141 142# Usage: update_group <group> <etcdir> <group> 143# 144# Update an existing group in <etcdir> according to the given <group> file. 145# 146update_group() { 147 local group="$1" 148 local etcdir="$2" 149 local fgroup="$3" 150 local _line 151 local _name _pw _gid _members 152 153 _line=$(grep "^${group}:" ${fgroup}) || true 154 if [ -z "${_line}" ]; then 155 echo "ERROR: no such group '${group}'" >&2 156 exit 1 157 fi 158 159 echo "${_line}" | { 160 IFS=':' read -r _name _pw _gid _members 161 echo "===> Updating group ${group} ..." 162 echo " * ${_name}: ${_gid}, ${_members}" 163 pw -V ${etcdir} groupmod ${group} -g ${_gid} -M "${_members}" 164 } 165} 166 167usage() { 168 cat > /dev/stderr << _EOF_ 169Add/update users and groups. 170 171Usage: ${0##*/} -d <etc-dir> -g <group-file> -p <master.passwd-file> 172 [-G group] [-U user] 173 174_EOF_ 175 176 exit 1 177} 178 179ETC_DIR= 180GROUP_FILE= 181PASSWD_FILE= 182UPDATE_GROUP= 183UPDATE_USER= 184 185while getopts :d:G:g:hp:U: opt; do 186 case ${opt} in 187 d) 188 ETC_DIR=${OPTARG} 189 ;; 190 G) 191 UPDATE_GROUP=${OPTARG} 192 ;; 193 g) 194 GROUP_FILE=${OPTARG} 195 ;; 196 p) 197 PASSWD_FILE=${OPTARG} 198 ;; 199 U) 200 UPDATE_USER=${OPTARG} 201 ;; 202 h | \? | :) 203 usage 204 ;; 205 esac 206done 207 208shift $((OPTIND - 1)) 209[ $# -eq 0 ] || usage 210[ -n "${ETC_DIR}" ] || usage 211[ -n "${GROUP_FILE}" ] || usage 212[ -n "${PASSWD_FILE}" ] || usage 213 214if [ -z "${UPDATE_GROUP}" ] && [ -z "${UPDATE_USER}" ]; then 215 add_users "${ETC_DIR}" "${PASSWD_FILE}" "${GROUP_FILE}" 216else 217 if [ -n "${UPDATE_GROUP}" ]; then 218 update_group "${UPDATE_GROUP}" "${ETC_DIR}" "${GROUP_FILE}" 219 fi 220 if [ -n "${UPDATE_USER}" ]; then 221 update_user "${UPDATE_USER}" "${ETC_DIR}" "${PASSWD_FILE}" 222 fi 223fi 224