File: is_able.pl

package info (click to toggle)
tct 1.07-9
  • links: PTS
  • area: main
  • in suites: woody
  • size: 1,828 kB
  • ctags: 1,128
  • sloc: perl: 9,604; ansic: 4,861; makefile: 516; sh: 77
file content (126 lines) | stat: -rw-r--r-- 2,836 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#
#  (This takes the place of the C program is_able.c, BTW.)
# 
#  is_able filename {w|g|s|S}       {r|w|B|b|s}
#      (world/group/SUID/SGID   read/write/{read&write}/{suid&write}/s[ug]id)
# 
#     The second arg of {r|w} determines whether a file is (group or world
#   depending on the first arg of {w|g}) writable/readable, or if it is
#   SUID/SGID (first arg, either s or S, respectively), and prints out a
#   short message to that effect.
# 
#  So:
#     is_able w w		# checks if world writable
#     is_able g r		# checks if group readable
#     is_able s s		# checks if SUID
#     is_able S b		# checks if world writable and SGID

package main;

require "file_mode.pl";

package is_able;

# package statics
#
%wg = ( 
	'w', 00006,
	'g', 00060,
	's', 04000,
	'S', 02000,
       );

%rwb= (
	'r', 00044,
	'w', 00022,
	'B', 00066,
	'b', 04022,
	's', 06000,
      );

$silent = 0;  # for suppressing diagnostic messages


sub main'is_able {
    local($file, $wg, $rwb) = @_;

    local ( 
	   $mode, 			# file mode
           $piece,			# 1 directory component
	   @pieces, 			# all the pieces
	   @dirs, 			# all the directories
	   $p, 				# punctuation; (*) mean writable
	   				#       due to writable parent
	   $retval,			# true if vulnerable
	   $[				# paranoia
	  );

    &usage, return undef	if @_ != 3 || $file eq '';

    &usage, return undef	unless defined $wg{$wg} && defined $rwb{$rwb};

    if (&'Mode($file) eq 'BOGUS' && $noisy) {
	warn "is_able: can't stat $file: $!\n";
	return undef;
    }

    $retval = 0;

    if ($rwb{$rwb} & $rwb{'w'}) {
	@pieces = split(m#/#, $file);
	for ($i = 1; $i <= $#pieces; $i++) {
	    push(@dirs, join('/', @pieces[0..$i]));
	}
    } else {
	@dirs = ( $file );
    } 

    for $piece ( reverse @dirs ) {

	next unless $mode = &'Mode($piece);
	next if $mode eq 'BOGUS';

	next unless $mode &= 07777 & $wg{$wg} & $rwb{$rwb};

	$retval = 1;

	$p = $piece eq $file ? '!' : '! (*)';

	$parent_is_writable = $p eq '! (*)'; # for later

	next if $silent; # for &is_writable

	print "Warning!  $file is group readable$p\n"	if $mode & 00040; 
	print "Warning!  $file is _World_ readable$p\n"	if $mode & 00004; 
	print "Warning!  $file is group writable$p\n"	if $mode & 00020; 
	print "Warning!  $file is _World_ writable$p\n"	if $mode & 00002; 
	print "Warning!  $file is SUID!\n"		if $mode & 04000; 
	print "Warning!  $file is SGID!\n"		if $mode & 02000; 

	last if $piece ne $file;  # only complain on first writable parent
    }
    $retval;
}

sub main'is_writable {
    local($silent) = 1;
    &'is_able($_[0], 'w', 'w') 
	? $parent_is_writable 
	     ? "writable (*)"
	     : "writable" 
	: 0;
} 

sub main'is_readable {
    local($silent) = 1;
    &'is_able($_[0], 'w', 'r');
}

sub usage { 
    warn <<EOF;
Usage: is_able file {w|g|S|s} {r|w|B|b|s}
 (not: is_able @_)
EOF
}

1;