#!/usr/bin/perl -w # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r714 src/43haes/usr/sbin/cluster/utilities/cl_manageallowpasswd.sh 1.15 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 2004,2009 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG # @(#)83 1.3 src/43haes/usr/sbin/cluster/utilities/cl_manageallowpasswd.sh, hacmp.utils, 51haes_r520 1/14/04 20:02:41 # @(#)83 1.15 src/43haes/usr/sbin/cluster/utilities/cl_manageallowpasswd.sh, hacmp.utils, 61haes_r714 1/6/09 06:12:00 use strict; use Getopt::Std; # vi set ts=8 ############################################################################## # # # cl_manageallowpasswd - utility script for handling adding / removing users # from the allow_passwd file. # ############################################################################## sub ALLOW_USERS_FILE() { "/usr/es/sbin/cluster/etc/clpasswd/cl_passwd_users" }; sub SYSTEM_USERS_FILE() { "/usr/es/sbin/cluster/etc/clpasswd/sysusers" }; sub GET_LOCAL_NODENAME(){ "/usr/es/sbin/cluster/utilities/get_local_nodename" }; sub ALL_USERS_TOKEN() { "ALL_USERS" }; sub ODMDIR() { "/etc/es/objrepos" }; my %global; ############################################################################## # # Error codes returned from functions # sub ERR_UNABLE_TO_OPEN_ETC_PASSWD() { 1 }; sub ERR_UNABLE_TO_OPEN_ALLOWED_USERS() { 2 }; sub ERR_DEPRECATED { 3 }; sub ERR_INVALID_USER_SPECIFIED() { 4 }; sub ERR_INVALID_ARGUMENTS() { 5 }; sub ERR_NO_NODES_AVAILABLE() { 6 }; sub ERR_AND_EXIT() { 7 }; sub SUCCESS() { 0 }; sub SUCCESS_AND_EXIT() { 999 }; # ############################################################################## ## # # Function: # add_users # # Description: # Add the list of users defined as the argument to this # function, only the list of users passed in will be # allowed users. # As each user is added, the function will print out the # name of user to the stdout. # # Arguments: # $: users_in - list of users allowed to change password (complete list) # $: sync_flag - synchronize these changes to other nodes in the cluster # # Return: # SUCCESS_AND_EXIT - upon succesful addition # ERR_* - upon encountering an error # sub add_users(@) { my ($users_in, $sync_flag) = @_; my $file = ALLOW_USERS_FILE(); my @users; my $all_users_token = ALL_USERS_TOKEN(); if (!defined $users_in) { $users_in = ""; open(FILE, "> $file") || return ERR_UNABLE_TO_OPEN_ALLOWED_USERS(); print "\n"; close(FILE); if ($sync_flag != 1) { sync_allowusers_file($users_in); } return SUCCESS_AND_EXIT(); } $users_in =~ s/'//g; if ($sync_flag != 1) { @users = split(/\s/, $users_in); } else { @users = split(/\:/, $users_in); } if ($users_in =~ /$all_users_token/) { # User specified the all users token, check to see if it # already exists, if not append it to the file # Check to make sure no other users were specified if ($users_in !~ /^\s*$all_users_token\s*$/) { return ERR_INVALID_USER_SPECIFIED(); } } open(FILE, "> $file") || return ERR_UNABLE_TO_OPEN_ALLOWED_USERS(); foreach (@users) { if ($_ eq $all_users_token) { print FILE "+\n"; dspmsg(200, "Allowing all cluster users to change password cluster wide.\n") if ($sync_flag != 1); } else { dspmsg(201, "Adding user %s\n", $_) if ($sync_flag != 1); print FILE $_ . "\n"; } } close(FILE); if ($sync_flag != 1) { sync_allowusers_file($users_in); } SUCCESS_AND_EXIT(); } ## # Function: sync_allowusers_file # # Description: # Synchronize the contents of the allow users password file across all specified nodes # in the cluster. Invokes the cl_manageallowpasswd perl script on the remote nodes with # a list of users to add. # # Arguments: # $: users_in - list of users to add cluster wide # # Return: # SUCCESS_AND_EXIT - if successful # ERR_* - if an error occurs # sub sync_allowusers_file($) { my ($users_in) = @_; my @nodes = `/usr/es/sbin/cluster/utilities/clnodename`; my ($localnode, $node, $ret, $cmd); open(CMD, GET_LOCAL_NODENAME() . " |"); $localnode = ; close(CMD); $localnode =~ s/\s//g; $users_in =~ s/\s/\:/g; foreach $node (@nodes) { $node =~ s/\s//g; if ($node eq $localnode) { next; } $cmd = "/usr/es/sbin/cluster/utilities/cl_rsh $node /usr/es/sbin/cluster/utilities/cl_manageallowpasswd -s '$users_in'"; $ret = system($cmd); if ($ret != 0) { dspmsg(202, "Node %s could not be contacted, please ensure the clcomdES sub-system is\ running properly and that the /usr/es/sbin/cluster/etc/rhosts file is properly configured.\n\n", $node); } else { dspmsg(203, "Synchronized allowed user data to node: %s.\n", $node); } } } ## # # Function: # list_all_cluster_users # # Description: # List all users defined to the list. Indirectly invoked by # list_all_cluster_users_smit via a call to cl_rsh $node ... # # Arguments: # N/A # # Return: # SUCCESS_AND_EXIT # sub list_all_cluster_users() { my ($username, $users); open(FILE, "< /etc/passwd") || return ERR_UNABLE_TO_OPEN_ETC_PASSWD(); foreach () { $username = (split(/:/))[0]; $users .= "$username:"; } print $users . "\n"; SUCCESS_AND_EXIT(); } ## # # Function: # list_all_cluster_users_smit_ra # # Description: # List all cluster users for SMIT by invoking the list_all_cluster_users # function (above) indirectly via cl_rsh on each node and then sifting # out user names that are not common among all nodes. # This function will also filter out user names that are system users # as defined in /usr/es/sbin/cluster/etc/sysusers # And this function ignores root and adm users # # Arguments: # N/A # # Return: # SUCCESS_AND_EXIT - upon successful execution # ERR_* - if an error occurs # sub list_all_cluster_users_smit_ra() { my ($node, $user, $sysuser, $match, $matches); my ($userA, $userB, $nodeA, $nodeB); my (@users, @nonsys_users, @common_users); my (@nodes) = `/usr/es/sbin/cluster/utilities/clnodename`; my (%usersByNode); if ($#nodes < 0) { return ERR_NO_NODES_AVAILABLE(); } # # Collect the list of users for each node # foreach $node (@nodes) { $node =~ s/\s//g; @users = split(/:/, `/usr/es/sbin/cluster/utilities/cl_rsh $node /usr/es/sbin/cluster/utilities/cl_manageallowpasswd -P 2>/dev/null`); # Filter out any system users that may appear @nonsys_users = (); foreach $user (@users) { $match = 0; foreach $sysuser (@{$global{"SYSTEM_USERS"}}) { $sysuser =~ s/\s//g; if ($user eq $sysuser) { $match = 1; } if ($user eq "root" or $user eq "adm") { $match = 0; } } if ($match != 1) { push @nonsys_users, $user; } } @{$usersByNode{$node}} = @nonsys_users; } # Now determine which users are common amongst all the nodes $nodeA = $nodes[0]; if ($#nodes > 0) { foreach $userA (@{$usersByNode{$nodeA}}) { $matches = 0; foreach $nodeB (@nodes) { if ($nodeA eq $nodeB) { next; } $match = 0; foreach $userB (@{$usersByNode{$nodeB}} ) { if ($userA eq $userB) { $match = 1; } } if ($match == 1) { $matches++; } } if ($matches == $#nodes) { push @common_users, $userA; } } } else { @common_users = @{$usersByNode{$nodeA}}; } print ALL_USERS_TOKEN(); print "\n"; foreach (@common_users) { if (!/^\s*$/) { print $_ . "\n" }; } } ## # # Function: # list_all_cluster_users_smit # # Description: # List all cluster users for SMIT by invoking the list_all_cluster_users # function (above) indirectly via cl_rsh on each node and then sifting # out user names that are not common among all nodes. # This function will also filter out user names that are system users # as defined in /usr/es/sbin/cluster/etc/sysusers # # Arguments: # N/A # # Return: # SUCCESS_AND_EXIT - upon successful execution # ERR_* - if an error occurs # sub list_all_cluster_users_smit() { my ($node, $user, $sysuser, $match, $matches); my ($userA, $userB, $nodeA, $nodeB); my (@users, @nonsys_users, @common_users); my (@nodes) = `/usr/es/sbin/cluster/utilities/clnodename`; my (%usersByNode); if ($#nodes < 0) { return ERR_NO_NODES_AVAILABLE(); } # # Collect the list of users for each node # foreach $node (@nodes) { $node =~ s/\s//g; @users = split(/:/, `/usr/es/sbin/cluster/utilities/cl_rsh $node /usr/es/sbin/cluster/utilities/cl_manageallowpasswd -P 2>/dev/null`); # Filter out any system users that may appear @nonsys_users = (); foreach $user (@users) { $match = 0; foreach $sysuser (@{$global{"SYSTEM_USERS"}}) { $sysuser =~ s/\s//g; if ($user eq $sysuser) { $match = 1; } } if ($match != 1) { push @nonsys_users, $user; } } @{$usersByNode{$node}} = @nonsys_users; } # Now determine which users are common amongst all the nodes $nodeA = $nodes[0]; if ($#nodes > 0) { foreach $userA (@{$usersByNode{$nodeA}}) { $matches = 0; foreach $nodeB (@nodes) { if ($nodeA eq $nodeB) { next; } $match = 0; foreach $userB (@{$usersByNode{$nodeB}} ) { if ($userA eq $userB) { $match = 1; } } if ($match == 1) { $matches++; } } if ($matches == $#nodes) { push @common_users, $userA; } } } else { @common_users = @{$usersByNode{$nodeA}}; } print ALL_USERS_TOKEN(); print "\n"; foreach (@common_users) { if (!/^\s*$/) { print $_ . "\n" }; } } ## # # Function: # list_all_allowed_users # # Description: # List all allowed users for SMIT consumption # # Arguments: # N/A # # Return: # SUCCESS_AND_EXIT sub list_all_allowed_users() { my $users = ""; my $file = ALLOW_USERS_FILE(); my $all_users_token = ALL_USERS_TOKEN(); open(FILE, "< $file") || return ERR_UNABLE_TO_OPEN_ALLOWED_USERS(); foreach () { s/\s//g; $users .= "$_ "; } if ($users =~ /\+/) { $users = $all_users_token; } $users =~ s/\s$//; # Remove trailing space print "#users\n"; print "$users\n"; SUCCESS_AND_EXIT(); } ## # # Function: # usage # # Description: # Prints a usage statement for the script, this is not intended to be # used by end-customers thus the usage statement is not ILS compliant. # This function will terminate the running perl script with exit(-1) # # Arguments: # N/A # # Return: # N/A # sub usage() { my $progname = $0; $progname =~ s/^.+\/([^\/]{1,})$/$1/; print "\n\n$progname: Manage the allowed password database\n\n"; print "-V SMIT output listing all available cluster users.\n"; print "-L SMIT output lising all allowed users.\n"; print "-a users... add users to the allowed users list.\n"; print "-s users... synchronize users to another node.\n"; print "-h this usage screen.\n"; print "\n\n"; exit(1); } ## # Function: dspmsg # # Description: # Invokes the dspmsg command with the arguments specified below (ILS function) # # Arguments: # $: id - message catalog id for cspoc.cat (set 124) # $: msg - message as string to display for LC_ALL=C # @: args - arguments to provide to message string # # Return: # N/A sub dspmsg(@) { my ($id, $msg, @args) = @_; my $arguments = join(", ", @args); $msg =~ s/\n/\\n/g; my $cmd = "/usr/bin/dspmsg -s 124 cspoc.cat $id \"$msg\" $arguments"; system($cmd); } ## # Function: print_error # # Description: # Print an error message based on the error code # # Arguments: # $: error code # # Return: # SUCCESS # sub print_error($) { my $val = $_[0]; if ($val == ERR_INVALID_USER_SPECIFIED()) { dspmsg(204, "ERROR: Please select one or more users to authorize password change\ across the cluster, or select only the ALL_USERS option to allow all\ cluster users to change their password cluster wide.\n"); } if ($val == ERR_UNABLE_TO_OPEN_ETC_PASSWD()) { dspmsg(205, "ERROR: Unable to open /etc/passwd, this operation can only be\ performed by a user with root privileges. Please logout and\ log back in as a user with such authority before trying this\ operation again, or contact your system administrator for\ further details.\n"); } if ($val == ERR_UNABLE_TO_OPEN_ALLOWED_USERS()) { dspmsg(206, "ERROR: Unable to open the allowed users database. This operation\ can only be performed by a user with root privileges. Please\ logout and log back in as a user with such authority before trying\ this operation again, or contact your system administrator for\ further details.\n"); } if ($val == ERR_NO_NODES_AVAILABLE()) { dspmsg(207, "No cluster nodes are defined on the local node. Please check\ your cluster configuration before attempting this operation again.\n"); } SUCCESS(); } ## # # Function: # modify_hafiles # # Description: # Modifies the HA files we use for allowing users to change passwords # # Arguments: # N/A # # Returns: # SUCCESS(), or ERR_AND_EXIT() # sub modify_hafiles() { if ( system("/usr/bin/touch " . ALLOW_USERS_FILE() ) != 0) { dspmsg(208, "Aborting could not touch file %s.\n", ALLOW_USERS_FILE() ); return ERR_AND_EXIT(); } if ( system("/usr/bin/chown root.system " . ALLOW_USERS_FILE()) != 0) { dspmsg(209, "Aborting could not change ownership of %s file.\n", ALLOW_USERS_FILE()); return ERR_AND_EXIT(); } if ( system("/usr/bin/chmod 600 " . ALLOW_USERS_FILE()) != 0) { dspmsg(210, "Aborting could not change mode of %s file.\n", ALLOW_USERS_FILE()); return ERR_AND_EXIT(); } SUCCESS(); } ## # # Function: # isRootUser # # Description: # Determines if the current running user is the root user # # Arguments: # N/A # # Returns: # 0 - if user is NOT the root user # 1 - if user IS the root user # sub isRootUser() { my ($real_gid, $real_uid, $eff_gid, $eff_uid) = (-1, -1, -1, -1); my $cmdoutput = `/usr/bin/whoami`; chomp($cmdoutput); if ($cmdoutput eq "root") { # Get the real and effective gid/uid for this process $real_gid = $(; $real_uid = $<; $eff_gid = $); $eff_uid = $>; #print "Real UID = $real_uid GID = $real_gid\n"; #print "Effective UID = $eff_uid GID = $eff_gid\n"; # The real gid and real uid should match the effective gid/uid for # this process. if ($real_gid ne $eff_gid || $real_uid ne $eff_uid) { return 0; } # If the real gid/uid is set to root, then we're running as root. if ($real_gid =~ /\s*0\s*/ && $real_uid == 0) { return 1; } } return 0; } ## # # Function: # MainFunc # # Description: # Main function for this script, starting point. Parses cmdline options # and invokes sub routines for performing requested operaton(s) # # Arguments: # N/A # sub MainFunc() { my $val = 999; my $ret = 999; my %options; # Read in list of system users to exclude from list of available # cluster users # open(FILE, "< " . SYSTEM_USERS_FILE()); @{$global{"SYSTEM_USERS"}} = ; close(FILE); # Is the user of the correct authority to run this command? if (isRootUser() != 1) { dspmsg(211, "ERROR: The SMIT menu chosen can only be accessed by\ the system administrator.\n"); exit(-1); } # Setup files we rely upon if (modify_hafiles() == ERR_AND_EXIT()) { exit(-1); } # Parse incoming arguments getopts('hPVLa:R:s:', \%options); # Process single-character switches # with switch clustering foreach $val (sort keys %options) { if ($val eq 'a') { $ret = add_users($options{$val}, \%options); } elsif ($val eq 'P') { $ret = list_all_cluster_users(); } elsif ($val eq 'V') { $ret = list_all_cluster_users_smit(); } elsif ($val eq 'R') { $ret = list_all_cluster_users_smit_ra(); } elsif ($val eq 'L') { $ret = list_all_allowed_users(); } elsif ($val eq 's') { $ret = add_users($options{$val}, 1); } elsif ($val eq 'h') { usage(); } } if (!defined $ret) { exit(0); } if ($ret == SUCCESS_AND_EXIT()) { exit(0); } elsif ($ret != SUCCESS()) { print_error($ret); exit(-1); } } MainFunc();