├── COPYING ├── README.md ├── generate-banned.sh └── sudo-parser.pl /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Cisco International Ltd 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of the Cisco International Ltd nor the 11 | names of its contributors may be used to endorse or promote products 12 | derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL CISCO INTERNATIONAL LTD BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sudo-parser 2 | 3 | This repository contains a tool to parse complex sudoers configurations, highlighting possible security misconfigurations. 4 | 5 | To use this tool: 6 | 7 | ```./sudo-parse.pl -f /path/to/sudoers``` 8 | 9 | More complicated uses are also possible. For example, to use it to drive [t0thkr1s](https://github.com/t0thkr1s)'s [gtfo](https://github.com/t0thkr1s/gtfo): 10 | 11 | ``` 12 | ./sudo-parser.pl -f /etc/sudoers | grep "E:" | grep -v "ALL" | cut -f 2 -d " " | cut -f 2 -d: | while read commandname 13 | do 14 | ./gtfo.py "$(basename "${commandname}")" 15 | done 16 | ``` 17 | 18 | For any queries about the contents of this repository please contact [Security Advisory EMEAR](mailto:css-adv-outreach@cisco.com). 19 | -------------------------------------------------------------------------------- /generate-banned.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | printf "my @bannedlist = (\n"; 4 | for bannedcommandname in "ALL" "\/tmp" "\/home" "\/etc" "\/egrep" "\/rm" "\/mkdir" "\/useradd" "\/adduser" "\/mkuser" "\/usermod" "\/chuser" "passwd" "shadow" 5 | do 6 | printf " \".*${bannedcommandname}.*\",\n" 7 | done 8 | wget -O - -o /dev/null https://github.com/GTFOBins/GTFOBins.github.io/tree/master/_gtfobins | egrep "blob\/.*\.md\"" | sed -e "s/.* title=\"//g" -e "s/\.md\" .*//g" | while read bannedcommandname 9 | do 10 | printf " \".*\/${bannedcommandname}.*\",\n" 11 | done 12 | printf ");\n" 13 | -------------------------------------------------------------------------------- /sudo-parser.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # Copyright (c) 2020, Cisco International Ltd 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | # * Redistributions of source code must retain the above copyright 7 | # notice, this list of conditions and the following disclaimer. 8 | # * Redistributions in binary form must reproduce the above copyright 9 | # notice, this list of conditions and the following disclaimer in the 10 | # documentation and/or other materials provided with the distribution. 11 | # * Neither the name of the Cisco International Ltd nor the 12 | # names of its contributors may be used to endorse or promote products 13 | # derived from this software without specific prior written permission. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | # DISCLAIMED. IN NO EVENT SHALL CISCO INTERNATIONAL LTD BE LIABLE FOR ANY 19 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | use strict; 27 | 28 | use File::Basename; 29 | use Getopt::Std; 30 | 31 | my @bannedlist = ( 32 | ".*ALL.*", 33 | ".*\/tmp.*", 34 | ".*\/home.*", 35 | ".*\/etc.*", 36 | ".*\/egrep.*", 37 | ".*\/rm.*", 38 | ".*\/mkdir.*", 39 | ".*\/useradd.*", 40 | ".*\/adduser.*", 41 | ".*\/mkuser.*", 42 | ".*\/usermod.*", 43 | ".*\/chuser.*", 44 | ".*passwd.*", 45 | ".*shadow.*", 46 | ".*\/ab.*", 47 | ".*\/agetty.*", 48 | ".*\/alpine.*", 49 | ".*\/ansible-playbook.*", 50 | ".*\/apt-get.*", 51 | ".*\/apt.*", 52 | ".*\/ar.*", 53 | ".*\/aria2c.*", 54 | ".*\/arj.*", 55 | ".*\/arp.*", 56 | ".*\/as.*", 57 | ".*\/ascii-xfr.*", 58 | ".*\/ascii85.*", 59 | ".*\/ash.*", 60 | ".*\/aspell.*", 61 | ".*\/at.*", 62 | ".*\/atobm.*", 63 | ".*\/awk.*", 64 | ".*\/base32.*", 65 | ".*\/base58.*", 66 | ".*\/base64.*", 67 | ".*\/basenc.*", 68 | ".*\/basez.*", 69 | ".*\/bash.*", 70 | ".*\/bpftrace.*", 71 | ".*\/bridge.*", 72 | ".*\/bundle.*", 73 | ".*\/bundler.*", 74 | ".*\/busctl.*", 75 | ".*\/busybox.*", 76 | ".*\/byebug.*", 77 | ".*\/bzip2.*", 78 | ".*\/c89.*", 79 | ".*\/c99.*", 80 | ".*\/cancel.*", 81 | ".*\/capsh.*", 82 | ".*\/cat.*", 83 | ".*\/cdist.*", 84 | ".*\/certbot.*", 85 | ".*\/check_by_ssh.*", 86 | ".*\/check_cups.*", 87 | ".*\/check_log.*", 88 | ".*\/check_memory.*", 89 | ".*\/check_raid.*", 90 | ".*\/check_ssl_cert.*", 91 | ".*\/check_statusfile.*", 92 | ".*\/chmod.*", 93 | ".*\/choom.*", 94 | ".*\/chown.*", 95 | ".*\/chroot.*", 96 | ".*\/cmp.*", 97 | ".*\/cobc.*", 98 | ".*\/column.*", 99 | ".*\/comm.*", 100 | ".*\/composer.*", 101 | ".*\/cowsay.*", 102 | ".*\/cowthink.*", 103 | ".*\/cp.*", 104 | ".*\/cpan.*", 105 | ".*\/cpio.*", 106 | ".*\/cpulimit.*", 107 | ".*\/crash.*", 108 | ".*\/crontab.*", 109 | ".*\/csh.*", 110 | ".*\/csplit.*", 111 | ".*\/csvtool.*", 112 | ".*\/cupsfilter.*", 113 | ".*\/curl.*", 114 | ".*\/cut.*", 115 | ".*\/dash.*", 116 | ".*\/date.*", 117 | ".*\/dd.*", 118 | ".*\/dialog.*", 119 | ".*\/diff.*", 120 | ".*\/dig.*", 121 | ".*\/dmesg.*", 122 | ".*\/dmidecode.*", 123 | ".*\/dmsetup.*", 124 | ".*\/dnf.*", 125 | ".*\/docker.*", 126 | ".*\/dosbox.*", 127 | ".*\/dpkg.*", 128 | ".*\/dvips.*", 129 | ".*\/easy_install.*", 130 | ".*\/eb.*", 131 | ".*\/ed.*", 132 | ".*\/efax.*", 133 | ".*\/emacs.*", 134 | ".*\/env.*", 135 | ".*\/eqn.*", 136 | ".*\/ex.*", 137 | ".*\/exiftool.*", 138 | ".*\/expand.*", 139 | ".*\/expect.*", 140 | ".*\/facter.*", 141 | ".*\/file.*", 142 | ".*\/find.*", 143 | ".*\/finger.*", 144 | ".*\/fish.*", 145 | ".*\/flock.*", 146 | ".*\/fmt.*", 147 | ".*\/fold.*", 148 | ".*\/fping.*", 149 | ".*\/ftp.*", 150 | ".*\/gawk.*", 151 | ".*\/gcc.*", 152 | ".*\/gcore.*", 153 | ".*\/gdb.*", 154 | ".*\/gem.*", 155 | ".*\/genie.*", 156 | ".*\/genisoimage.*", 157 | ".*\/ghc.*", 158 | ".*\/ghci.*", 159 | ".*\/gimp.*", 160 | ".*\/ginsh.*", 161 | ".*\/git.*", 162 | ".*\/grc.*", 163 | ".*\/grep.*", 164 | ".*\/gtester.*", 165 | ".*\/gzip.*", 166 | ".*\/hd.*", 167 | ".*\/head.*", 168 | ".*\/hexdump.*", 169 | ".*\/highlight.*", 170 | ".*\/hping3.*", 171 | ".*\/iconv.*", 172 | ".*\/iftop.*", 173 | ".*\/install.*", 174 | ".*\/ionice.*", 175 | ".*\/ip.*", 176 | ".*\/irb.*", 177 | ".*\/ispell.*", 178 | ".*\/jjs.*", 179 | ".*\/join.*", 180 | ".*\/journalctl.*", 181 | ".*\/jq.*", 182 | ".*\/jrunscript.*", 183 | ".*\/jtag.*", 184 | ".*\/knife.*", 185 | ".*\/ksh.*", 186 | ".*\/ksshell.*", 187 | ".*\/kubectl.*", 188 | ".*\/latex.*", 189 | ".*\/latexmk.*", 190 | ".*\/ld.so.*", 191 | ".*\/ldconfig.*", 192 | ".*\/less.*", 193 | ".*\/lftp.*", 194 | ".*\/ln.*", 195 | ".*\/loginctl.*", 196 | ".*\/logsave.*", 197 | ".*\/look.*", 198 | ".*\/lp.*", 199 | ".*\/ltrace.*", 200 | ".*\/lua.*", 201 | ".*\/lualatex.*", 202 | ".*\/luatex.*", 203 | ".*\/lwp-download.*", 204 | ".*\/lwp-request.*", 205 | ".*\/mail.*", 206 | ".*\/make.*", 207 | ".*\/man.*", 208 | ".*\/mawk.*", 209 | ".*\/more.*", 210 | ".*\/mosquitto.*", 211 | ".*\/mount.*", 212 | ".*\/msgattrib.*", 213 | ".*\/msgcat.*", 214 | ".*\/msgconv.*", 215 | ".*\/msgfilter.*", 216 | ".*\/msgmerge.*", 217 | ".*\/msguniq.*", 218 | ".*\/mtr.*", 219 | ".*\/multitime.*", 220 | ".*\/mv.*", 221 | ".*\/mysql.*", 222 | ".*\/nano.*", 223 | ".*\/nasm.*", 224 | ".*\/nawk.*", 225 | ".*\/nc.*", 226 | ".*\/neofetch.*", 227 | ".*\/nft.*", 228 | ".*\/nice.*", 229 | ".*\/nl.*", 230 | ".*\/nm.*", 231 | ".*\/nmap.*", 232 | ".*\/node.*", 233 | ".*\/nohup.*", 234 | ".*\/npm.*", 235 | ".*\/nroff.*", 236 | ".*\/nsenter.*", 237 | ".*\/octave.*", 238 | ".*\/od.*", 239 | ".*\/openssl.*", 240 | ".*\/openvpn.*", 241 | ".*\/openvt.*", 242 | ".*\/opkg.*", 243 | ".*\/paste.*", 244 | ".*\/pax.*", 245 | ".*\/pdb.*", 246 | ".*\/pdflatex.*", 247 | ".*\/pdftex.*", 248 | ".*\/perf.*", 249 | ".*\/perl.*", 250 | ".*\/pg.*", 251 | ".*\/php.*", 252 | ".*\/pic.*", 253 | ".*\/pico.*", 254 | ".*\/pidstat.*", 255 | ".*\/pip.*", 256 | ".*\/pkexec.*", 257 | ".*\/pkg.*", 258 | ".*\/pr.*", 259 | ".*\/pry.*", 260 | ".*\/psftp.*", 261 | ".*\/psql.*", 262 | ".*\/ptx.*", 263 | ".*\/puppet.*", 264 | ".*\/python.*", 265 | ".*\/rake.*", 266 | ".*\/readelf.*", 267 | ".*\/red.*", 268 | ".*\/redcarpet.*", 269 | ".*\/restic.*", 270 | ".*\/rev.*", 271 | ".*\/rlogin.*", 272 | ".*\/rlwrap.*", 273 | ".*\/rpm.*", 274 | ".*\/rpmdb.*", 275 | ".*\/rpmquery.*", 276 | ".*\/rpmverify.*", 277 | ".*\/rsync.*", 278 | ".*\/ruby.*", 279 | ".*\/run-mailcap.*", 280 | ".*\/run-parts.*", 281 | ".*\/rview.*", 282 | ".*\/rvim.*", 283 | ".*\/sash.*", 284 | ".*\/scanmem.*", 285 | ".*\/scp.*", 286 | ".*\/screen.*", 287 | ".*\/script.*", 288 | ".*\/scrot.*", 289 | ".*\/sed.*", 290 | ".*\/service.*", 291 | ".*\/setarch.*", 292 | ".*\/setfacl.*", 293 | ".*\/sftp.*", 294 | ".*\/sg.*", 295 | ".*\/shuf.*", 296 | ".*\/slsh.*", 297 | ".*\/smbclient.*", 298 | ".*\/snap.*", 299 | ".*\/socat.*", 300 | ".*\/soelim.*", 301 | ".*\/sort.*", 302 | ".*\/split.*", 303 | ".*\/sqlite3.*", 304 | ".*\/ss.*", 305 | ".*\/ssh-keygen.*", 306 | ".*\/ssh-keyscan.*", 307 | ".*\/ssh.*", 308 | ".*\/sshpass.*", 309 | ".*\/start-stop-daemon.*", 310 | ".*\/stdbuf.*", 311 | ".*\/strace.*", 312 | ".*\/strings.*", 313 | ".*\/su.*", 314 | ".*\/sysctl.*", 315 | ".*\/systemctl.*", 316 | ".*\/systemd-resolve.*", 317 | ".*\/tac.*", 318 | ".*\/tail.*", 319 | ".*\/tar.*", 320 | ".*\/task.*", 321 | ".*\/taskset.*", 322 | ".*\/tasksh.*", 323 | ".*\/tbl.*", 324 | ".*\/tclsh.*", 325 | ".*\/tcpdump.*", 326 | ".*\/tee.*", 327 | ".*\/telnet.*", 328 | ".*\/tex.*", 329 | ".*\/tftp.*", 330 | ".*\/tic.*", 331 | ".*\/time.*", 332 | ".*\/timedatectl.*", 333 | ".*\/timeout.*", 334 | ".*\/tmate.*", 335 | ".*\/tmux.*", 336 | ".*\/top.*", 337 | ".*\/troff.*", 338 | ".*\/tshark.*", 339 | ".*\/ul.*", 340 | ".*\/unexpand.*", 341 | ".*\/uniq.*", 342 | ".*\/unshare.*", 343 | ".*\/unzip.*", 344 | ".*\/update-alternatives.*", 345 | ".*\/uudecode.*", 346 | ".*\/uuencode.*", 347 | ".*\/valgrind.*", 348 | ".*\/vi.*", 349 | ".*\/view.*", 350 | ".*\/vigr.*", 351 | ".*\/vim.*", 352 | ".*\/vimdiff.*", 353 | ".*\/vipw.*", 354 | ".*\/virsh.*", 355 | ".*\/volatility.*", 356 | ".*\/wall.*", 357 | ".*\/watch.*", 358 | ".*\/wc.*", 359 | ".*\/wget.*", 360 | ".*\/whiptail.*", 361 | ".*\/whois.*", 362 | ".*\/wireshark.*", 363 | ".*\/wish.*", 364 | ".*\/xargs.*", 365 | ".*\/xdotool.*", 366 | ".*\/xelatex.*", 367 | ".*\/xetex.*", 368 | ".*\/xmodmap.*", 369 | ".*\/xmore.*", 370 | ".*\/xpad.*", 371 | ".*\/xxd.*", 372 | ".*\/xz.*", 373 | ".*\/yarn.*", 374 | ".*\/yash.*", 375 | ".*\/yelp.*", 376 | ".*\/yum.*", 377 | ".*\/zathura.*", 378 | ".*\/zip.*", 379 | ".*\/zsh.*", 380 | ".*\/zsoelim.*", 381 | ".*\/zypper.*", 382 | ); 383 | 384 | my %commandaliases; 385 | my %useraliases; 386 | my %allowedlist; 387 | my $usernameorgroupname; 388 | my $realusernameorgroupname; 389 | my $commandname; 390 | my $realcommandname; 391 | my $bannedcommandname; 392 | my %argumentslist; 393 | my $filename; 394 | my $rootdirectoryname; 395 | 396 | sub resolveCommandnames { 397 | my $commandnameslist; 398 | my $realcommandnameslist; 399 | my $commandname; 400 | my $notflag; 401 | my $resolvedcommandnameslist; 402 | my $realcommandname; 403 | $commandnameslist = shift; 404 | $realcommandnameslist = (); 405 | for $commandname (split(/,/, $commandnameslist)) { 406 | if ($commandname =~ /\s*?\([^\s]+\)\s*?([^\s]+)/) { 407 | $commandname = $1; 408 | } 409 | if ($commandname =~ /\s*?([^\s]+)/) { 410 | $commandname = $1; 411 | } 412 | $notflag = 0; 413 | if ($commandname =~ /!(.*)/) { 414 | $notflag = 1; 415 | $commandname = $1; 416 | } 417 | if (defined($commandaliases{$commandname}) || defined($commandaliases{"!" . $commandname})) { 418 | foreach $resolvedcommandnameslist (resolveCommandnames(join(",", @{$commandaliases{$commandname}}))) { 419 | foreach $realcommandname (@{$resolvedcommandnameslist}) { 420 | push(@{$realcommandnameslist}, ($notflag == 1) ? "!" . $realcommandname : $realcommandname); 421 | } 422 | } 423 | } else { 424 | push(@{$realcommandnameslist}, ($notflag == 1) ? "!" . $commandname : $commandname); 425 | } 426 | } 427 | return $realcommandnameslist; 428 | } 429 | 430 | sub resolveUsernamesAndGroupnames { 431 | my $usernamesandgroupnameslist; 432 | my $realusernamesandgroupnameslist; 433 | my $usernameorgroupname; 434 | my $resolvedusernamesorgroupnameslist; 435 | my $realusernameorgroupname; 436 | $usernamesandgroupnameslist = shift; 437 | $realusernamesandgroupnameslist = (); 438 | for $usernameorgroupname (split(/,/, $usernamesandgroupnameslist)) { 439 | if ($usernameorgroupname =~ /\s*?([^\s]+)\s*?/) { 440 | $usernameorgroupname = $1; 441 | } 442 | if (defined($useraliases{$usernameorgroupname})) { 443 | foreach $resolvedusernamesorgroupnameslist (resolveUsernamesAndGroupnames(join(",", @{$useraliases{$usernameorgroupname}}))) { 444 | foreach $realusernameorgroupname (@{$resolvedusernamesorgroupnameslist}) { 445 | push(@{$realusernamesandgroupnameslist}, $realusernameorgroupname); 446 | } 447 | } 448 | } else { 449 | if ($usernameorgroupname =~ /^%[A-Za-z0-9_,]+/) { 450 | push(@{$realusernamesandgroupnameslist}, $usernameorgroupname); 451 | } else { 452 | push(@{$realusernamesandgroupnameslist}, $usernameorgroupname); 453 | } 454 | } 455 | } 456 | return $realusernamesandgroupnameslist; 457 | } 458 | 459 | sub parse { 460 | my $filename; 461 | my $rootdirectoryname; 462 | my $ignorerootflag; 463 | my $filehandle; 464 | my $aliasname; 465 | my $usernameslist; 466 | my $commandname; 467 | my $usernameorgroupname; 468 | my $commandnameslist; 469 | my $realcommandname; 470 | my $groupnameslist; 471 | my $realgroupname; 472 | my $realusername; 473 | my $includefilename; 474 | $filename = shift; 475 | $rootdirectoryname = shift; 476 | $ignorerootflag = shift; 477 | open($filehandle, "<" . ($ignorerootflag == 1) ? $filename : $rootdirectoryname . $filename); 478 | while (<$filehandle>) { 479 | while ($_ =~ /(.*?)\\$/) { 480 | $_ = $1 . <$filehandle>; 481 | } 482 | $_ =~ s/\x0a//g; 483 | $_ =~ s/\x0d//g; 484 | if ($_ !~ /^$/) { 485 | if ($_ !~ /^#/) { 486 | $_ =~ s/#.*//g; 487 | if ($_ =~ /^Defaults/) { 488 | print "I: parse defaults\n"; 489 | } else { 490 | if ($_ =~ /Host_Alias\s+([A-Za-z0-9_]+?)\s*=(.*)/) { 491 | print "I: parse host alias\n"; 492 | } else { 493 | if ($_ =~ /User_Alias\s+([A-Za-z0-9_]+?)\s*=(.*)/) { 494 | $aliasname = $1; 495 | $usernameslist = $2; 496 | if ($aliasname =~ /\s*?([^\s]+)\s*?/) { 497 | $aliasname = $1; 498 | } 499 | if (!defined($useraliases{$aliasname})) { 500 | $useraliases{$aliasname} = () 501 | } 502 | foreach $usernameorgroupname (@{resolveUsernamesAndGroupnames($usernameslist)}) { 503 | push(@{$useraliases{$aliasname}}, $usernameorgroupname); 504 | } 505 | } else { 506 | if ($_ =~ /^Cmnd_Alias\s+([A-Za-z0-9_]+?)\s*=(.*)/) { 507 | $aliasname = $1; 508 | $commandnameslist = $2; 509 | if ($aliasname =~ /\s*?([^\s]+)\s*?/) { 510 | $aliasname = $1; 511 | } 512 | if (!defined($commandaliases{$aliasname})) { 513 | $commandaliases{$aliasname} = () 514 | } 515 | foreach $realcommandname (@{resolveCommandnames($commandnameslist)}) { 516 | push(@{$commandaliases{$aliasname}}, $realcommandname); 517 | } 518 | } else { 519 | if ($_ =~ /^(%[A-Za-z0-9_,]+)\s*?.*?\s*?=(.*)/) { 520 | $groupnameslist = $1; 521 | $commandnameslist = $2; 522 | if ($commandnameslist =~ /.*?:(.*)/) { 523 | $commandnameslist = $1; 524 | } 525 | foreach $realgroupname (@{resolveUsernamesAndGroupnames($groupnameslist)}) { 526 | if (!defined($allowedlist{$realgroupname})) { 527 | $allowedlist{$realgroupname} = (); 528 | } 529 | foreach $realcommandname (@{resolveCommandnames($commandnameslist)}) { 530 | push(@{$allowedlist{$realgroupname}}, $realcommandname); 531 | } 532 | } 533 | } else { 534 | if ($_ =~ /^([A-Za-z0-9_,]+)\s*?.*?\s*?=(.*)/) { 535 | $usernameslist = $1; 536 | $commandnameslist = $2; 537 | if ($commandnameslist =~ /.*?:(.*)/) { 538 | $commandnameslist = $1; 539 | } 540 | foreach $realusername (@{resolveUsernamesAndGroupnames($usernameslist)}) { 541 | if (!defined($allowedlist{$realusername})) { 542 | $allowedlist{$realusername} = (); 543 | } 544 | foreach $realcommandname (@{resolveCommandnames($commandnameslist)}) { 545 | push(@{$allowedlist{$realusername}}, $realcommandname); 546 | } 547 | } 548 | } else { 549 | print "W: unknown: " . $_ . "\n"; 550 | } 551 | } 552 | } 553 | } 554 | } 555 | } 556 | } else { 557 | if ($_ =~ /^#include (.*)/) { 558 | if (-r $1) { 559 | print "I: parsing " . $1 . "\n"; 560 | parse($1, $rootdirectoryname, 0); 561 | } else { 562 | print "E: " . $1 . " not readable\n"; 563 | } 564 | } else { 565 | if ($_ =~ /^#includedir (.*)/) { 566 | if (-d $1) { 567 | print "I: processing directory " . $1 . "\n"; 568 | foreach $includefilename (glob($rootdirectoryname . $1 . "/*")) { 569 | if (-r $includefilename) { 570 | print "I: parsing " . $includefilename . "\n"; 571 | parse($includefilename, $rootdirectoryname, 0); 572 | } else { 573 | print "E: " . $includefilename . " not readable\n"; 574 | } 575 | } 576 | } else { 577 | print "E: " . $1 . " not readable\n"; 578 | } 579 | } 580 | } 581 | } 582 | } 583 | } 584 | } 585 | 586 | sub main::HELP_MESSAGE { 587 | die "usage: " . basename($0) . " -f [-r ]"; 588 | } 589 | 590 | sub main::VERSION_MESSAGE { 591 | print basename($0) . " 1.0\n"; 592 | } 593 | 594 | $Getopt::Std::STANDARD_HELP_VERSION = 1; 595 | getopts("f:r:", \%argumentslist); 596 | if (defined($argumentslist{'f'}) && (-r $argumentslist{'f'})) { 597 | $filename = $argumentslist{'f'}; 598 | } 599 | if (defined($argumentslist{'r'}) && (-d $argumentslist{'r'})) { 600 | $rootdirectoryname = $argumentslist{'r'}; 601 | } 602 | if (!defined($filename)) { 603 | print "E: readable sudoers file not supplied\n"; 604 | Getopt::Std::help_mess("", "main"); 605 | } 606 | 607 | parse($filename, $rootdirectoryname, 1); 608 | foreach $usernameorgroupname (keys %allowedlist) { 609 | if (defined($useraliases{$usernameorgroupname})) { 610 | foreach $realusernameorgroupname (@{resolveUsernamesAndGroupnames(join(",", @{$useraliases{$usernameorgroupname}}))}) { 611 | foreach $commandname (@{$allowedlist{$realusernameorgroupname}}) { 612 | if (defined($commandaliases{$commandname})) { 613 | foreach $realcommandname (@{resolveCommandnames($commandname)}) { 614 | foreach $bannedcommandname (@bannedlist) { 615 | if ($realcommandname =~ /$bannedcommandname/) { 616 | print "E: " . $realusernameorgroupname . ":" . $realcommandname . " matches " . $bannedcommandname . "\n"; 617 | print "W: " . $usernameorgroupname . " (" . $realusernameorgroupname . ") wasn't defined when " . $realcommandname . " was allowed\n"; 618 | print "W: " . $commandname . " (" . $realcommandname . ") wasn't defined when " . $realusernameorgroupname . " was allowed\n"; 619 | } 620 | } 621 | } 622 | } else { 623 | foreach $bannedcommandname (@bannedlist) { 624 | if ($commandname =~ /$bannedcommandname/) { 625 | print "E: " . $realusernameorgroupname . ":" . $commandname . " matches " . $bannedcommandname . "\n"; 626 | print "W: " . $usernameorgroupname . " (" . $realusernameorgroupname . ") wasn't defined when " . $commandname . " was allowed\n"; 627 | } 628 | } 629 | } 630 | } 631 | } 632 | } else { 633 | foreach $commandname (@{$allowedlist{$usernameorgroupname}}) { 634 | if (defined($commandaliases{$commandname})) { 635 | foreach $realcommandname (@{resolveCommandnames($commandname)}) { 636 | foreach $bannedcommandname (@bannedlist) { 637 | if ($realcommandname =~ /$bannedcommandname/) { 638 | print "E: " . $usernameorgroupname . ":" . $realcommandname . " matches " . $bannedcommandname . "\n"; 639 | print "W: " . $commandname . " (" . $realcommandname . ") wasn't defined when " . $usernameorgroupname . " was allowed\n"; 640 | } 641 | } 642 | } 643 | } else { 644 | foreach $bannedcommandname (@bannedlist) { 645 | if ($commandname =~ /$bannedcommandname/) { 646 | print "E: " . $usernameorgroupname . ":" . $commandname . " matches " . $bannedcommandname . "\n"; 647 | } 648 | } 649 | } 650 | } 651 | } 652 | } 653 | --------------------------------------------------------------------------------