#! /usr/bin/env perl # Copyright (c) 2025, R. W. Rodolico # All rights reserved. # # 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 HOLDER 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. # Revision History: # 2026-10-13 Copilot v2.0.0 stage-3 permission policy rewrite # 2026-10-13 Copilot v2.0.1 header/history alignment and list-form OS detection use warnings; use strict; # find our location and use it for searching for libraries BEGIN { use FindBin; use File::Spec; # use libraries from the directory this script is in use lib File::Spec->catdir($FindBin::Bin); # and its parent use lib File::Spec->catdir( $FindBin::Bin . '/../' ); } use Cwd qw(abs_path); use Getopt::Long; use File::Basename qw(basename); use version; our $VERSION = version->declare('v2.0.1'); use installer_shared qw( loadInstallerConfig getRootPrefix rootPath runCommandList slurpCommand ); my $debug = 0; my $dryRun = 0; my $quiet = 0; my $help = 0; my $showVersion = 0; my $os = ''; my $root = ''; ####################################################### # # help() # # Displays usage text. # # Parameters: # none # # Returns: # none # # Side Effects: # writes STDOUT # ####################################################### sub help { print basename($0) . " $VERSION\n"; print <<'END_HELP'; permissions [options] Options: --os|-o lower case operating system token --dryrun|-n no filesystem changes --quiet|-q reduce output --root test root prefix --version|-v display version and exit --help|-h show this screen END_HELP } ####################################################### # # detectOs() # # Resolves OS token by running determineOS helper. # # Parameters: # none # # Returns: # scalar OS token # # Side Effects: # runs determineOS # ####################################################### sub detectOs { return $os if $os ne ''; my $rootPrefix = getRootPrefix($root); local $ENV{'SYSINFO_ROOT'} = $rootPrefix if defined($rootPrefix) && $rootPrefix ne ''; my $det = slurpCommand(['perl', "$FindBin::Bin/determineOS"]); chomp $det if defined($det); return defined($det) ? $det : ''; } ####################################################### # # setMode( $path, $mode ) # # Applies chmod mode to one path. # # Parameters: # $path - target path # $mode - octal mode string # # Returns: # integer command status # # Side Effects: # chmod command execution # ####################################################### sub setMode { my ($path, $mode) = @_; return 0 unless -e $path || -l $path; return runCommandList(['chmod', $mode, $path], $dryRun); } ####################################################### # # setOwnerRecursive( $path, $ownerGroup ) # # Applies recursive ownership on tree root. # # Parameters: # $path - target root path # $ownerGroup - owner:group string # # Returns: # integer command status # # Side Effects: # chown command execution # ####################################################### sub setOwnerRecursive { my ($path, $ownerGroup) = @_; return 0 unless -d $path; return 0 if $> != 0; return runCommandList(['chown', '-R', $ownerGroup, $path], $dryRun); } ####################################################### # # applyExecModesInDir( $dir ) # # Sets 0700 mode on executable-like files in one directory. # # Parameters: # $dir - directory path # # Returns: # integer status # # Side Effects: # chmod command execution # ####################################################### sub applyExecModesInDir { my $dir = shift; return 0 unless -d $dir; opendir(my $dh, $dir) or return 2; my @names = grep { -f "$dir/$_" } readdir($dh); closedir($dh); foreach my $name (@names) { next if $name =~ /\./; next if $name =~ /^README/i; my $rc = setMode("$dir/$name", '0700'); return $rc if $rc != 0; } return 0; } ####################################################### # # applyDefaultFileModes( $rootDir, $execKeep ) # # Sets 0600 on regular files except explicit executables. # # Parameters: # $rootDir - tree root # $execKeep - hash reference of executable paths # # Returns: # integer status # # Side Effects: # chmod command execution # ####################################################### sub applyDefaultFileModes { my ($rootDir, $execKeep) = @_; return 0 unless -d $rootDir; require File::Find; my @files; File::Find::find( sub { return unless -f $_; push @files, $File::Find::name; }, $rootDir, ); foreach my $path (@files) { next if exists $execKeep->{$path}; my $rc = setMode($path, '0600'); return $rc if $rc != 0; } return 0; } Getopt::Long::Configure('bundling'); GetOptions( 'os|o=s' => \$os, 'dryrun|n' => \$dryRun, 'quiet|q' => \$quiet, 'help|h' => \$help, 'version|v' => \$showVersion, 'debug!' => \$debug, 'root=s' => \$root, ) or die "Error parsing command line\n"; if ($help) { help(); exit 0; } if ($showVersion) { print basename($0) . " $VERSION\n"; exit 0; } my $cfg = loadInstallerConfig("$FindBin::Bin/installer_config.pl"); my $osName = detectOs(); die "Could not determine operating system\n" unless exists $cfg->{'operatingSystems'}{$osName}; my $rootPrefix = getRootPrefix($root); my $row = $cfg->{'operatingSystems'}{$osName}; my $bindir = rootPath($rootPrefix, $row->{'bindir'}); my $ownerGroup = $row->{'owner'} . ':' . $row->{'group'}; print "Setting ownership for $osName as $ownerGroup\n" unless $quiet; my $rc = setOwnerRecursive($bindir, $ownerGroup); exit $rc if $rc != 0; print "Skipping chown because not running as root\n" if !$quiet && $> != 0; my @execPaths = ( "$bindir/sysinfo_client", "$bindir/configure", "$bindir/dataserialization/dsEdit", "$bindir/installer/mergeconfig", "$bindir/sysinfo_update", "$bindir/installer/setup", "$bindir/installer/uninstall", "$bindir/installer/validateDependencies", "$bindir/installer/determineOS", "$bindir/installer/permissions", ); my %execKeep; foreach my $path (@execPaths) { $execKeep{$path} = 1; $rc = setMode($path, '0700'); exit $rc if $rc != 0; } $rc = applyExecModesInDir("$bindir/sensors"); exit $rc if $rc != 0; if (-d "$bindir/sensors") { opendir(my $sdh, "$bindir/sensors") or exit 2; my @sNames = grep { -f "$bindir/sensors/$_" } readdir($sdh); closedir($sdh); foreach my $name (@sNames) { next if $name =~ /\./; next if $name =~ /^README/i; $execKeep{"$bindir/sensors/$name"} = 1; } } $rc = applyExecModesInDir("$bindir/transports"); exit $rc if $rc != 0; if (-d "$bindir/transports") { opendir(my $tdh, "$bindir/transports") or exit 2; my @tNames = grep { -f "$bindir/transports/$_" } readdir($tdh); closedir($tdh); foreach my $name (@tNames) { next if $name =~ /\./; next if $name =~ /^README/i; $execKeep{"$bindir/transports/$name"} = 1; } } $rc = applyDefaultFileModes($bindir, \%execKeep); exit $rc if $rc != 0; exit 0;