Quantcast
Channel: Viktor Balogh's HP-UX blog » files
Viewing all articles
Browse latest Browse all 8

Advanced scripting – changing the UID of a user

$
0
0

At one of our customer we will have an monstre’ UID-consolidation projekt in the near future. For this purpose I’ve written a script to enlighten our task. Most of you thinks at it a an over-complicated (usermod && find …. chown …) job, but my script has undergone some performance optimisation too: it starts parallel (find….chown) processes to get the job done as fast as it can be. Here is the output for changing the UID of a testuser:

# ./chguid testuser 234
 Checking parameters                                                OK
 Checking for duplicate UID                                         OK
 Changing the UID in the password database                          OK
 The UID was successfully changed:
 Before:
 testuser:x:123:20:test:/home/testuser:/usr/bin/ksh
 After:
 testuser:x:234:20:test:/home/testuser:/usr/bin/ksh
 Starting parallel processes to change ownership
 Threads with chown: (the list may be incomplete on fast systems)
 root  4993  4981  7 13:54:04 pts/0     0:00 find / -xdev -user 123 ( -type f -o -type d ) -exec chown 234 +
 root  4995     1  7 13:54:04 pts/0     0:00 find /orae6/hes1 -xdev -user 123 ( -type f -o -type d ) -exec chown 234 +
 root  4994     1  5 13:54:04 pts/0     0:00 find /orae6/heb1 -xdev -user 123 ( -type f -o -type d ) -exec chown 234 +
 FINISHED
 #

and here is the script:

#!/usr/bin/ksh
#
# Author: Viktor Balogh
# Version: v0.4
#

function usage {

        print "\n  Usage: chguid USERNAME NEW_UID\n"
        print "  Where USERNAME is the name of an existing user and UID is between 0 and 2147483647."
        print "  This UID needs to be unique, as this will be the new UID of the user.\n"
        print "  The UID of the system users cannot be changed for security reasons.\n"

}

function check_root {

        # check if we have enough permission

        if  [ $(id -u) -ne "0" ]
        then
                printf "  %s\n\n" "You should be root in order to use this tool!"
                exit 6
        fi

}

