Unify output by creating a seperated output module
[snf-image-creator] / image_creator / os_type / unix.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 import re
35 import sys
36
37 from image_creator.os_type import OSBase, sysprep
38
39
40 class Unix(OSBase):
41
42     sensitive_userdata = [
43         '.bash_history',
44         '.gnupg',
45         '.ssh',
46         '.mozilla',
47         '.thunderbird'
48     ]
49
50     def __init__(self, rootdev, ghandler, output):
51         super(Unix, self).__init__(rootdev, ghandler, output)
52
53         self.meta["USERS"] = " ".join(self._get_passworded_users())
54
55     def _get_passworded_users(self):
56         users = []
57         regexp = re.compile('(\S+):((?:!\S+)|(?:[^!*]\S+)|):(?:\S*:){6}')
58
59         for line in self.g.cat('/etc/shadow').splitlines():
60             match = regexp.match(line)
61             if not match:
62                 continue
63
64             user, passwd = match.groups()
65             if len(passwd) > 0 and passwd[0] == '!':
66                 warn("Ignoring locked %s account." % user)
67             else:
68                 users.append(user)
69
70         return users
71
72     @sysprep(enabled=False)
73     def remove_user_accounts(self, print_header=True):
74         """Remove all user accounts with id greater than 1000"""
75
76         if print_header:
77             self.out.output(
78                     'Removing all user accounts with id greater than 1000')
79
80         # Remove users from /etc/passwd
81         passwd = []
82         removed_users = {}
83         metadata_users = self.meta['USERS'].split()
84         for line in self.g.cat('/etc/passwd').splitlines():
85             fields = line.split(':')
86             if int(fields[2]) > 1000:
87                 removed_users[fields[0]] = fields
88                 # remove it from the USERS metadata too
89                 if fields[0] in metadata_users:
90                     metadata_users.remove(fields[0])
91             else:
92                 passwd.append(':'.join(fields))
93
94         self.meta['USERS'] = " ".join(metadata_users)
95         self.g.write('/etc/passwd', '\n'.join(passwd) + '\n')
96
97         # Remove the corresponding /etc/shadow entries
98         shadow = []
99         for line in self.g.cat('/etc/shadow').splitlines():
100             fields = line.split(':')
101             if fields[0] not in removed_users:
102                 shadow.append(':'.join(fields))
103
104         self.g.write('/etc/shadow', "\n".join(shadow) + '\n')
105
106         # Remove the corresponding /etc/group entries
107         group = []
108         for line in self.g.cat('/etc/group').splitlines():
109             fields = line.split(':')
110             # Remove groups tha have the same name as the removed users
111             if fields[0] not in removed_users:
112                 group.append(':'.join(fields))
113
114         self.g.write('/etc/group', '\n'.join(group) + '\n')
115
116         # Remove home directories
117         for home in [field[5] for field in removed_users.values()]:
118             if self.g.is_dir(home) and home.startswith('/home/'):
119                 self.g.rm_rf(home)
120
121     @sysprep()
122     def cleanup_passwords(self, print_header=True):
123         """Remove all passwords and lock all user accounts"""
124
125         if print_header:
126             self.out.output(
127                     'Cleaning up passwords & locking all user accounts')
128
129         shadow = []
130
131         for line in self.g.cat('/etc/shadow').splitlines():
132             fields = line.split(':')
133             if fields[1] not in ('*', '!'):
134                 fields[1] = '!'
135
136             shadow.append(":".join(fields))
137
138         self.g.write('/etc/shadow', "\n".join(shadow) + '\n')
139
140     @sysprep()
141     def cleanup_cache(self, print_header=True):
142         """Remove all regular files under /var/cache"""
143
144         if print_header:
145             self.out.output('Removing files under /var/cache')
146
147         self.foreach_file('/var/cache', self.g.rm, ftype='r')
148
149     @sysprep()
150     def cleanup_tmp(self, print_header=True):
151         """Remove all files under /tmp and /var/tmp"""
152
153         if print_header:
154             self.out.output('Removing files under /tmp and /var/tmp')
155
156         self.foreach_file('/tmp', self.g.rm_rf, maxdepth=1)
157         self.foreach_file('/var/tmp', self.g.rm_rf, maxdepth=1)
158
159     @sysprep()
160     def cleanup_log(self, print_header=True):
161         """Empty all files under /var/log"""
162
163         if print_header:
164             self.out.output('Emptying all files under /var/log')
165
166         self.foreach_file('/var/log', self.g.truncate, ftype='r')
167
168     @sysprep(enabled=False)
169     def cleanup_mail(self, print_header=True):
170         """Remove all files under /var/mail and /var/spool/mail"""
171
172         if print_header:
173             self.out.output('Removing files under /var/mail & /var/spool/mail')
174
175         self.foreach_file('/var/spool/mail', self.g.rm_rf, maxdepth=1)
176         self.foreach_file('/var/mail', self.g.rm_rf, maxdepth=1)
177
178     @sysprep()
179     def cleanup_userdata(self, print_header=True):
180         """Delete sensitive userdata"""
181
182         homedirs = ['/root'] + self.ls('/home/')
183
184         if print_header:
185             self.out.output('Removing sensitive user data under %s' % " ".
186                                                         join(homedirs))
187
188         for homedir in homedirs:
189             for data in self.sensitive_userdata:
190                 fname = "%s/%s" % (homedir, data)
191                 if self.g.is_file(fname):
192                     self.g.scrub_file(fname)
193
194 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :