mirror of
https://github.com/PureDarwin/PureDarwin.git
synced 2026-01-25 12:16:26 +00:00
475 lines
11 KiB
Bash
Executable File
475 lines
11 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2007-2008 The PureDarwin Project.
|
|
# All rights reserved.
|
|
#
|
|
# @LICENSE_HEADER_START@
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
# @LICENSE_HEADER_END@
|
|
#
|
|
|
|
#
|
|
# aladin <aladin@puredarwin.org>
|
|
#
|
|
|
|
#
|
|
# Add users without dscl in Darwin 9
|
|
# based on pd_adduser
|
|
#
|
|
|
|
#
|
|
# Changelog
|
|
#
|
|
# 20080321 - See `hg log' (mercurial) from now - aladin
|
|
# 20081211 - Adding shell choice - aladin
|
|
# 20081209 - License header update - aladin
|
|
# 20080831 - Minor updates - aladin
|
|
# 20080827 - Minor updates
|
|
# typo fixed - aladin
|
|
# 20080817 - <user>.state file added
|
|
# Minor updates - aladin
|
|
# 20080808 - Initial version - aladin
|
|
#
|
|
|
|
# Ensure root exclusivity
|
|
if [ "$UID" -ne 0 ]
|
|
then
|
|
echo "You must be root in order to use $(basename $0)"
|
|
exit 1
|
|
fi
|
|
|
|
#
|
|
# Set up variables
|
|
#
|
|
|
|
OPENSSL="/usr/bin/openssl"
|
|
GREP="/usr/bin/grep"
|
|
HEAD="/usr/bin/head"
|
|
SORT="/usr/bin/sort"
|
|
XXD="/usr/bin/xxd"
|
|
AWK="/usr/bin/awk"
|
|
|
|
# Mount devfs (e.g., /dev/null, /dev/random, etc.. are needed)
|
|
/sbin/mount_devfs devfs /dev
|
|
|
|
# Default target volume
|
|
MOUNT=/
|
|
if [ "$1" != "" ]
|
|
then
|
|
# Ensure "target" exists
|
|
if [ ! -e "$1" ]
|
|
then
|
|
echo "Unknown volume name: $1"
|
|
exit -1
|
|
fi
|
|
# Set new target
|
|
MOUNT="$1"
|
|
fi
|
|
echo "Target volume name: $MOUNT"
|
|
echo ""
|
|
|
|
# User existence
|
|
EXISTS=0
|
|
|
|
# Usage
|
|
#
|
|
# ./pd_injectuser target default_username
|
|
# $0 $1 $2
|
|
T_USERNAME=$2
|
|
|
|
#
|
|
# Interactive
|
|
#
|
|
|
|
# Prompt for username (if no arg specified above)
|
|
if [ "$T_USERNAME" = "" ]; then
|
|
# Infinite loop until username is not blank
|
|
while [ 1 ]; do
|
|
printf "Username: "
|
|
read T_USERNAME;
|
|
|
|
if [ ! "$T_USERNAME" = "" ]; then
|
|
break
|
|
else
|
|
echo "Username must not be blank, try again."
|
|
fi
|
|
done
|
|
else
|
|
T_DEFAULT="TRUE"
|
|
echo "Entering default mode"
|
|
fi
|
|
|
|
|
|
# Check whether user already exists
|
|
if [ -e ${MOUNT}/private/var/db/dslocal/nodes/Default/users/${T_USERNAME}.plist ]; then
|
|
echo "${T_USERNAME} already exists"
|
|
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
printf "Overwrite? y[n]: "
|
|
read CHOICE
|
|
else
|
|
echo "Overwrite forced by default mode"
|
|
CHOICE="y"
|
|
fi
|
|
|
|
if [ "$CHOICE" = "y" ]; then
|
|
EXISTS=1
|
|
else
|
|
echo "Aborted."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Prompt for realname (if no arg specified above)
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
printf "Realname: "
|
|
read T_REALNAME
|
|
else
|
|
T_REALNAME="$T_USERNAME"
|
|
fi
|
|
|
|
# Generating a random Universally Unique IDentifier (UUID)
|
|
#
|
|
# ie for user: e0d36085-8a80-238b-abfb-c537c7b36378
|
|
# ie for root: FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000
|
|
#
|
|
T_UUID="` $OPENSSL rand 4 | $XXD -p -u`-`$OPENSSL rand 2 |
|
|
$XXD -p -u`-`$OPENSSL rand 2 | $XXD -p -u`-`$OPENSSL rand 2 |
|
|
$XXD -p -u`-`$OPENSSL rand 6 | $XXD -p -u`"
|
|
|
|
# Find max uid
|
|
M_UID=`cat ${MOUNT}/var/db/dslocal/nodes/Default/users/*.plist |
|
|
$GREP -v generateduid | $GREP -A2 uid |
|
|
$GREP string | $AWK -F ">|<" ' {printf $3"\n"}' |
|
|
$SORT -rg | $HEAD -n1`
|
|
|
|
if [ "$T_USERNAME" == "root" ]; then
|
|
# echo "Warning: Security consequences involved"
|
|
M_UID=0
|
|
T_UUID="FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000"
|
|
else
|
|
if [ $M_UID -lt 1000 ]; then
|
|
M_UID=1000
|
|
else
|
|
M_UID=`expr $M_UID + 1`
|
|
fi
|
|
fi
|
|
|
|
T_UID_DEFAULT=$M_UID
|
|
T_GID_DEFAULT=$M_UID
|
|
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
printf "UID (default: $T_UID_DEFAULT): "
|
|
read T_UID
|
|
|
|
printf "GID (default: $T_GID_DEFAULT): "
|
|
read T_GID
|
|
fi
|
|
|
|
if [ "$T_UID" = "" ]; then
|
|
T_UID=${T_UID_DEFAULT}
|
|
fi
|
|
|
|
#if [ "$T_UID" = "0" ]; then
|
|
# echo "Warning: Security consequences involved"
|
|
#fi
|
|
|
|
#if [ "$T_GID" = "" ]; then
|
|
# T_GID=${T_GID_DEFAULT}
|
|
#fi
|
|
|
|
if [ "$T_GID" = "0" ]; then
|
|
echo "Warning: Security consequences involved"
|
|
fi
|
|
|
|
if [ "$T_USERNAME" == "root" ]; then
|
|
T_HOMEDIR_DEFAULT="/var/$T_USERNAME"
|
|
else
|
|
T_HOMEDIR_DEFAULT="/Users/$T_USERNAME"
|
|
fi
|
|
|
|
|
|
# Default shell
|
|
T_SHELL_DEFAULT=/bin/zsh
|
|
#SHELL_VALID=0
|
|
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
# Prompt for default shell
|
|
echo "Available shells:"
|
|
echo "`cat $MOUNT/etc/shells | $GREP bin`"
|
|
# Infinite loop until a valid shell is entered
|
|
while [ 1 ]; do
|
|
printf "Shell (default: $T_SHELL_DEFAULT): "
|
|
read T_SHELL;
|
|
|
|
if [ ! "$T_SHELL" = "" ]; then
|
|
$GREP $T_SHELL $MOUNT/etc/shells > /dev/null 2> /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
break
|
|
else
|
|
echo "Insert valid shell, try again."
|
|
fi
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Default shell
|
|
if [ "$T_SHELL" = "" ]; then
|
|
T_SHELL=$T_SHELL_DEFAULT
|
|
fi
|
|
|
|
# Prompt for home directory
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
printf "Home directory (default: $T_HOMEDIR_DEFAULT): "
|
|
read T_HOMEDIR;
|
|
fi
|
|
|
|
# Default home directory
|
|
if [ "$T_HOMEDIR" = "" ]; then
|
|
T_HOMEDIR=$T_HOMEDIR_DEFAULT
|
|
fi
|
|
|
|
# Check whether home directory already exists
|
|
if [ -e "${MOUNT}${T_HOMEDIR}" ]; then
|
|
echo "${MOUNT}${T_HOMEDIR} already exists"
|
|
#exit1
|
|
fi
|
|
|
|
# Prompt for admin group
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
printf "Add ${T_USERNAME} to the admin group? [y]n: "
|
|
read CHOICE;
|
|
if [ ! "$CHOICE" = "n" ]; then
|
|
ADMIN_USER=1
|
|
fi
|
|
else
|
|
echo "Granted (Admin) by default mode"
|
|
ADMIN_USER=1
|
|
fi
|
|
|
|
# Disabling tty output
|
|
stty -echo
|
|
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
# Infinite loop until password was entered the same twice
|
|
while [ 1 ]; do
|
|
printf "Password: "
|
|
read T_PASSWORD;
|
|
echo ""
|
|
|
|
printf "Verifying - Password: "
|
|
read V_PASSWORD;
|
|
echo ""
|
|
|
|
if [ "$T_PASSWORD" = "$V_PASSWORD" ]; then
|
|
break
|
|
else
|
|
echo "Verify failure"
|
|
fi
|
|
done
|
|
else
|
|
T_PASSWORD="$T_USERNAME"
|
|
fi
|
|
|
|
# Enabling tty output
|
|
stty echo
|
|
|
|
# Display summary
|
|
echo ""
|
|
echo "Generated UUID: ${T_UUID}"
|
|
echo "Username: ${T_USERNAME}"
|
|
echo "Realname: ${T_REALNAME}"
|
|
echo "Password: ********"
|
|
echo "UID: ${T_UID}"
|
|
if [ "$ADMIN_USER" == "1" ]; then
|
|
echo "GID: 80, ${T_GID}"
|
|
else
|
|
echo "GID: ${T_GID}"
|
|
fi
|
|
echo "Shell: ${T_SHELL}"
|
|
echo "Home: ${T_HOMEDIR}"
|
|
echo ""
|
|
|
|
if [ "$T_DEFAULT" = "" ]; then
|
|
printf "Add [y]n: "
|
|
read CHOICE
|
|
if [ "$CHOICE" = "n" ]; then
|
|
echo "Aborted."
|
|
exit 0
|
|
fi
|
|
fi
|
|
#
|
|
# Create user plist file
|
|
#
|
|
|
|
USER_PLIST="${MOUNT}/var/db/dslocal/nodes/Default/users/${T_USERNAME}.plist"
|
|
|
|
cat >$USER_PLIST<<EOOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>authentication_authority</key>
|
|
<array>
|
|
<string>;ShadowHash;</string>
|
|
</array>
|
|
<key>generateduid</key>
|
|
<array>
|
|
<string>${T_UUID}</string>
|
|
</array>
|
|
<key>gid</key>
|
|
<array>
|
|
<string>${T_GID}</string>
|
|
</array>
|
|
<key>home</key>
|
|
<array>
|
|
<string>/Users/${T_USERNAME}</string>
|
|
</array>
|
|
<key>name</key>
|
|
<array>
|
|
<string>${T_USERNAME}</string>
|
|
</array>
|
|
<key>passwd</key>
|
|
<array>
|
|
<string>********</string>
|
|
</array>
|
|
<key>realname</key>
|
|
<array>
|
|
<string>${T_REALNAME}</string>
|
|
</array>
|
|
<key>shell</key>
|
|
<array>
|
|
<string>${T_SHELL}</string>
|
|
</array>
|
|
<key>uid</key>
|
|
<array>
|
|
<string>${T_UID}</string>
|
|
</array>
|
|
</dict>
|
|
</plist>
|
|
EOOF
|
|
|
|
#
|
|
# Create shadow hash file (620 bytes)
|
|
#
|
|
# 1240 hexadecimal characters:
|
|
# ( 64 + 40 + 64 ) + ( 8 + 40 ) + ( 1024 )
|
|
#
|
|
|
|
mkdir -p ${MOUNT}/private/var/db/shadow/hash
|
|
# Init character pointer
|
|
i=0
|
|
|
|
# 168 hexadecimal characters
|
|
#
|
|
# 64 characters for the LANMAN password (require "Windows sharing" enabled)
|
|
# 40 characters for the "unsalted" SHA1 hash
|
|
# 64 characters for md5 hash, at least it's a power of 2 (2^6)
|
|
#
|
|
while (( i++ < 168 ))
|
|
do
|
|
printf "0" >> ${MOUNT}/private/var/db/shadow/hash/${T_UUID}
|
|
done
|
|
|
|
# Hexadecimal salt (ie: FC2B0C2E)
|
|
T_HEX_SALT=`$OPENSSL rand 4 | $XXD -p -u`
|
|
|
|
# Binary salt (reverted from hex, plain hexdump style)
|
|
T_BIN_SALT=`printf ${T_HEX_SALT} | $XXD -r -u -p`
|
|
|
|
# Generated sha1 hash with provided password and binary salt
|
|
T_SHA1_HASH=`printf "%s%s" ${T_BIN_SALT} ${T_PASSWORD} | $OPENSSL dgst -sha1 | tr ' a-z' ' A-Z'`
|
|
|
|
# 8 characters for the hexadecimal salt
|
|
printf ${T_HEX_SALT} >> ${MOUNT}/private/var/db/shadow/hash/${T_UUID}
|
|
# 40 characters for the sha1 hash
|
|
printf ${T_SHA1_HASH} >> ${MOUNT}/private/var/db/shadow/hash/${T_UUID}
|
|
|
|
# iJump the hash!
|
|
i=$[$i+48]
|
|
|
|
# 1024 characters for "unknow reason (future big hash?)", at least it's another power of 2 (2^10)
|
|
while (( i++ < 1241 ))
|
|
do
|
|
printf "0" >> ${MOUNT}/private/var/db/shadow/hash/${T_UUID}
|
|
done
|
|
|
|
#
|
|
# Create shadow hash file .state (not mandatory)
|
|
#
|
|
|
|
T_CREATION_DATE=`date "+%Y-%m-%dT%H:%M:%SZ"`
|
|
sleep 1
|
|
T_LASTLOGIN_DATE=`date "+%Y-%m-%dT%H:%M:%SZ"`
|
|
|
|
cat >${MOUNT}/private/var/db/shadow/hash/${T_UUID}.state<<EOOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CreationDate</key>
|
|
<date>${T_CREATION_DATE}</date>
|
|
<key>FailedLoginCount</key>
|
|
<integer>0</integer>
|
|
<key>LastLoginDate</key>
|
|
<date>${T_LASTLOGIN_DATE}</date>
|
|
<key>NewPasswordRequired</key>
|
|
<integer>0</integer>
|
|
</dict>
|
|
</plist>
|
|
EOOF
|
|
|
|
# Add to admin group
|
|
if [ "$ADMIN_USER" = "1" ];
|
|
then
|
|
$GREP "<string>${T_USERNAME}</string>" "${MOUNT}/private/var/db/dslocal/nodes/Default/groups/admin.plist"
|
|
if [ $? -eq 1 ];
|
|
then
|
|
cp ${MOUNT}/private/var/db/dslocal/nodes/Default/groups/admin.plist ${MOUNT}/private/var/db/dslocal/nodes/Default/groups/admin.plist.origin
|
|
cat ${MOUNT}/private/var/db/dslocal/nodes/Default/groups/admin.plist.origin |
|
|
sed "s/<string>root<\/string>/<string>root<\/string><string>${T_USERNAME}<\/string>/" > ${MOUNT}/private/var/db/dslocal/nodes/Default/groups/admin.plist
|
|
# FIXME missing \n\t\t as ...>root</string>\n\t\t<string>${T_US...
|
|
# Alternative
|
|
#perl -pi -e "s|<string>root</string>|<string>root</string>\n\t\t<string>${T_USERNAME}</string>|g" ${MOUNT}/private/var/db/dslocal/nodes/Default/groups/admin.plist
|
|
else
|
|
echo "${T_USERNAME} is already found in admin group"
|
|
fi
|
|
fi
|
|
|
|
# Create and populate home directory
|
|
mkdir -p ${MOUNT}${T_HOMEDIR}
|
|
chown ${T_UID}:${T_GID} ${MOUNT}${T_HOMEDIR}
|
|
|
|
# Auto reload DirectoryService in order to avoid reboot to make effect
|
|
if [ "$MOUNT" = "/" ]; then
|
|
launchctl stop com.apple.DirectoryServices
|
|
fi
|
|
|
|
echo "User ${T_USERNAME} created"
|
|
|
|
# Symetry: restoring state
|
|
/sbin/umount -f /dev
|