-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathchangerootpasswd.py
307 lines (248 loc) · 9.84 KB
/
changerootpasswd.py
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#!/usr/bin/env bash
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 www.jd.com
#
# File: changerootpasswd.sh
# Author: xuriwuyun <[email protected]>
# Created: 11/19/2014 15:36:21
# Last_modified: 09/18/2015 13:53:10
import six
import sys
import random
import crypt
try:
import guestfs
except Exception as e:
raise Exception( "libguestfs is not installed (%s)" % e)
forceTCG = False
def force_tcg(force=True):
"""Prevent libguestfs trying to use KVM acceleration
It is a good idea to call this if it is known that
KVM is not desired, even if technically available.
"""
global forceTCG
forceTCG = force
class VFSGuestFS(object):
"""This class implements a VFS module that uses the libguestfs APIs
to access the disk image. The disk image is never mapped into
the host filesystem, thus avoiding any potential for symlink
attacks from the guest filesystem.
"""
def __init__(self, imgfile, imgfmt='raw', partition=-1):
self.imgfile = imgfile
self.imgfmt = imgfmt
self.partition = partition
self.handle = None
def inspect_capabilities(self):
"""Determines whether guestfs is well configured."""
try:
g = guestfs.GuestFS()
g.add_drive("/dev/null") # sic
g.launch()
except Exception as e:
raise Exception("libguestfs installed but not usable (%s)" % e)
return self
def setup_os(self):
if self.partition == -1:
self.setup_os_inspect()
else:
self.setup_os_static()
def setup_os_static(self):
print "Mount guest OS image %(imgfile)s partition %(part)s"%\
{'imgfile': self.imgfile, 'part': str(self.partition)}
if self.partition:
self.handle.mount_options("", "/dev/sda%d" % self.partition, "/")
else:
self.handle.mount_options("", "/dev/sda", "/")
def setup_os_inspect(self):
print("Inspecting guest OS image %s"% self.imgfile)
roots = self.handle.inspect_os()
if len(roots) == 0:
raise Exception("No operating system found in %s" % self.imgfile)
if len(roots) != 1:
print("Multi-boot OS %(roots)s"% {'roots': str(roots)})
raise Exception("Multi-boot operating system found in %s" %
self.imgfile)
self.setup_os_root(roots[0])
def setup_os_root(self, root):
print("Inspecting guest OS root filesystem %s"% root)
mounts = self.handle.inspect_get_mountpoints(root)
if len(mounts) == 0:
raise Exception("No mount points found in %(root)s of %(imgfile)s" %
{'root': root, 'imgfile': self.imgfile})
# the root directory must be mounted first
mounts.sort(key=lambda mount: mount[0])
root_mounted = False
for mount in mounts:
print("Mounting %(dev)s at %(dir)s"%
{'dev': mount[1], 'dir': mount[0]})
try:
self.handle.mount_options("", mount[1], mount[0])
root_mounted = True
except RuntimeError as e:
msg = "Error mounting %(device)s to %(dir)s in image"\
" %(imgfile)s with libguestfs (%(e)s)" % \
{'imgfile': self.imgfile, 'device': mount[1],
'dir': mount[0], 'e': e}
if root_mounted:
print(msg)
else:
raise Exception(msg)
def setup(self):
print("Setting up appliance for %(imgfile)s %(imgfmt)s"%
{'imgfile': self.imgfile, 'imgfmt': self.imgfmt})
try:
self.handle = guestfs.GuestFS(python_return_dict=False, close_on_exit=False)
except TypeError as e:
if ('close_on_exit' in six.text_type(e) or
'python_return_dict' in six.text_type(e)):
# NOTE(russellb) In case we're not using a version of
# libguestfs new enough to support parameters close_on_exit
# and python_return_dict which were added in libguestfs 1.20.
self.handle = guestfs.GuestFS()
else:
raise
try:
self.handle.add_drive_opts(self.imgfile, format=self.imgfmt)
self.handle.launch()
self.setup_os()
self.handle.aug_init("/", 0)
except RuntimeError as e:
# explicitly teardown instead of implicit close()
# to prevent orphaned VMs in cases when an implicit
# close() is not enough
self.teardown()
raise Exception("Error mounting %(imgfile)s with libguestfs (%(e)s)" %
{'imgfile': self.imgfile, 'e': e})
except Exception:
# explicitly teardown instead of implicit close()
# to prevent orphaned VMs in cases when an implicit
# close() is not enough
self.teardown()
raise
def teardown(self):
print("Tearing down appliance")
try:
try:
self.handle.aug_close()
except RuntimeError as e:
print("Failed to close augeas %s"% e)
try:
self.handle.shutdown()
except AttributeError:
# Older libguestfs versions haven't an explicit shutdown
pass
except RuntimeError as e:
print("Failed to shutdown appliance %s"% e)
try:
self.handle.close()
except AttributeError:
# Older libguestfs versions haven't an explicit close
pass
except RuntimeError as e:
print("Failed to close guest handle %s"% e)
finally:
# dereference object and implicitly close()
self.handle = None
@staticmethod
def _canonicalize_path(path):
if path[0] != '/':
return '/' + path
return path
def make_path(self, path):
print("Make directory path=%s"% path)
path = self._canonicalize_path(path)
self.handle.mkdir_p(path)
def command(self, cmd):
cmd = cmd.split()
print 'cmd: ', cmd
self.handle.command(cmd)
def append_file(self, path, content):
print("Append file path=%s"% path)
path = self._canonicalize_path(path)
self.handle.write_append(path, content)
def replace_file(self, path, content):
print("Replace file path=%s"% path)
path = self._canonicalize_path(path)
self.handle.write(path, content)
def read_file(self, path):
print("Read file path=%s"% path)
path = self._canonicalize_path(path)
return self.handle.read_file(path)
def has_file(self, path):
print("Has file path=%s"% path)
path = self._canonicalize_path(path)
try:
self.handle.stat(path)
return True
except RuntimeError:
return False
def set_permissions(self, path, mode):
print("Set permissions path=%(path)s mode=%(mode)s"%
{'path': path, 'mode': mode})
path = self._canonicalize_path(path)
self.handle.chmod(mode, path)
def set_ownership(self, path, user, group):
print("Set ownership path=%(path)s "
"user=%(user)s group=%(group)s"%
{'path': path, 'user': user, 'group': group})
path = self._canonicalize_path(path)
uid = -1
gid = -1
if user is not None:
uid = int(self.handle.aug_get(
"/files/etc/passwd/" + user + "/uid"))
if group is not None:
gid = int(self.handle.aug_get(
"/files/etc/group/" + group + "/gid"))
print("chown uid=%(uid)d gid=%(gid)s"% {'uid': uid, 'gid': gid})
self.handle.chown(uid, gid, path)
def _generate_salt():
salt_set = ('abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'0123456789./')
salt = 16 * ' '
return ''.join([random.choice(salt_set) for c in salt])
def encrypted_passwd(admin_passwd):
algos = {'SHA-512': '$6$', 'SHA-256': '$5$', 'MD5': '$1$', 'DES': ''}
salt = _generate_salt()
# crypt() depends on the underlying libc, and may not support all
# forms of hash. We try md5 first. If we get only 13 characters back,
# then the underlying crypt() didn't understand the '$n$salt' magic,
# so we fall back to DES.
# md5 is the default because it's widely supported. Although the
# local crypt() might support stronger SHA, the target instance
# might not.
encrypted_passwd = crypt.crypt(admin_passwd, algos['MD5'] + salt)
if len(encrypted_passwd) == 13:
encrypted_passwd = crypt.crypt(admin_passwd, algos['DES'] + salt)
return encrypted_passwd
if __name__ == "__main__":
if len(sys.argv) != 4:
print "USAGE: changerootpasswd.py image_file image_format password"
exit(1)
image_file = sys.argv[1]
image_format = sys.argv[2]
password = sys.argv[3]
guest = VFSGuestFS(image_file, imgfmt=image_format)
guest.setup()
shadow_file = "/etc/shadow"
shadow_data = guest.read_file(shadow_file)
print shadow_data
s_file = shadow_data.split("\n")
passwd_encrypted = encrypted_passwd(password)
command_str = r"sed -i -r /^root/s#root:([^:]+):(.*)#root:" + \
passwd_encrypted + r":\2# " + shadow_file
guest.command(command_str)
# new_s_file = []
# for entry in s_file:
# split_entry = entry.split(":")
# if split_entry[0] == "root":
# split_entry[1] = encrypted_passwd(password)
# new_s_file.append(':'.join(split_entry))
# new_shadow_data = '\n'.join(new_s_file)
new_shadow_data = guest.read_file(shadow_file)
print new_shadow_data
#guest.replace_file(shadow_file, new_shadow_data)
guest.teardown()