# runtime : this file is used by the ldapscripts, it sould not be used independently # Copyright (C) 2005 Ganaël LAPLANCHE - Linagora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. ### Useful functions ### # Tests a string # Input : string to test ($1) # Output : true or false is_yes () { echo "$1" | grep -qi '^yes$' } # Tests a string # Input : string to test ($1) # Output : true or false is_no () { echo "$1" | grep -qi '^no$' } # Tests a string # Input : string to test ($1) # Output : true or false is_uri () { echo "$1" | grep -q '://' } # Tests a string # Input : string to test ($1) # Output : true or false is_integer () { echo "$1" | grep -qE '^[0-9]+$' } # Tests a string (a command name) and tells if it is built-in (true) or external (false) # Input : string to test ($1) # Output : true or false is_builtin () { type "$1" 2>/dev/null | grep -qi 'built' } # Logs a string to $LOGFILE # Input : string to log ($1) # Output : nothing log_only () { if [ -n "$1" ] then if [ -n "$LOGFILE" ] then if [ ! -w "$LOGFILE" ] then touch "$LOGFILE" 2>/dev/null if [ $? -ne 0 ] then echo "Unable to create $LOGFILE, exiting..." && exit 1 fi fi echo "$1" >> "$LOGFILE" fi fi } # Echoes and logs a string to $LOGFILE # Input : string to echo and log ($1) # Output : nothing echo_log () { [ -n "$1" ] && echo "$1" [ -n "$1" ] && log_only "$1" } # Echoes/logs $1, exits and returns 0 # Input : string to echo and log ($1) # Output : 0 end_ok () { [ -n "$1" ] && echo_log "$1" exit 0 } # Echoes/logs $1, exits and returns 1 # Input : string to echo and log ($1) # Output : 1 end_die () { [ -n "$1" ] && echo_log "$1" exit 1 } # Allocates and creates a temporary file $_TMPFILE under $TMPDIR # Output : nothing mktempf () { # Avoid creating two temporary files (must have been released before) [ -n "$_TMPFILE" ] && end_die "Error allocating temporary file $_TMPFILE" # Name temp file _TMPFILE="$TMPDIR/`basename $0`.`date '+%Y%m%d-%H%M%S'`.$$" # Catch CTRL-C to remove $_TMPFILE trap 'rm -f "$_TMPFILE" 2>/dev/null ; end_die "Interrupted - Removing temporary file $_TMPFILE"' 2 # Create temp file _TMPMASK=`umask` umask 0077 touch "$_TMPFILE" 2>/dev/null || end_die "Error creating temporary file $_TMPFILE" umask "$_TMPMASK" } # Releases a previously allocated temporary file # Output : nothing reltempf () { # Clean up the temporary file and restore traps rm -f "$_TMPFILE" 2>/dev/null # Reset traps trap - 2 # Clean up name _TMPFILE='' } ### LDAP functions ### # Performs a search in the LDAP directory # Input : base ($1), filter ($2), attribute to display ($3) # Output : entry/entries found (stdout) _ldapsearch () { if [ -n "$BINDPWDFILE" ] then $LDAPSEARCHBIN -y "$BINDPWDFILE" -D "$BINDDN" -b "${1:-$SUFFIX}" -xH "$SERVER" -s sub -LLL "${2:-(objectclass=*)}" "${3:-*}" 2>>"$LOGFILE" else $LDAPSEARCHBIN -w "$BINDPWD" -D "$BINDDN" -b "${1:-$SUFFIX}" -xH "$SERVER" -s sub -LLL "${2:-(objectclass=*)}" "${3:-*}" 2>>"$LOGFILE" fi } # Adds an entry to the LDAP directory # Input : LDIF - entry to add (stdin) # Output : nothing _ldapadd () { if [ -n "$BINDPWDFILE" ] then $LDAPADDBIN -y "$BINDPWDFILE" -D "$BINDDN" -xH "$SERVER" 2>>"$LOGFILE" 1>/dev/null else $LDAPADDBIN -w "$BINDPWD" -D "$BINDDN" -xH "$SERVER" 2>>"$LOGFILE" 1>/dev/null fi } # Modifies an entry in the LDAP directory # Input : LDIF - modification information (stdin) # Output : nothing _ldapmodify () { if [ -n "$BINDPWDFILE" ] then $LDAPMODIFYBIN -y "$BINDPWDFILE" -D "$BINDDN" -xH "$SERVER" 2>>"$LOGFILE" 1>/dev/null else $LDAPMODIFYBIN -w "$BINDPWD" -D "$BINDDN" -xH "$SERVER" 2>>"$LOGFILE" 1>/dev/null fi } # Renames an entry in the LDAP directory # Input : old dn ($1), new rdn ($2) # Output : nothing _ldaprename () { if [ -z "$1" ] || [ -z "$2" ] then end_die "_ldaprename : missing argument(s)" else if [ -n "$BINDPWDFILE" ] then $LDAPMODRDNBIN -y "$BINDPWDFILE" -D "$BINDDN" -xH "$SERVER" -r "$1" "$2" 2>>"$LOGFILE" 1>/dev/null else $LDAPMODRDNBIN -w "$BINDPWD" -D "$BINDDN" -xH "$SERVER" -r "$1" "$2" 2>>"$LOGFILE" 1>/dev/null fi fi } # Deletes an entry in the LDAP directory # Input : dn to delete ($1) # Output : nothing _ldapdelete () { [ -z "$1" ] && end_die "_ldapdelete : missing argument" if [ -n "$BINDPWDFILE" ] then $LDAPDELETEBIN -y "$BINDPWDFILE" -D "$BINDDN" -xH "$SERVER" -r "$1" 2>>"$LOGFILE" 1>/dev/null else $LDAPDELETEBIN -w "$BINDPWD" -D "$BINDDN" -xH "$SERVER" -r "$1" 2>>"$LOGFILE" 1>/dev/null fi } # Extracts LDIF information from $0 (the current script itself) # selecting lines beginning with $1 occurrences of '#' # Input : depth ($1) # Output : extracted LDIF data (stdout) _extractldif () { if [ -n "$1" ] && is_integer "$1" then _EXTRACTDEPTH="$1" else echo_log "Warning : invalid depth supplied to _extractldif(), using default (2)..." _EXTRACTDEPTH='2' fi grep -E "^#{$_EXTRACTDEPTH}[^#]*$" "$0" | sed -e 's|^#*||' 2>>"$LOGFILE" } # Filters LDIF information # Input : Data to filter (stdin) # Output : Filtered data (stdout) _filterldif () { # Allocate and create temp file mktempf # Generate filter file echo "s||$_GROUP|g" > "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_USER|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_UID|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_GID|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$SUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s|<_suffix>|$_SUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$USUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s|<_usuffix>|$_USUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$MSUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s|<_msuffix>|$_MSUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$GSUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s|<_gsuffix>|$_GSUFFIX|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_HOMEDIR|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$USHELL|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_PASSWORD|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_GECOS|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" echo "s||$_ENTRY|g" >> "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" # Use it sed -f "$_TMPFILE" 2>>"$LOGFILE" # Release temp file reltempf } ### Nsswitch functions # Converts to gid any group passed in as name/gid # Input : the name or gid to convert ($1) # Output : the result of the conversion ($_GID) _grouptogid () { [ -z "$1" ] && end_die "_grouptogid : missing argument" _GID=`$GETENTGRCMD "$1" 2>/dev/null | head -n 1 | cut -d ":" -f 3` if [ -z "$_GID" ] then _GID=`echo "$1" | grep '^[0-9]\+$'` # Check if group is a gid [ -z "$_GID" ] && end_die "Cannot resolve group $1 to gid : groupname not found" echo_log "Warning : gid $2 not resolved, using it anyway..." fi } # Converts to name any group passed in as name/gid # Input : the name or gid to convert ($1) # Output : the result of the conversion ($_GID) _gidtogroup () { [ -z "$1" ] && end_die "_gidtogroup : missing argument" _GID=`$GETENTGRCMD "$1" 2>/dev/null | head -n 1 | cut -d ":" -f 1` if [ -z "$_GID" ] then _GID="$1" echo_log "Warning : group $1 not resolved, using it anyway..." fi } # Converts to uid any user passed in as name/uid # Input : the name or uid to convert ($1) # Output : the result of the conversion ($_UID) _usertouid () { [ -z "$1" ] && end_die "_usertouid : missing argument" _UID=`$GETENTPWCMD "$1" 2>/dev/null | head -n 1 | cut -d ":" -f 3` if [ -z "$_UID" ] then _UID=`echo "$1" | grep '^[0-9]\+$'` # Check if user is a UID [ -z "$_UID" ] && end_die "Cannot resolve user $1 to uid : username not found" echo_log "Warning : uid $1 not resolved, using it anyway..." fi } # Converts to name any user passed in as name/uid # Input : the name or uid to convert ($1) # Output : the result of the conversion ($_UID) _uidtouser () { [ -z "$1" ] && end_die "_uidtouser : missing argument" _UID=`$GETENTPWCMD "$1" 2>/dev/null | head -n 1 | cut -d ":" -f 1` if [ -z "$_UID" ] then _UID="$1" echo_log "Warning : user $1 not resolved, using it anyway..." fi } ### LDAP advanced functions # Finds the last group id used in LDAP # Input : nothing # Output : the last gid used + 1 (so the first useable gid) ($_GID) _findlastgroup () { _GID=`_ldapsearch "$GSUFFIX,$SUFFIX" '(objectClass=posixGroup)' gidNumber | grep "gidNumber: " | sed -e "s|gidNumber: ||" | uniq | sort -n | tail -n 1` if [ -z "$_GID" ] || [ ! "$_GID" -gt "$GIDSTART" ] then _GID="$GIDSTART" fi _GID=`expr "$_GID" + 1` } # Finds the last machine id used in LDAP # Input : nothing # Output : the last machine id used + 1 (so the first useable machine id) ($_UID) _findlastmachine () { _UID=`_ldapsearch "$SUFFIX" '(objectClass=posixAccount)' uidNumber | grep "uidNumber: " | sed -e "s|uidNumber: ||" | uniq | sort -n | tail -n 1` if [ -z "$_UID" ] || [ ! "$_UID" -gt "$MIDSTART" ] then _UID="$MIDSTART" fi _UID=`expr "$_UID" + 1` } # Finds the last user id used in LDAP # Input : nothing # Output : the last user id used + 1 (so the first useable user id) ($_UID) _findlastuser () { _UID=`_ldapsearch "$SUFFIX" '(objectClass=posixAccount)' uidNumber | grep "uidNumber: " | sed -e "s|uidNumber: ||" | uniq | sort -n | tail -n 1` if [ -z "$_UID" ] || [ ! "$_UID" -gt "$UIDSTART" ] then _UID="$UIDSTART" fi _UID=`expr "$_UID" + 1` } # Finds a particular entry in the LDAP directory # Input : base ($1), filter ($2) # Output : the dn of the first matching entry found ($_ENTRY) _findentry () { _ENTRY=`_ldapsearch "$1" "$2" dn | grep "dn: " | head -n 1 | sed -e "s|dn: ||"` } ### Other functions ### # Generates a password using the $PASSWORDGEN variable # Input : the username related to the generation ($1) # Output : the generated password ($_PASSWORD) _genpassword () { PASSWORDGEN=`echo "$PASSWORDGEN" | sed -e "s|%u|$1|g"` _PASSWORD=`eval $PASSWORDGEN` } # Changes a password for a particular DN # Input : new clear-text password ($1), user DN ($2) # Output : nothing _changepassword () { if [ -z "$1" ] || [ -z "$2" ] then end_die "_changepassword : missing argument(s)" else if is_yes "$RECORDPASSWORDS" then echo "$2 : $1" >> "$PASSWORDFILE" fi if [ -n "$BINDPWDFILE" ] then ## Change password in a secure way # Allocate and create temp file mktempf # Generate password file echo -n "$1" > "$_TMPFILE" || end_die "Error writing to temporary file $_TMPFILE" # Change password $LDAPPASSWDBIN -y "$BINDPWDFILE" -D "$BINDDN" -xH "$SERVER" -T "$_TMPFILE" "$2" 2>>"$LOGFILE" 1>/dev/null # Release temp file reltempf else ## Change password in the unsecure, old-fashioned way $LDAPPASSWDBIN -w "$BINDPWD" -D "$BINDDN" -xH "$SERVER" -s "$1" "$2" 2>>"$LOGFILE" 1>/dev/null fi fi } ### Source configuration file _CONFIGFILE="/usr/local/etc/ldapscripts/ldapscripts.conf" . "$_CONFIGFILE" || end_die "Unable to source configuration file ($_CONFIGFILE), exiting..." ### Checks and defaults ### # Check if ldap client tools are correctly configured if [ ! -x "$LDAPADDBIN" ] || [ ! -x "$LDAPDELETEBIN" ] || [ ! -x "$LDAPSEARCHBIN" ] || [ ! -x "$LDAPMODIFYBIN" ] || [ ! -x "$LDAPPASSWDBIN" ] || [ ! -x "$LDAPMODRDNBIN" ] then end_die "You must have OpenLDAP client commands installed before running these scripts" fi # Check for bindpwd file if [ ! -f "$BINDPWDFILE" ] || [ ! -r "$BINDPWDFILE" ] then if [ -n "$BINDPWD" ] then echo_log "Warning : using command-line passwords, ldapscripts may not be safe" else end_die "Unable to read password file $BINDPWDFILE, exiting..." fi fi # Does the shell has built-in echo command ? # If not, print a warning message if is_builtin "echo" && is_builtin "[" then : else echo_log "Warning : 'echo' or '[' (test) is not built-in, ldapscripts may not be safe" fi # Check if a full URI has been given if is_uri "$SERVER" then : else SERVER="ldap://$SERVER" fi # Check homes, shell and logfile UHOMES=${UHOMES:-"/dev/null"} USHELL=${USHELL:-"/bin/false"} LOGFILE=${LOGFILE:-"/var/log/ldapscripts.log"} TMPDIR=${TMPDIR:-"/tmp"} # Check password file if password recording set if is_yes "$RECORDPASSWORDS" then PASSWORDFILE=${PASSWORDFILE:-"/var/log/ldapscripts_passwd.log"} if [ ! -w "$PASSWORDFILE" ] then touch "$PASSWORDFILE" 2>/dev/null || end_die "Unable to create password log file $PASSWORDFILE, exiting..." fi fi # Guess what kind of getent command to use if [ -z "$GETENTPWCMD" ] || [ -z "$GETENTGRCMD" ] then case "`uname`" in Linux*) GETENTPWCMD="getent passwd" GETENTGRCMD="getent group" ;; FreeBSD*) GETENTPWCMD="pw usershow" GETENTGRCMD="pw groupshow" ;; *) GETENTPWCMD="getent passwd" GETENTGRCMD="getent group" ;; esac fi # Record command call into logfile _NOW=`date "+%D - %R"` log_only ">> $_NOW : Command : $0 $*"