Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: client/site_tests/platform_ProcessPrivilegesComprehensive/platform_ProcessPrivilegesComprehensive.py

Issue 3322015: Add autotest which accounts for all process privileges (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/autotest.git
Patch Set: Address seano items. Also baseline corrections. Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « client/site_tests/platform_ProcessPrivilegesComprehensive/control ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import grp
6 import json
7 import os
8 import pwd
9 import re
10 import string
11 import time
12
13 from autotest_lib.client.bin import site_login, site_ui_test, utils
14 from autotest_lib.client.common_lib import error
15
16 class platform_ProcessPrivilegesComprehensive(site_ui_test.UITest):
17 """
18 Builds a process list (without spawning 'ps'), and validates
19 the list against a baseline of expected processes, their priviliges,
20 how many we expect to find, etc.
21 """
22 version = 1
23 baseline = None
24 strict = True
25
26 def load_baseline(self):
27 # Figure out path to baseline file, by looking up our own path
28 bpath = os.path.abspath(__file__)
29 bpath = os.path.join(os.path.dirname(bpath), 'baseline')
30 bfile = open(bpath)
31 self.baseline = json.loads(bfile.read())
32 bfile.close()
33 # Initialize the 'seen' counter here, makes code below easier
34 for user in self.baseline.keys():
35 for prog in self.baseline[user].keys():
36 self.baseline[user][prog]['seen'] = 0
37
38
39 def get_procentry(self, pid):
40 """Gathers info about one process, given its PID"""
41 pid_status_file = open(os.path.join('/proc', pid, 'status'))
42 procentry = {}
43 # pull Name, Uids, and Guids out of the status output
44 for line in pid_status_file:
45 fields = re.split('\s+',line)
46 if fields[0] == 'Name:':
47 procentry['name'] = fields[1]
48 elif fields[0] == 'Uid:' or fields[0] == 'Gid:':
49 # Add dictionary items like ruid, rgid, euid, egid, etc
50 # Prefer to save uname ('root') but will save uid ('123')
51 # if no uname can be found for that id.
52 ug = fields[0][0].lower() # 'u' or 'g'
53 for i in range(1,4):
54 try:
55 if ug == 'u':
56 fields[i] = pwd.getpwuid(int(fields[i]))[0]
57 else:
58 fields[i] = grp.getgrgid(int(fields[i]))[0]
59 except KeyError:
60 # couldn't find name. We'll save bare id# instead.
61 pass
62
63 procentry['r%sid' % ug] = fields[1]
64 procentry['e%sid' % ug] = fields[2]
65 procentry['s%sid' % ug] = fields[3]
66
67 pid_status_file.close()
68 return procentry
69
70
71 def procwalk(self):
72 """Gathers info about every process on the system"""
73 for pid in os.listdir('/proc'):
74 if not pid.isdigit():
75 continue
76
77 # There can be a race where after we listdir(), a process
78 # exits. In that case get_procentry will throw an IOError
79 # becase /prod/NNNN won't exist.
80 # In those cases, skip to the next go-round of our loop.
81 try:
82 procentry = self.get_procentry(pid)
83 except IOError:
84 continue
85 procname = procentry['name']
86 procuid = procentry['euid']
87
88 # The baseline might not contain a section for this uid
89 if not procuid in self.baseline:
90 self.baseline[procuid] = {}
91
92 # For processes not explicitly mentioned in the baseline,
93 # our implicit rule depends on how strict we want our checking.
94 # In strict mode, it is an implicit "max: 0" rule (default deny)
95 # In non-strict mode, it is an implicit "min: 0" (default allow)
96 if not procname in self.baseline[procuid]:
97 if self.strict:
98 self.baseline[procuid][procname] = {'max': 0}
99 else:
100 self.baseline[procuid][procname] = {'min': 0}
101
102 # Initialize/increment a count of how many times we see
103 # this process (e.g. we may expect a min of 4 and a max of 8
104 # of some certain process, so 'seen' is not a boolean).
105 if not 'seen' in self.baseline[procuid][procname]:
106 self.baseline[procuid][procname]['seen'] = 0
107 self.baseline[procuid][procname]['seen'] += 1
108
109
110 def report(self):
111 """Return a list of problems identified during procwalk"""
112 problems = []
113 for user in self.baseline.keys():
114 for prog in self.baseline[user].keys():
115 # If there's a min, we may not have met it
116 # If there's a max, we may have exceeded it
117 if 'min' in self.baseline[user][prog]:
118 if (self.baseline[user][prog]['seen'] <
119 self.baseline[user][prog]['min']):
120 p = ('%s (run as %s): expected at least %s processes,'
121 ' saw only %s')
122 p = p % (prog, user, self.baseline[user][prog]['min'],
123 self.baseline[user][prog]['seen'])
124 problems.append(p)
125
126 if 'max' in self.baseline[user][prog]:
127 if (self.baseline[user][prog]['seen'] >
128 self.baseline[user][prog]['max']):
129 p = ('%s (run as %s): expected at most %s processes,'
130 ' saw %s')
131 p = p % (prog, user, self.baseline[user][prog]['max'],
132 self.baseline[user][prog]['seen'])
133 problems.append(p)
134 problems.sort()
135 return problems
136
137
138 def run_once(self):
139 self.load_baseline()
140 self.procwalk()
141 problems = self.report()
142
143 if (len(problems) != 0):
144 raise error.TestFail(
145 'Process list had %s mis-matches with baseline: %s%s' %
146 (len(problems), string.join(problems, '. '),
147 '(END)'))
OLDNEW
« no previous file with comments | « client/site_tests/platform_ProcessPrivilegesComprehensive/control ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698