diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b04d16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +/*.html +/*.css +/*.cache +/*.1 +/*.1.gz +/*.tgz +/*.md5 +/*.sha256 +/*.sig +/logo.svg +/update-passwd.sed +/update-passwd +/AUTHORS +/AUTHORS.txt +/passwd-fink.conf +/group-fink.conf diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..1c0c82d --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Alexander Hansen diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cab4870 --- /dev/null +++ b/Makefile @@ -0,0 +1,159 @@ +# Makefile for the passwd project + +# `a2x / asciidoc` is required to generate the Man page. +# `markdown` is required for the `docs` target, though it is not +# strictly necessary for packaging since unless you are planning on +# serving the docs on a web site they are more readable not as html. +# `shipper` and `gpg` are required for the `release` target, which +# should only be used if you are shipping tarballs (you probably are +# not). + +# Get the version number +VERS := $(shell autorevision -s VCS_TAG -o ./passwd.cache | sed -e 's:v/::') +# Date for documentation +DOCDATE := $(shell autorevision -s VCS_DATE -o ./passwd.cache -f | sed -e 's:T.*::') + +# Find a md5 program +MD5 := $(shell if command -v "md5" > /dev/null 2>&1; then echo "md5 -q"; elif command -v "md5sum" > /dev/null 2>&1; then echo "md5sum"; fi) + +.SUFFIXES: .md .html + +.md.html: + markdown $< > $@ + + +# `prefix`, `mandir` & `DESTDIR` can and should be set on the command line to control installation locations +prefix ?= /usr/local +mandir ?= /share/man +target = $(DESTDIR)$(prefix) + + +DOCS = \ + NEWS \ + NEWS.passwd-configs \ + update-passwd.asciidoc \ + README.md \ + README.passwd-configs.md \ + README.removing-users.md + +SOURCES = \ + $(DOCS) \ + update-passwd.tool \ + Makefile \ + group-fink.conf.txt \ + passwd-fink.conf.txt + +EXTRA_DIST = \ + passwd.conf \ + AUTHORS.txt \ + passwd.cache + +TEXT_PRODUCTS = \ + update-passwd.1 + +all : cmd man conf + +# The config files +conf: group-fink.conf passwd-fink.conf + +# The script +cmd: update-passwd + +# Set up the config files +group-fink.conf: group-fink.conf.txt + sed -e 's:&&PRFIX&&:$(prefix):' $< > $@ + +passwd-fink.conf: passwd-fink.conf.txt + sed -e 's:&&PRFIX&&:$(prefix):' $< > $@ + +# Insert the version number +update-passwd: update-passwd.tool + sed -e 's:&&UPVERSION&&:$(VERS):g' -e 's:&&PRFIX&&:$(prefix):' update-passwd.tool > update-passwd + chmod +x update-passwd + +# The Man Page +man: update-passwd.1.gz + +update-passwd.1.gz: update-passwd.1 + gzip --no-name < update-passwd.1 > update-passwd.1.gz + +update-passwd.1: update-passwd.asciidoc + a2x --attribute="revdate=$(DOCDATE)" --attribute="revnumber=$(VERS)" -f manpage update-passwd.asciidoc + +# HTML representation of the man page +update-passwd.html: update-passwd.asciidoc + asciidoc --attribute="revdate=$(DOCDATE)" --attribute="footer-style=revdate" --attribute="revnumber=$(VERS)" --doctype=manpage --backend=xhtml11 update-passwd.asciidoc + +# Authors +auth: AUTHORS.txt + +AUTHORS.txt: .mailmap passwd.cache + git log --format='%aN <%aE>' | sort -f | uniq -c | sort -rn | sed 's:^ *[0-9]* *::' > AUTHORS.txt + +passwd.sed: passwd.cache + autorevision -f -t sed -o $< > $@ + +# The tarball signed and sealed +dist: tarball passwd-$(VERS).tgz.md5 passwd-$(VERS).tgz.sha256 passwd-$(VERS).tgz.sig + +# The tarball +tarball: passwd-$(VERS).tgz + +# Make an md5 checksum +passwd-$(VERS).tgz.md5: tarball + $(MD5) passwd-$(VERS).tgz > passwd-$(VERS).tgz.md5 + +# Make an sha256 checksum +passwd-$(VERS).tgz.sha256: tarball + shasum -a 256 passwd-$(VERS).tgz > passwd-$(VERS).tgz.sha256 + cat passwd-$(VERS).tgz.sha256 + +# Make a detached gpg sig +passwd-$(VERS).tgz.sig: tarball + gpg --armour --detach-sign --output "passwd-$(VERS).tgz.sig" "passwd-$(VERS).tgz" + +# The actual tarball +passwd-$(VERS).tgz: $(SOURCES) all auth + mkdir passwd-$(VERS) + cp -pR $(SOURCES) $(EXTRA_DIST) $(TEXT_PRODUCTS) passwd-$(VERS)/ + @COPYFILE_DISABLE=1 GZIP=-n9 tar -czf passwd-$(VERS).tgz --exclude=".DS_Store" passwd-$(VERS) + rm -fr passwd-$(VERS) + +install: all + install -d "$(target)/sbin" + install -m 755 update-passwd "$(target)/sbin/update-passwd" + install -d "$(target)$(mandir)/man1" + install -m 644 update-passwd.1.gz "$(target)$(mandir)/man1/update-passwd.1.gz" + install -d "$(target)/etc" + install -m 644 group-fink.conf "$(target)/etc/group-fink.conf" + install -m 644 passwd-fink.conf "$(target)/etc/passwd-fink.conf" + install -m 644 passwd.conf "$(target)/etc/passwd.conf" + +uninstall: + rm -f "$(target)/sbin/update-passwd" "$(target)$(mandir)/man1/update-passwd.1.gz" "$(target)/etc/group-fink.conf" "$(target)/etc/passwd-fink.conf" "$(target)/etc/passwd.conf" + +clean: + rm -f update-passwd update-passwd.html update-passwd.1 update-passwd.1.gz + rm -f update-passwd.sed logo.svg passwd-fink.conf group-fink.conf + rm -f *.tgz *.md5 *.sig *.sha256 + rm -f docbook-xsl.css + rm -f README.removing-users.html README.passwd-configs.html README.html + rm -f *~ index.html + +# Not safe to run in a tarball +devclean: clean + rm -f passwd.cache + rm -f AUTHORS AUTHORS.txt + rm -f *.orig ./*/*.orig + +# HTML versions of doc files suitable for use on a website +docs: \ + update-passwd.html \ + README.html \ + README.passwd-configs.html \ + README.removing-users.html + +# Tag with `git tag -s ` before running this. +release: docs dist + git tag -v "$(VERS)" +# shipper version=$(VERS) | sh -e -x diff --git a/NEWS b/NEWS index 82055b5..374d2e6 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ For a more comprehensive changelog of the latest experimental code, see: https://github.com/fink/passwd/commits/ +Next + * Refactor the script from the ground up. + * Use sysadminctl on newer systems to create users. + * Use dseditgroup on newer systems to create groups. + * Reuse existing users more often (including ones that differ only by + starting with _). + * Eliminate blocking for input. + * Add a Make file to control build, install and make tarballs. + * Add a Man page + 20160421 * Add entries for quagga and redis to avoid forcing people to set their UIDs/GIDs manually. diff --git a/README.in b/README.in deleted file mode 100644 index 0daf6dc..0000000 --- a/README.in +++ /dev/null @@ -1,52 +0,0 @@ -Passwd-core package for the Fink Project -http://www.finkproject.org/ - -The package passwd-core and its associated splitoffs and related .info files -are in the Public Domain. - -This package provides a unified method by which several administrative user -and group entries can be added to your user database by the various "passwd-*" -packages. These are needed to protect the data of several daemons (e.g. news -server, database server, etc.). - -USAGE: -This package installs the "update-passwd" script, which takes arguments in the -following format: - -@PREFIX@/sbin/update-passwd [ ] - -In addition, during the install process the package will check whether fink is -using an automatic ID range (the default as of fink-0.33.0) and set itself to -match, or will query the user whether automatic or manual allocation is -desired. This setting is saved in @PREFIX@/etc/passwd.conf. - -UID and GID entries for users can be assigned in several ways: - -1) If there is already a matching user and/or group on the system, it/they -will be regarded as satisfying the requirement of the package that needs them. -Examples are: users/groups installed by previous versions of the passwd-* -packages, or deployed via a central directory service. No action is needed. - -2) If there is no matching user, then the UID and GID will be either -generated dynamically or via the administrator's design, depending on whether -the AutoUid entry in @PREFIX@/etc/passwd.conf is "true" or "false". - -In the latter case, the files @PREFIX@/etc/passwd-fink and @PREFIX@/etc/group-fink -from the 'passwd-configs' package will first be queried for UID and GID values. -The administrator may edit these files to set up desired UID and GID values for -the system. If the user is not present in those files (e.g. a new user package -was added to Fink and passwd-configs hasn't been updated yet), then the -administrator will be prompted to enter values manually. - -If the user entry is not created, either because the administrator elected not to -proceed with the install, or if the dynamic allocation range is full, -the passwd-* package will still be installed. In such a case, we advise the -administrator to resolve the situation to make sure the user/group exists via one -of these methods: -* Change the dynamic allocation range and run 'fink reinstall passwd-' if - dynamic UID/GID allocation is in use. -* Edit @PREFIX@/etc/passwd-fink and @PREFIX@/etc/group-fink to include the desired UID/GID - values and run 'fink reinstall passwd-' if dynamic allocation isn't being - used. -* Get the user/group from a central directory server. -* Create the entries manually. diff --git a/README b/README.md similarity index 84% rename from README rename to README.md index 3abb40d..b219ef1 100644 --- a/README +++ b/README.md @@ -13,12 +13,12 @@ USAGE: This package installs the "update-passwd" script, which takes arguments in the following format: -/sw/sbin/update-passwd [ ] +/usr/local/sbin/update-passwd [ ] In addition, during the install process the package will check whether fink is using an automatic ID range (the default as of fink-0.33.0) and set itself to match, or will query the user whether automatic or manual allocation is -desired. This setting is saved in /sw/etc/passwd.conf. +desired. This setting is saved in /usr/local/etc/passwd.conf. UID and GID entries for users can be assigned in several ways: @@ -29,9 +29,9 @@ packages, or deployed via a central directory service. No action is needed. 2) If there is no matching user, then the UID and GID will be either generated dynamically or via the administrator's design, depending on whether -the AutoUid entry in /sw/etc/passwd.conf is "true" or "false". +the AutoUid entry in /usr/local/etc/passwd.conf is "true" or "false". -In the latter case, the files /sw/etc/passwd-fink and /sw/etc/group-fink +In the latter case, the files /usr/local/etc/passwd-fink and /usr/local/etc/group-fink from the 'passwd-configs' package will first be queried for UID and GID values. The administrator may edit these files to set up desired UID and GID values for the system. If the user is not present in those files (e.g. a new user package @@ -45,7 +45,7 @@ administrator to resolve the situation to make sure the user/group exists via on of these methods: * Change the dynamic allocation range and run 'fink reinstall passwd-' if dynamic UID/GID allocation is in use. -* Edit /sw/etc/passwd-fink and /sw/etc/group-fink to include the desired UID/GID +* Edit /usr/local/etc/passwd-fink and /usr/local/etc/group-fink to include the desired UID/GID values and run 'fink reinstall passwd-' if dynamic allocation isn't being used. * Get the user/group from a central directory server. diff --git a/README.passwd-configs b/README.passwd-configs.md similarity index 78% rename from README.passwd-configs rename to README.passwd-configs.md index 893ca1a..c199c74 100644 --- a/README.passwd-configs +++ b/README.passwd-configs.md @@ -4,11 +4,15 @@ as follows. passwd-fink: +``` :*::::0:0::: +``` group-fink: +``` :*:: +``` - denotes a field which can be set by the administrator, and everything +`` denotes a field which can be set by the administrator, and everything else is mandatory. diff --git a/README.removing-users b/README.removing-users.md similarity index 96% rename from README.removing-users rename to README.removing-users.md index 2c65574..6576be6 100644 --- a/README.removing-users +++ b/README.removing-users.md @@ -5,18 +5,23 @@ pacakge, since these might have been deployed on the system by some other method If you want to remove them, for example to change the legacy UID/GID from older passwd-* packages, the following commands should suffice: +``` sudo dscl . -delete /Users/ sudo dscl . -delete /Groups/ +``` For example: +``` sudo dscl . -delete /Users/news sudo dscl . -delete /Groups/news +``` to remove the "news" user installed by "passwd-news", or if installed manually. Then, to get a new UID/GID using dynamic allocation in a safe range, you could just do +``` fink reinstall passwd-news +``` -. diff --git a/group-fink b/group-fink.conf.txt similarity index 100% rename from group-fink rename to group-fink.conf.txt diff --git a/passwd-fink.in b/passwd-fink.conf.txt similarity index 67% rename from passwd-fink.in rename to passwd-fink.conf.txt index 8c72011..9da63ad 100644 --- a/passwd-fink.in +++ b/passwd-fink.conf.txt @@ -25,16 +25,16 @@ news:*:601:601::0:0:News Server:/dev/null:/dev/null postgres:*:602:602::0:0:PostgreSQL Database Server:/var/empty:/dev/null games:*:603:603::0:0:Game Files Owner:/dev/null:/dev/null canna:*:604:604::0:0:Canna Japanese Input Server:/dev/null:/dev/null -tomcat:*:607:607::0:0:Tomcat Servlet Engine:@PREFIX@/var/empty:/usr/bin/false -opennms:*:609:609::0:0:OpenNMS Network Management:@PREFIX@/var/opennms:/dev/null -distcc:*:612:612::0:0:distcc daemon,,,:@PREFIX@/var/spool/distcc:/dev/null -messagebus:*:613:613::0:0:messagebus (dbus) daemon,,,:@PREFIX@/var/run/dbus:/dev/null +tomcat:*:607:607::0:0:Tomcat Servlet Engine:&&PRFIX&&/var/empty:/usr/bin/false +opennms:*:609:609::0:0:OpenNMS Network Management:&&PRFIX&&/var/opennms:/dev/null +distcc:*:612:612::0:0:distcc daemon,,,:&&PRFIX&&/var/spool/distcc:/dev/null +messagebus:*:613:613::0:0:messagebus (dbus) daemon,,,:&&PRFIX&&/var/run/dbus:/dev/null icecast:*:614:614::0:0:Icecast Server:/var/empty:/usr/bin/false gdm:*:615:615::0:0:gdm Login GUI priv-sep:/var/empty:/usr/bin/false -ossec:*:617:617::0:0:OSSec HIDS Monitor Daemon:@PREFIX@/var/ossec:/usr/bin/false -ossecm:*:618:617::0:0:OSSec HIDS Mail Daemon:@PREFIX@/var/ossec:/usr/bin/false -ossece:*:619:617::0:0:OSSec HIDS Daemon:@PREFIX@/var/ossec:/usr/bin/false -ossecr:*:620:617::0:0:OSSec HIDS Remote Daemon:@PREFIX@/var/ossec:/usr/bin/false +ossec:*:617:617::0:0:OSSec HIDS Monitor Daemon:&&PRFIX&&/var/ossec:/usr/bin/false +ossecm:*:618:617::0:0:OSSec HIDS Mail Daemon:&&PRFIX&&/var/ossec:/usr/bin/false +ossece:*:619:617::0:0:OSSec HIDS Daemon:&&PRFIX&&/var/ossec:/usr/bin/false +ossecr:*:620:617::0:0:OSSec HIDS Remote Daemon:&&PRFIX&&/var/ossec:/usr/bin/false rt:*:621:621::0:0:Request Tracker:/dev/null:/dev/null haldaemon:*:623:623::0:0:Hardware Abstraction Layer Daemon:/dev/null:/dev/null avahi:*:624:624::0:0:Service Discovery Daemon:/dev/null:/dev/null @@ -42,6 +42,6 @@ nagios:*:625:625::0:0:Nagios and Icinga Monitoring Daemon:/dev/null:/dev/null amqp:*:626:626::0:0:AMQP Messaging Daemon:/dev/null:/dev/null # On 10.5: dovecot:*:622:622::0:0:Dovecot IMAP Server Daemon:/dev/null:/dev/null -quagga:*:627:627::0:0:Quagga Daemon:@PREFIX@/var/quagga:/usr/bin/false -redis:*:628:628::0:0:Redis Key-Value Store Server:@PREFIX@/var/db/redis:/dev/null -man:*:629:629::0:0:man:@PREFIX@/var/cache/man:/sbin/nologin +quagga:*:627:627::0:0:Quagga Daemon:&&PRFIX&&/var/quagga:/usr/bin/false +redis:*:628:628::0:0:Redis Key-Value Store Server:&&PRFIX&&/var/db/redis:/dev/null +man:*:629:629::0:0:man:&&PRFIX&&/var/cache/man:/sbin/nologin diff --git a/passwd.conf.in b/passwd.conf similarity index 82% rename from passwd.conf.in rename to passwd.conf index 6b124fc..a11cc28 100644 --- a/passwd.conf.in +++ b/passwd.conf @@ -6,3 +6,9 @@ # Set AutoUidMin and AutoUidMax to change the range from which UIDs and GIDs will # be allocated. # + + +AutoUid: true + +AutoUidMin: 600 +AutoUidMax: 699 diff --git a/update-passwd.asciidoc b/update-passwd.asciidoc new file mode 100644 index 0000000..fc8ff10 --- /dev/null +++ b/update-passwd.asciidoc @@ -0,0 +1,56 @@ += UPDATE-PASSWD(1) = + +== NAME == +update-passwd - add user and group entries for daemons + +== SYNOPSIS == +*update-passwd* *-n* *-g* [*-h* ] [*-s* ] *-i* *-m* + +*update-passwd* *-g* *-m* + +*update-passwd* *-V* + +== DESCRIPTION == +Adds user and group entries for daemons. + +This program is meant to be used by the fink package manager to setup necessary +users and groups for other packages. It is meant to be called by independent +packages for each user/group. + +By default it reuses any existing users/groups on the system that match the +calling requirements. This includes adding aliases for close matches such as +`` -> `_`. If a matching user/group is not found it +will create one with dynamically allocated ids as specified in the `passwd.conf` +file. If this functionality is turned off it will fall back to the lists +maintained in the `group-fink.conf` and `passwd-fink.conf` files. + +== OPTIONS == + +*-n* '':: +Sets the user short name. It is required to make a user. + +*-g* '':: +Sets the name of the (primary) group. (Always Required) + +*-h* '':: +Sets the home directory path (optional). If not set `/var/empty` will be used. + +*-s* '':: +Sets the path to login shell (optional). If not set `/usr/bin/false` will be used. + +*-i* '':: +Sets the long discription of the user and/or group. (Required for Users) + +*-m* '':: +A comma separated list of group membersips by name. (Always Required) + +*-V*:: +Emits the update-passwd version and exits. + +== Notes == +Development of autorevision is carried out at +https://github.com/fink/passwd + +== AUTHORS == + +See AUTHORS.txt for a full list in order of number of contributions. diff --git a/update-passwd.in b/update-passwd.in deleted file mode 100755 index 04bfad0..0000000 --- a/update-passwd.in +++ /dev/null @@ -1,240 +0,0 @@ -#!/bin/sh -# -# Merge Fink's passwd and group additions into DirectoryServices -# - -### Verify that update-passwd was called correctly - -if [ `/usr/bin/id -u` -ne 0 ]; then - echo "You must be root to run update-passwd." - exit 1 -fi - -if [ $# -eq 6 ] - then GROUPONLY=0 - name="$1" - info="$2" - home="$3" - shell="$4" - groupname="$5" - groupmembership="$6" -elif [ $# -eq 2 ] - then GROUPONLY=1 - groupname="$1" - groupmembership="$2" -else - echo "update-passwd needs 2 or 6 arguments:" - echo - echo "update-passwd [username info home shell] groupname groupmembership" - exit 1 -fi - -PREFIX=@PREFIX@ - -### check for existing user - -ADDUSER=0 -if [ $GROUPONLY -eq 0 ] ; then - idcheck=`/usr/bin/id $name 2>/dev/null` - if [ "x$idcheck" != "x" ]; then - printf "'%s' user exists, with parameters:\n" $name - echo "$idcheck" - printf "Fink will use these settings.\n\n" - else - ADDUSER=1 - fi -fi - -# check for existing group -idcheck=`dscl . -read /Groups/$groupname 2>/dev/null` -if [ "x$idcheck" != "x" ]; then - printf "'%s' group exists, with parameters:\n\n" $groupname - echo "$idcheck" - printf "\nFink will use these settings.\n\n" - ADDGROUP=0 - # get the gid value in case we're adding additional users - # to an existing group. - gid=`printf "$idcheck\n" | grep PrimaryGroupID | cut -d: -f2` -else - ADDGROUP=1 -fi - -# We can exit with success if both the user and group are available - -if [ $ADDUSER -eq 0 -a $ADDGROUP -eq 0 ] ; then - printf "Using existing entry for '%s' user/group.\n\n" $name - exit 0 -fi - -# otherwise, press on - -### dynamic UID/GID allocation - -# Test passwd.conf -if [[ "`grep AutoUid: $PREFIX/etc/passwd.conf`" =~ true ]] ; then - #Auto ID - uidmin=`grep AutoUidMin: $PREFIX/etc/passwd.conf | cut -d: -f2` - uidmax=`grep AutoUidMax: $PREFIX/etc/passwd.conf | cut -d: -f2` - #uid - printf "AutoUID is configured. Locating an unused ID between $uidmin and $uidmax.\n\n" - if [ $ADDUSER -ne 0 ] ; then - uid=0 - for (( test_id=$uidmin; test_id<=$uidmax; test_id++ )) ; do - check_id=`/usr/bin/id $test_id 2>/dev/null` - if [ "x$check_id" = "x" ] ; then - uid=$test_id - printf "Using $uid for UID\n" - break - fi - done - if [ "$uid" = "0" ] ; then - # bail if we can't allocate a UID - printf "I couldn't find an unused UID in the range %d - %d.\n" $uidmin $uidmax - printf "You can expand this range by changing the AutoUidMin and/or\n" - printf "AutoUidMax values in $PREFIX/etc/passwd.conf .\n\n" - printf "##########################################\n\n" - printf "Make sure to run 'fink reinstall passwd-%s'\n" $1 - printf "to install the %s entry, or you can create\n" $1 - printf "it manually if you would prefer to do that.\n\n" - printf "##########################################\n\n" - exit 0 - fi - fi - - #gid - if [ $ADDGROUP -ne 0 ] ; then - gid=0 - for (( test_id=$uidmin; test_id<=$uidmax; test_id++ )) ; do - check_id=`/usr/bin/id $test_id 2>/dev/null` - if [ "x$check_id" = "x" ] ; then - gid=$test_id - printf "Using $gid for GID\n" - break - fi - done - if [ "$gid" = "0" ] ; then - # bail if we can't allocate a GID - printf "I couldn't find an unused GID in the range %d - %d.\n" $uidmin $uidmax - printf "You can expand this range by changing the AutoUidMin and/or\n" - printf "AutoUidMax values in $PREFIX/etc/passwd.conf .\n\n" - printf "##########################################\n\n" - printf "Make sure to run 'fink reinstall passwd-%s'\n" $1 - printf "to install the %s entry, or you can create\n" $1 - printf "it manually if you would prefer to do that.\n\n" - printf "##########################################\n\n" - exit 0 - fi - fi -else - #manual entries - # UID - if [ $ADDUSER -ne 0 ] ; then - unused=0 - printf "Looking for $name entry in $PREFIX/etc/passwd-fink..." - stuff=`grep "^$name:" $PREFIX/etc/passwd-fink 2>/dev/null` - if [ ! -z "$stuff" ] ; then - test_id=`echo $stuff | cut -d: -f3` - printf "found.\n" - else - printf "No entry for $name in $PREFIX/etc/passwd-fink\n" - printf "Enter a value manually.\n" - # read value, check if in use - read -p "Enter a value for the UID: " test_id - fi - until [ $unused -ne 0 ]; do - check_id=`/usr/bin/id $test_id 2>/dev/null | cut -d\ -f1` - if [ "x$check_id" != "x" ] - then printf "UID %s is use.\n" $test_id - read -p "Enter a value for the UID: " test_id - elif [[ ! $test_id =~ ^[0-9]+$ ]] - then printf "UID must be a positive integer.\n" - read -p "Enter a value for the UID: " test_id - else - unused=1 - printf "%d is available. Using that for the UID.\n" $test_id - uid=$test_id - fi - done - fi - # GID - # read value, check if in use - if [ $ADDGROUP -ne 0 ] ; then - unused=0 - printf "Looking for $groupname entry in $PREFIX/etc/group-fink..." - stuff=`grep "^$groupname:" $PREFIX/etc/group-fink 2>/dev/null` - if [ ! -z "$stuff" ] ; then - printf "found.\n" - test_id=`echo $stuff | cut -d: -f3` - else - printf "No entry for $name in $PREFIX/etc/group-fink\n" - printf "Enter a value manually.\n" - # read value, check if in use - read -p "Enter a value for the GID: " test_id - fi - until [ $unused -ne 0 ]; do - check_id=`/usr/bin/id $test_id 2>/dev/null | cut -d\ -f2` - if [ "x"$check_id != "x" ] - then printf "%d is use" $check_id - read -p "Enter a value for the GID: " test_id - elif [[ ! $test_id =~ ^[0-9]+$ ]] - then printf "GID must be a positive integer.\n" - read -p "Enter a value for the GID: " test_id - else - unused=1 - printf "%d is available. Using that for the GID.\n" $test_id - gid=$test_id - fi - done - fi -fi - -printf "\nThe following user entry will be added to your DirectoryServices database:\n" -if [ $ADDUSER -ne 0 ] ; then - printf "RecordName: $name\n" - printf "UniqueID: $uid\n" - printf "PrimaryGroupID: %s\n" $gid - printf "RealName: $info\n" - printf "NFSHomeDirectory: %s\n" $home - printf "UserShell: %s\n" $shell -fi -if [ $ADDGROUP -ne 0 ] ; then - printf "Group Name: %s\n" $groupname - printf "GroupMembership: %s\n" $groupmembership - printf "\nYou may experience problems later on if you don't add this information.\n" -fi -read -p "Add the entry now? [Y/n] " answer - -answer=`echo $answer | sed 's/^[yY].*$/y/'` - -if [ -z "$answer" -o "x$answer" = "xy" ]; then - if [ $ADDUSER -ne 0 ] ; then - printf "\nAdding user info...\n" - dscl . create /users/$name - dscl . create /users/$name name $name - dscl . create /users/$name passwd '*' - dscl . create /users/$name hint "" - dscl . create /users/$name uid $uid - dscl . create /users/$name gid $gid - dscl . create /users/$name home "$home" - dscl . create /users/$name shell "$shell" - dscl . create /users/$name realname "$info" - dscl . delete /users/$name AuthenticationAuthority - fi - if [ $ADDGROUP -ne 0 ] ; then - printf "Adding group info...\n" - dscl . create /groups/$groupname - dscl . create /groups/$groupname name $groupname - dscl . create /groups/$groupname passwd '*' - dscl . create /groups/$groupname gid $gid - dscl . create /groups/$groupname GroupMembership $groupmembership - fi - printf "Done.\n\n" -else - printf "Okay, not adding the entry.\n\n" - printf "##########################################\n\n" - printf "Either run 'fink reinstall passwd-%s' to repeat\n" $1 - printf "the install process, or you can install the user/group\n" - printf "entries manually if you would prefer to do that.\n\n" - printf "##########################################\n\n" -fi -exit 0 diff --git a/update-passwd.tool b/update-passwd.tool new file mode 100755 index 0000000..7656ba2 --- /dev/null +++ b/update-passwd.tool @@ -0,0 +1,390 @@ +#!/bin/bash +# shellcheck disable=SC2236,SC2207 +# +# Merge Fink's passwd and group additions into DirectoryServices +# + + +# Config +prefixPath="&&PRFIX&&" + +DarwinVersion="$(uname -r | cut -d. -f1)" +sysadminctlVersion="17" # macOS 10.13 +if [ "${DarwinVersion}" -ge "${sysadminctlVersion}" ]; then + sysadminctlVersionRun="1" +fi + + + +# Use sysadminctl for primary user creation +function dsImport () { + local name="${1}" + local uid="${2}" + local gid="${3}" + local home="${4}" + local shell="${5}" + local info="${6}" + local output + + output="$(dsimport /dev/stdin '/Local/Default' 'I' --outputfile "/dev/stdout" --template StandardUser <<< "${name}:*:${uid}:${gid}:${info}:${home}:${shell}")" + + if ! /usr/libexec/PlistBuddy -c "Print :Succeeded:0" /dev/stdin <<< "${output}" 2> /dev/null; then + echo "Could not create user: ${name}" >&2 + exit 1 + fi + + dscl . create "/users/${name}" IsHidden 1 + dscl . delete "/users/${name}" AuthenticationAuthority + dscl . delete "/users/${name}" accountPolicyData + + defaults write /Library/Preferences/com.apple.loginwindow HiddenUsersList -array-add "${name}" + + dscacheutil -q user -a name "${name}" 2> /dev/null +} + +# Use dscl for primary user creation +function dsclUser () { + local name="${1}" + local uid="${2}" + local gid="${3}" + local home="${4}" + local shell="${5}" + local info="${6}" + + + if ! dscl . create "/users/${name}"; then + echo "Could not create user: ${name}" >&2 + exit 1 + fi + dscl . create "/users/${name}" uid "${uid}" + dscl . create "/users/${name}" name "${name}" + dscl . create "/users/${name}" passwd '*' + dscl . create "/users/${name}" hint "" + dscl . create "/users/${name}" gid "${gid}" + dscl . create "/users/${name}" home "${home}" + dscl . create "/users/${name}" shell "${shell}" + dscl . create "/users/${name}" realname "${info}" + + dscl . create "/users/${name}" IsHidden 1 + dscl . delete "/users/${name}" AuthenticationAuthority + + defaults write /Library/Preferences/com.apple.loginwindow HiddenUsersList -array-add "${name}" + + dscacheutil -q user -a name "${name}" 2> /dev/null +} + +# Use dseditgroup for primary group creation +function dseditgroupGroup () { + local groupname="${1}" + local gid="${2}" + local groupmembership="${3}" + + + if ! dseditgroup -o create -i "${gid}" "${groupname}"; then + echo "Could not create group: ${groupname}" >&2 + exit 1 + fi + dscl . create "/groups/${groupname}" passwd '*' + dscl . create "/groups/${groupname}" GroupMembership "${groupmembership}" + + dscl . create "/groups/${groupname}" IsHidden 1 + + dscacheutil -q group -a name "${groupname}" 2> /dev/null +} + +# Use dscl for primary group creation +function dsclGroup () { + local groupname="${1}" + local gid="${2}" + local groupmembership="${3}" + + + if ! dscl . create "/groups/${groupname}"; then + echo "Could not create group: ${groupname}" >&2 + exit 1 + fi + dscl . create "/groups/${groupname}" name "${groupname}" + dscl . create "/groups/${groupname}" passwd '*' + dscl . create "/groups/${groupname}" gid "${gid}" + dscl . create "/groups/${groupname}" GroupMembership "${groupmembership}" + + dscl . create "/groups/${groupname}" IsHidden 1 + + dscacheutil -q group -a name "${groupname}" 2> /dev/null +} + +# Add user alias +function userAlias () { + local sysUser="${1}" + local aliUser="${2}" + + + dscl . -merge "/users/${sysUser}" RecordName "${aliUser}" +} + +# Add group alias +function groupAlias () { + local sysGroup="${1}" + local aliGroup="${2}" + + + dscl . -merge "/groups/${sysGroup}" RecordName "${aliGroup}" +} + +# Get uid +function uidNumber () { + local name="${1}" + local _uid + + if [ ! -z "${fixedUserFink}" ]; then + _uid="$(grep "^${name}:" "${fixedUserFink}" 2>/dev/null | cut -d ':' -f '3')" + elif [ ! -z "${uidMin}" ]; then + local testUid="${uidMin}" + while [ "${testUid}" -le "${uidMax}" ]; do + if [ ! -z "$(dscacheutil -q user -a uid "${testUid}" 2> /dev/null)" ]; then + testUid="$((testUid + 1))" + else + _uid="${testUid}" + break + fi + done + fi + + # No uid found + if [ -z "${_uid}" ]; then + tee >&2 <<- EOF + I could not find an unused UID in the range ${uidMin} - ${uidMax}. + You can expand this range by changing the AutoUidMin and/or + AutoUidMax values in ${prefixPath}/etc/passwd.conf. + + ########################################## + + Make sure to run 'fink reinstall passwd-${SHORTNAME}' + to install the ${SHORTNAME} entry, or you can create + it manually if you would prefer to do that. + + ########################################## + + EOF + exit 1 + elif [ ! -z "$(dscacheutil -q user -a uid "${_uid}" 2> /dev/null)" ]; then + tee >&2 <<- EOF + UID ${_uid} is already in use + EOF + exit 1 + fi + + echo "${_uid}" +} + +# Get gid +function gidNumber () { + local groupname="${1}" + local _gid + + if [ ! -z "${fixedGroupFink}" ]; then + _gid="$(grep "^${name}:" "${fixedGroupFink}" 2>/dev/null | cut -d ':' -f '3')" + elif [ ! -z "${uidMin}" ]; then + local testGid="${uidMin}" + while [ "${testGid}" -le "${uidMax}" ]; do + if [ ! -z "$(dscacheutil -q group -a gid "${testGid}" 2> /dev/null)" ]; then + testGid="$((testGid + 1))" + else + _gid="${testGid}" + break + fi + done + fi + + # No gid found + if [ -z "${_gid}" ]; then + tee >&2 <<- EOF + I could not find an unused GID in the range ${uidMin} - ${uidMax}. + You can expand this range by changing the AutoUidMin and/or + AutoUidMax values in ${prefixPath}/etc/passwd.conf. + + ########################################## + + Make sure to run 'fink reinstall passwd-${GROUPNAME}' + to install the ${GROUPNAME} entry, or you can create + it manually if you would prefer to do that. + + ########################################## + + EOF + exit 1 + elif [ ! -z "$(dscacheutil -q group -a gid "${_gid}" 2> /dev/null)" ]; then + tee >&2 <<- EOF + GID ${_gid} is already in use + EOF + exit 1 + fi + + echo "${_gid}" +} + + +# Usage message. +function upUsage () { + tee >&2 << EOF +usage: update-passwd -n -g [-h ] [-s ] -i -m + update-passwd -g -m + update-passwd -V + + Options include: + -n short-name = User short name + -g group-name = Name of (primary) group + -h home-dir = Home directory path (optional) + -s shell = Path to login shell (optional) + -i info-string = Long discription of user + -m group-members = A list of group membersips by name + -V = emit version and exit + -? = help message +EOF + exit 1 +} + +# Config +UPVERSION="&&UPVERSION&&" +while getopts ":n:g:h:s:i:m:V" OPTION; do + case "${OPTION}" in + n) + SHORTNAME="${OPTARG}" + ;; + g) + GROUPNAME="${OPTARG}" + ;; + h) + HOME="${OPTARG}" + ;; + s) + SHELL="${OPTARG}" + ;; + i) + INFO="${OPTARG}" + ;; + m) + MEMBERS="${OPTARG}" + ;; + V) + echo "update-passwd ${UPVERSION}" + exit 0 + ;; + ?) + # If an unknown flag is used (or -?): + upUsage + ;; + esac +done + + +# Check if needed software is installed. +PATH="${PATH}:/usr/local/sbin:/usr/local/bin" +commands=( +dscl +dscacheutil +defaults +id +sed +grep +cut +tr +tee +) +if [ "${sysadminctlVersionRun}" = "1" ]; then +commands+=( +dsimport +dseditgroup +dsmemberutil +/usr/libexec/PlistBuddy +) +fi +for command in "${commands[@]}"; do + if ! type "${command}" &> /dev/null; then + echo "${command} is missing, please install" >&2 + exit 100 + fi +done + + +### Verify that update-passwd was called correctly +if [ "$(/usr/bin/id -u)" -ne "0" ]; then + echo "You must be root to run update-passwd." + exit 1 +fi + +# Set the operating mode +if [ ! -z "${SHORTNAME}" ]; then + opMode="user" + : "${HOME:="/var/empty"}" + : "${SHELL:="/usr/bin/false"}" + if [ -z "${INFO}" ] || [ -z "${GROUPNAME}" ] || [ -z "${MEMBERS}" ]; then + upUsage + fi +elif [ ! -z "${GROUPNAME}" ]; then + opMode="group" + if [ -z "${MEMBERS}" ]; then + upUsage + fi +else + upUsage +fi + + +# Decide if ids are static or dynamic +if [ "$(grep '^AutoUid:' "${prefixPath}/etc/passwd.conf" | sed -e 's:[[:blank:]]\{1,\}: :g' | cut -d ' ' -f "2")" = "true" ]; then + uidMin="$(grep '^AutoUidMin:' "${prefixPath}/etc/passwd.conf" | sed -e 's:[[:blank:]]\{1,\}: :g' | cut -d ' ' -f '2')" + uidMax="$(grep '^AutoUidMax:' "${prefixPath}/etc/passwd.conf" | sed -e 's:[[:blank:]]\{1,\}: :g' | cut -d ' ' -f '2')" +elif [ -f "${prefixPath}/etc/passwd-fink.conf" ] && [ -f "${prefixPath}/etc/group-fink.conf" ]; then + fixedUserFink="${prefixPath}/etc/passwd-fink.conf" + fixedGroupFink="${prefixPath}/etc/group-fink.conf" +fi + + + +# Setup group +echo "Checking to see if the group ${GROUPNAME} exists:" +if [ ! -z "$(dscacheutil -q group -a name "${GROUPNAME}" 2> /dev/null)" ]; then + echo "Group ${GROUPNAME} already exists." +elif [ ! -z "$(dscacheutil -q group -a name "_${GROUPNAME}" 2> /dev/null)" ]; then + echo "Group _${GROUPNAME} exists; creating alias..." + groupAlias "_${GROUPNAME}" "${GROUPNAME}" + dscl . -merge "/groups/_${GROUPNAME}" GroupMembership "${MEMBERS}" +else + echo "Group ${GROUPNAME} does not exist; creating..." + gidNumber="$(gidNumber "${GROUPNAME}")" + if [ "${sysadminctlVersionRun}" = "1" ]; then + dseditgroupGroup "${GROUPNAME}" "${gidNumber}" "${MEMBERS}" + else + dsclGroup "${GROUPNAME}" "${gidNumber}" "${MEMBERS}" + fi +fi + + + +# Setup user +if [ "${opMode}" = "user" ]; then + echo "Checking to see if the user ${SHORTNAME} exists:" +fi +if [ "${opMode}" = "user" ] && [ ! -z "$(dscacheutil -q user -a name "${SHORTNAME}" 2> /dev/null)" ]; then + echo "User ${SHORTNAME} already exists." +elif [ "${opMode}" = "user" ] && [ ! -z "$(dscacheutil -q user -a name "_${SHORTNAME}" 2> /dev/null)" ]; then + echo "User _${SHORTNAME} exists; creating alias..." + userAlias "_${SHORTNAME}" "${SHORTNAME}" +elif [ "${opMode}" = "user" ]; then + echo "User ${SHORTNAME} does not exist; creating..." + : "${gidNumber="$(dscl . -read "/groups/${GROUPNAME}" PrimaryGroupID | cut -d ' ' -f '2')"}" + if [ "${sysadminctlVersionRun}" = "1" ]; then + dsImport "${SHORTNAME}" "$(uidNumber "${SHORTNAME}")" "${gidNumber}" "${HOME}" "${SHELL}" "${INFO}" + dsmemberutil flushcache + else + dsclUser "${SHORTNAME}" "$(uidNumber "${SHORTNAME}")" "${gidNumber}" "${HOME}" "${SHELL}" "${INFO}" + fi +fi + + + + +# kill local directory service so it will see our local +# file changes -- it will automatically restart +/usr/bin/killall DirectoryService 2>/dev/null || /usr/bin/killall opendirectoryd 2>/dev/null