1#!/usr/bin/env perl 2use strict; 3 4# 5# Converts a sudoers file to LDIF format in prepration for loading into 6# the LDAP server. 7# 8 9# BUGS: 10# Does not yet handle multiple lines with : in them 11# Does not yet remove quotation marks from options 12# Does not yet escape + at the beginning of a dn 13# Does not yet handle line wraps correctly 14# Does not yet handle multiple roles with same name (needs tiebreaker) 15# 16# CAVEATS: 17# Sudoers entries can have multiple RunAs entries that override former ones, 18# with LDAP sudoRunAs{Group,User} applies to all commands in a sudoRole 19 20my %RA; 21my %UA; 22my %HA; 23my %CA; 24my $base=$ENV{SUDOERS_BASE} or die "$0: Container SUDOERS_BASE undefined\n"; 25my @options=(); 26 27my $did_defaults=0; 28my $order = 0; 29 30# parse sudoers one line at a time 31while (<>){ 32 33 # remove comment 34 s/#.*//; 35 36 # line continuation 37 $_.=<> while s/\\\s*$//s; 38 39 # cleanup newline 40 chomp; 41 42 # ignore blank lines 43 next if /^\s*$/; 44 45 if (/^Defaults\s+/i) { 46 my $opt=$'; 47 $opt=~s/\s+$//; # remove trailing whitespace 48 push @options,$opt; 49 } elsif (/^(\S+)\s+(.+)=\s*(.*)/) { 50 51 # Aliases or Definitions 52 my ($p1,$p2,$p3)=($1,$2,$3); 53 $p2=~s/\s+$//; # remove trailing whitespace 54 $p3=~s/\s+$//; # remove trailing whitespace 55 56 if ($p1 eq "User_Alias") { 57 $UA{$p2}=$p3; 58 } elsif ($p1 eq "Runas_Alias") { 59 $RA{$p2}=$p3; 60 } elsif ($p1 eq "Host_Alias") { 61 $HA{$p2}=$p3; 62 } elsif ($p1 eq "Cmnd_Alias") { 63 $CA{$p2}=$p3; 64 } else { 65 if (!$did_defaults++){ 66 # do this once 67 print "dn: cn=defaults,$base\n"; 68 print "objectClass: top\n"; 69 print "objectClass: sudoRole\n"; 70 print "cn: defaults\n"; 71 print "description: Default sudoOption's go here\n"; 72 print "sudoOption: $_\n" foreach @options; 73 printf "sudoOrder: %d\n", ++$order; 74 print "\n"; 75 } 76 # Definition 77 my @users=split /\s*,\s*/,$p1; 78 my @hosts=split /\s*,\s*/,$p2; 79 my @cmds= split /\s*,\s*/,$p3; 80 @options=(); 81 print "dn: cn=$users[0],$base\n"; 82 print "objectClass: top\n"; 83 print "objectClass: sudoRole\n"; 84 print "cn: $users[0]\n"; 85 # will clobber options 86 print "sudoUser: $_\n" foreach expand(\%UA,@users); 87 print "sudoHost: $_\n" foreach expand(\%HA,@hosts); 88 foreach (@cmds) { 89 if (s/^\(([^\)]+)\)\s*//) { 90 my @runas = split(/:\s*/, $1); 91 if (defined($runas[0])) { 92 print "sudoRunAsUser: $_\n" foreach expand(\%RA, split(/,\s*/, $runas[0])); 93 } 94 if (defined($runas[1])) { 95 print "sudoRunAsGroup: $_\n" foreach expand(\%RA, split(/,\s*/, $runas[1])); 96 } 97 } 98 } 99 print "sudoCommand: $_\n" foreach expand(\%CA,@cmds); 100 print "sudoOption: $_\n" foreach @options; 101 printf "sudoOrder: %d\n", ++$order; 102 print "\n"; 103 } 104 105 } else { 106 print "parse error: $_\n"; 107 } 108 109} 110 111# 112# recursively expand hash elements 113sub expand{ 114 my $ref=shift; 115 my @a=(); 116 117 # preen the line a little 118 foreach (@_){ 119 # if NOPASSWD: directive found, mark entire entry as not requiring 120 s/NOPASSWD:\s*// && push @options,"!authenticate"; 121 s/PASSWD:\s*// && push @options,"authenticate"; 122 s/NOEXEC:\s*// && push @options,"noexec"; 123 s/EXEC:\s*// && push @options,"!noexec"; 124 s/SETENV:\s*// && push @options,"setenv"; 125 s/NOSETENV:\s*// && push @options,"!setenv"; 126 s/LOG_INPUT:\s*// && push @options,"log_input"; 127 s/NOLOG_INPUT:\s*// && push @options,"!log_input"; 128 s/LOG_OUTPUT:\s*// && push @options,"log_output"; 129 s/NOLOG_OUTPUT:\s*// && push @options,"!log_output"; 130 s/\w+://; # silently remove other directives 131 s/\s+$//; # right trim 132 } 133 134 # do the expanding 135 push @a,$ref->{$_} ? expand($ref,split /\s*,\s*/,$ref->{$_}):$_ foreach @_; 136 @a; 137} 138 139 140