function check_param {

        # check the parameters of the script

        printf "  %-60s" "Checking parameters"

        if  [ $# -ne 2 ]
        then
                printf "%s\n" "ERROR"
                printf "  %s\n\n" "Please feed exactly two parameters!"
                usage
                exit 2

        elif $(pwget -n ${UNAME} 1>/dev/null 2>/dev/null)
        then
                # UID_MAX is 2147483647 in limits.h, so 2147483646 is the highest UID
                # we do not touch UID 0 and system users

                if [[ "${UID}" -lt "2147483647" && "${UID}" -gt "0" && "${UNAME}" != "root" &&
                        "${UNAME}" != "daemon" && "${UNAME}" != "bin" && "${UNAME}" != "sys" &&
                        "${UNAME}" != "adm" && "${UNAME}" != "uucp" && "${UNAME}" != "lp" &&
                        "${UNAME}" != "nuucp" && "${UNAME}" != "hpdb" && "${UNAME}" != "nobody" ]]
                then
                        # check if UID already exists
                        printf "%s\n" "OK"
                        printf "  %-60s" "Checking for duplicate UID"
                        UID_SEARCH=$(pwget | awk  -F: '$3 == "'${UID}'"' 2>/dev/null)

                        if [ ! -z "${UID_SEARCH}" ]
                        then
                                printf "%s\n" "ERROR"
                                printf "  %s\n\n" "The given UID already exists! Please specify another one!"
                                usage
                                exit 5
                        else
                                printf "%s\n" "OK"
                        fi

                else
                        printf "%s\n" "ERROR"
                        printf "  %s\n\n" "Invalid UID/UNAME!"
                        usage
                        exit 4
                fi

        else
                printf "%s\n" "ERROR"
                printf "  %s\n\n" "User not found!"
                exit 3
        fi

}

function check_trusted {

        ## OBSOLETE : usermod also handles trusted
        # check if system is trusted
        printf "  %-60s" "Checking if system is trusted"
        /usr/lbin/getprdef -r 1>/dev/null 2>/dev/null

        if [ $? -eq 4 ]
        then
                printf "%s\n" "NO"
        else
                printf "%s\n" "YES"
                printf "  %s\n" "Trusted mode is on, but this hasn't been implemented in this script. Contact developer!"
                exit 1
        fi

}

function check_nis {

        ## OBSOLETE : pwget/usermod also handles NIS
        # check if NIS is in use
        printf "  %-60s" "Checking if NIS is used for authentication"
        /usr/bin/ypwhich 1>/dev/null 2>/dev/null

        if [ $? -eq 1 ]
        then
                NIS=0
                printf "%s\n" "NO"
        else
                NIS=1
                printf "%s\n" "YES"
                printf "  %s\n" "NIS is in use, but this hasn't been implemented in this script. Contact developer!"
                exit 1
        fi

}

function check_shadow {

        ## OBSOLETE : pwget/usermod also handles ShadowPassword
        # check if ShadowPassword is in use
        printf "  %-60s" "Checking if passwords are shadow'ed"

        if [ -f /etc/shadow ]
        then
                SHADOW=1
                printf "%s\n" "YES"
        else
                SHADOW=0
                printf "%s\n" "NO"
        fi

}

function change_uid {

        # before any modification make a backup of the passwd/shadow file and keep the old value of the UID
        POSTFIX=$(date '+%d%m%Y_%H%M%S')
        OLDUID=$(pwget -n ${UNAME} | cut -d: -f3)

        if [ -f /etc/passwd ]
        then
                cp /etc/passwd /etc/passwd_${POSTFIX}
        fi

        if [ -f /etc/shadow ]
        then
                cp /etc/shadow /etc/shadow_${POSTFIX}
        fi

        # changing the UID with the usermod command
        printf "  %-60s" "Changing the UID in the password database"
        CHECK_BEFORE="$(pwget | awk -F: '$1 == "'${UNAME}'" && $3 == "'${OLDUID}'"')"
        usermod -u ${UID} ${UNAME} 1>/dev/null 2>/dev/null

        # The following is an extended check because usermod fails in some strange cases if the permission of the homedir is 700 or so.
        # Only the password entry should be checked, the homedir can be ignored as the massive find/chown operation begins only after this.
        # That's why we cannot rely on the exit status of usermod.

        CHECK_AFTER="$(pwget | awk -F: '$1 == "'${UNAME}'" && $3 == "'${UID}'"')"

        if [[ $? -eq "0" || ! -z "${CHECK_AFTER}" ]]
        then
                printf "%s\n" "OK"
                printf "  %-60s\n\n" "The UID was successfully changed:"
                printf "  %-60s\n" "Before:"
                printf "  %-60s\n" "${CHECK_BEFORE}"
                printf "  %-60s\n" "After:"
                printf "  %-60s\n" "${CHECK_AFTER}"
                print
        else
                printf "%s\n" "ERROR"
                printf "  %-60s\n" "The change of the UID was failed, for more info try it manually:"
                printf "  %-60s\n" "usermod -u ${UID} ${UNAME}"
                printf "  %-60s\n" "...and do not forget to chown all the data to the new UID!"
                exit 7
        fi

}

function change_ownership {

        # starting some find processes to change the ownership
        printf "  %-60s\n" "Starting parallel processes to change ownership"

        # Now we start one find process on each separate NFS mount...

        for nfs in $(mount -v | awk '/ type nfs / {print $3}')
        do
                find ${nfs} -xdev -user ${OLDUID} \( -type f -o -type d \) -exec chown ${UID} {} \+ &
        done &

        # only a single find process on the local disks...

        for localfs in $(mount -v | awk '$1 ~ /\/dev\/vg0[01]/ {print $3}')
        do
                find ${localfs} -xdev -user ${OLDUID} \( -type f -o -type d \) -exec chown ${UID} \+
        done &

        # ...and one find per FS on storage

        for storagefs in $(mount -v | awk '$1 !~ /\/dev\/vg0[01]/ && $5 !~ /nfs/ {print $3}')
        do
                find ${storagefs} -xdev -user ${OLDUID} \( -type f -o -type d \) -exec chown ${UID} \+ &
        done &

        printf "  %-60s\n\n" "Threads with chown: (the list may be incomplete on fast systems)"
        ps -ef | awk '(/chown/ || /find/) && ! /awk/'
        wait
        printf "\n  %s\n" "FINISHED"

}

print
UNAME=$1
UID=$2
check_root
check_param ${UNAME} ${UID}
change_uid
change_ownership
print

It needs some customization if you want to use it in your environment, because it assesses the type/location of the FS based on the name of the volumgroup. Feel free to comment & suggest modifications!

UPDATE 10.12.2010: Thanks to Markus, the testuser-bug was corrected, v0.4 released.


Viewing all articles
Browse latest Browse all 8

Trending Articles