The whitelist is now taken from a file (/etc/findwwwritable/whitelist).
[toast/findwwwritable.git] / findwwwritable.py
1 #!/usr/bin/python
2 # python 2.x is used
3
4 import os
5 import stat
6 from os.path import join
7
8
9 def collect_writable_dirs(rootdir, uids, gids):
10     """Returns a list of directories below rootdir (including rootdir) that are writable 
11     by the users with the given uids or gids or that are world writable.
12     Normally, uid(s) is the user id of the apache user (e.g. www-data) and gids is a list
13     of group ids this user is member of.
14     
15     :param rootdir: string. directory where the search should start at
16     :param uids: list of integer user ids
17     :param gids: list of integer group ids"""
18     assert isinstance(rootdir, str)
19     assert isinstance(uids, list)
20     for uid in uids: assert isinstance(uid, int)
21     assert isinstance(gids, list)
22     for gid in gids: assert isinstance(gid, int)
23
24     writable_dirs = [] # dirs with write permissions - this list is filled by this function
25
26     for root, dirs, files in os.walk(rootdir):
27         for d in dirs:
28             dp = join(root, d) # dp is the dir with path
29             s = os.lstat(dp)
30             if (s.st_mode & stat.S_IFLNK) == stat.S_IFLNK: continue # skip symlinks
31             if s.st_uid in uids and (s.st_mode & stat.S_IWUSR) > 0:
32                 writable_dirs.append(dp)
33             elif s.st_gid in gids and (s.st_mode & stat.S_IWGRP) > 0:
34                 writable_dirs.append(dp)
35             elif (s.st_mode & stat.S_IWOTH) > 0:
36                 writable_dirs.append(dp)
37
38     return writable_dirs
39
40
41
42 def summarize_dirs(writable_dirs):
43     """Takes a list of directories and omits each subdirectory if its parent directory
44     is also included in the list. The modified list is returned.
45
46     :param writable_dirs: List of directories (strings)."""
47     writable_dirs = sorted(writable_dirs)
48
49     i = 0
50     while i < len(writable_dirs)-1:
51         if writable_dirs[i+1].startswith(writable_dirs[i] + '/'):
52             del writable_dirs[i+1]
53         else:
54             i += 1
55
56     return writable_dirs
57
58
59 def read_whitelist(whitelist_filename):
60     """Reads the given whitelist (one directory name per line) and returns it as list.
61     Empty lines are omitted. Lines beginning with # are omitted as well.
62     If the file does not exist, it returns an empty list."""
63     whitelist = []
64     try: file = open(whitelist_filename, 'r')
65     except IOError: return []
66     for line in file:
67         line = line.strip()
68         if len(line) == 0: continue
69         if line[0] == '#': continue
70         whitelist.append(line)
71     file.close()
72     return sorted(set(whitelist))
73
74
75 def apply_whitelist(writable_dirs, whitelist):
76     """Removes all directories that are contained in the list whitelist from the list writable_dirs.
77     It returns the modified writable_dirs.
78
79     :param writable_dirs: List of directories
80     :param whitelist: List of directories that should be removed from writable_dirs.
81     :return: list of writable directories."""
82     return sorted(list(set(writable_dirs).difference(whitelist)))
83
84
85
86 if __name__ == '__main__':
87
88     # variables
89     uids = [33]                # user ids of the user whos write permissions should be found
90     gids = [33, 42, 121, 127]  # group ids of the user whos write permissions should be found
91     rootdir = '/home'          # directory where the seach is started
92     whitelist_filename = '/etc/findwwwritable/whitelist' # list of directories that are known to be writable
93                                                          # and that should not be reported.
94
95     # read whitelist
96     whitelist = read_whitelist(whitelist_filename)
97
98     # collect and summarize writable directories
99     writable_dirs = collect_writable_dirs(rootdir, uids, gids)
100     writable_dirs = summarize_dirs(writable_dirs)
101     writable_dirs = apply_whitelist(writable_dirs, whitelist)
102
103     # print writable directories
104     for d in writable_dirs:
105         print d
106
107