blob: f2ef939fab5468a32b36696bbc9a4097b83e9dc9 [file] [log] [blame]
Frank Farzand5e36312012-01-13 14:34:03 -08001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Masone5e06f182010-03-23 08:29:52 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Will Drewry9e440792013-12-11 17:18:35 -06005import dbus, gobject, logging, os, random, re, shutil, string
6from dbus.mainloop.glib import DBusGMainLoop
barfab@chromium.orgb6d29932012-04-11 09:46:43 +02007
8import common, constants
barfab@chromium.org5c374632012-04-05 16:50:56 +02009from autotest_lib.client.bin import utils
Chris Masone5e06f182010-03-23 08:29:52 -070010from autotest_lib.client.common_lib import error
Will Drewry9e440792013-12-11 17:18:35 -060011from autotest_lib.client.cros.cros_disks import DBusClient
Eric Lic4d8f4a2010-12-10 09:49:23 -080012
Sean Oe5d8fd02010-09-30 10:44:44 +020013CRYPTOHOME_CMD = '/usr/sbin/cryptohome'
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -040014GUEST_USER_NAME = '$guest'
Sean Oe5d8fd02010-09-30 10:44:44 +020015
Chris Masone5d010aa2013-05-06 14:38:42 -070016class ChromiumOSError(error.TestError):
Sean Oe5d8fd02010-09-30 10:44:44 +020017 """Generic error for ChromiumOS-specific exceptions."""
18 pass
19
Sean Oe5d8fd02010-09-30 10:44:44 +020020def __run_cmd(cmd):
21 return utils.system_output(cmd + ' 2>&1', retain_output=True,
22 ignore_status=True).strip()
23
Sean Oe5d8fd02010-09-30 10:44:44 +020024def get_user_hash(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +020025 """Get the user hash for the given user."""
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -040026 return utils.system_output(['cryptohome', '--action=obfuscate_user',
27 '--user=%s' % user])
Sean Oe5d8fd02010-09-30 10:44:44 +020028
29
barfab@chromium.org5c374632012-04-05 16:50:56 +020030def user_path(user):
31 """Get the user mount point for the given user."""
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -040032 return utils.system_output(['cryptohome-path', 'user', user])
barfab@chromium.org5c374632012-04-05 16:50:56 +020033
34
35def system_path(user):
36 """Get the system mount point for the given user."""
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -040037 return utils.system_output(['cryptohome-path', 'system', user])
barfab@chromium.org5c374632012-04-05 16:50:56 +020038
39
Chris Masone5d010aa2013-05-06 14:38:42 -070040def ensure_clean_cryptohome_for(user, password=None):
41 """Ensure a fresh cryptohome exists for user.
42
43 @param user: user who needs a shiny new cryptohome.
44 @param password: if unset, a random password will be used.
45 """
46 if not password:
47 password = ''.join(random.sample(string.ascii_lowercase, 6))
48 remove_vault(user)
49 mount_vault(user, password, create=True)
50
51
Frank Farzand5e36312012-01-13 14:34:03 -080052def get_tpm_status():
53 """Get the TPM status.
54
55 Returns:
56 A TPM status dictionary, for example:
57 { 'Enabled': True,
58 'Owned': True,
59 'Being Owned': False,
60 'Ready': True,
61 'Password': ''
62 }
63 """
64 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status')
65 status = {}
66 for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']:
67 match = re.search('TPM %s: (true|false)' % field, out)
68 if not match:
69 raise ChromiumOSError('Invalid TPM status: "%s".' % out)
70 status[field] = match.group(1) == 'true'
71 match = re.search('TPM Password: (\w*)', out)
72 status['Password'] = ''
73 if match:
74 status['Password'] = match.group(1)
75 return status
76
77
David Pursell2a2ef342014-10-17 10:34:56 -070078def get_login_status():
79 """Query the login status
80
81 Returns:
82 A login status dictionary containing:
83 { 'owner_user_exists': True|False,
84 'boot_lockbox_finalized': True|False
85 }
86 """
87 out = __run_cmd(CRYPTOHOME_CMD + ' --action=get_login_status')
88 status = {}
89 for field in ['owner_user_exists', 'boot_lockbox_finalized']:
90 match = re.search('%s: (true|false)' % field, out)
91 if not match:
92 raise ChromiumOSError('Invalid login status: "%s".' % out)
93 status[field] = match.group(1) == 'true'
94 return status
95
96
Darren Krahn5f880f62012-10-02 15:17:59 -070097def get_tpm_attestation_status():
98 """Get the TPM attestation status. Works similar to get_tpm_status().
99 """
100 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_attestation_status')
101 status = {}
102 for field in ['Prepared', 'Enrolled']:
103 match = re.search('Attestation %s: (true|false)' % field, out)
104 if not match:
105 raise ChromiumOSError('Invalid attestation status: "%s".' % out)
106 status[field] = match.group(1) == 'true'
107 return status
108
109
Frank Farzand5e36312012-01-13 14:34:03 -0800110def take_tpm_ownership():
111 """Take TPM owernship.
112
113 Blocks until TPM is owned.
114 """
115 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_take_ownership')
116 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_wait_ownership')
117
118
Darren Krahn0e73e7f2012-09-05 15:35:15 -0700119def verify_ek():
120 """Verify the TPM endorsement key.
121
122 Returns true if EK is valid.
123 """
124 cmd = CRYPTOHOME_CMD + ' --action=tpm_verify_ek'
125 return (utils.system(cmd, ignore_status=True) == 0)
126
127
Sean Oe5d8fd02010-09-30 10:44:44 +0200128def remove_vault(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200129 """Remove the given user's vault from the shadow directory."""
Sean Oe5d8fd02010-09-30 10:44:44 +0200130 logging.debug('user is %s', user)
131 user_hash = get_user_hash(user)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200132 logging.debug('Removing vault for user %s with hash %s' % (user, user_hash))
Sean Oe5d8fd02010-09-30 10:44:44 +0200133 cmd = CRYPTOHOME_CMD + ' --action=remove --force --user=%s' % user
134 __run_cmd(cmd)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200135 # Ensure that the vault does not exist.
136 if os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash)):
Darren Krahne6c44b92014-03-31 12:11:08 -0700137 raise ChromiumOSError('Cryptohome could not remove the user\'s vault.')
Sean Oe5d8fd02010-09-30 10:44:44 +0200138
139
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200140def remove_all_vaults():
141 """Remove any existing vaults from the shadow directory.
142
143 This function must be run with root privileges.
144 """
barfab@chromium.org5c374632012-04-05 16:50:56 +0200145 for item in os.listdir(constants.SHADOW_ROOT):
146 abs_item = os.path.join(constants.SHADOW_ROOT, item)
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200147 if os.path.isdir(os.path.join(abs_item, 'vault')):
148 logging.debug('Removing vault for user with hash %s' % item)
149 shutil.rmtree(abs_item)
150
151
Sean Oe5d8fd02010-09-30 10:44:44 +0200152def mount_vault(user, password, create=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200153 """Mount the given user's vault."""
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400154 args = [CRYPTOHOME_CMD, '--action=mount', '--user=%s' % user,
Chris Masone3543e512013-11-04 13:09:30 -0800155 '--password=%s' % password, '--async']
Sean Oe5d8fd02010-09-30 10:44:44 +0200156 if create:
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400157 args.append('--create')
Chris Masone3543e512013-11-04 13:09:30 -0800158 logging.info(__run_cmd(' '.join(args)))
barfab@chromium.org5c374632012-04-05 16:50:56 +0200159 # Ensure that the vault exists in the shadow directory.
Sean Oe5d8fd02010-09-30 10:44:44 +0200160 user_hash = get_user_hash(user)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200161 if not os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash)):
Sean Oe5d8fd02010-09-30 10:44:44 +0200162 raise ChromiumOSError('Cryptohome vault not found after mount.')
barfab@chromium.org5c374632012-04-05 16:50:56 +0200163 # Ensure that the vault is mounted.
164 if not is_vault_mounted(
165 user=user,
166 device_regex=constants.CRYPTOHOME_DEV_REGEX_REGULAR_USER,
167 allow_fail=True):
168 raise ChromiumOSError('Cryptohome created a vault but did not mount.')
Sean Oe5d8fd02010-09-30 10:44:44 +0200169
170
Chris Masone5d010aa2013-05-06 14:38:42 -0700171def mount_guest():
172 """Mount the given user's vault."""
Chris Masone3543e512013-11-04 13:09:30 -0800173 args = [CRYPTOHOME_CMD, '--action=mount_guest', '--async']
174 logging.info(__run_cmd(' '.join(args)))
Chris Masone5d010aa2013-05-06 14:38:42 -0700175 # Ensure that the guest tmpfs is mounted.
176 if not is_guest_vault_mounted(allow_fail=True):
177 raise ChromiumOSError('Cryptohome did not mount tmpfs.')
178
179
Sean Oe5d8fd02010-09-30 10:44:44 +0200180def test_auth(user, password):
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400181 cmd = [CRYPTOHOME_CMD, '--action=test_auth', '--user=%s' % user,
182 '--password=%s' % password, '--async']
183 return 'Authentication succeeded' in utils.system_output(cmd)
Sean Oe5d8fd02010-09-30 10:44:44 +0200184
185
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400186def unmount_vault(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200187 """Unmount the given user's vault.
188
189 Once unmounting for a specific user is supported, the user parameter will
190 name the target user. See crosbug.com/20778.
Elly Jones686c2f42011-10-24 16:45:07 -0400191 """
Chris Masone3543e512013-11-04 13:09:30 -0800192 __run_cmd(CRYPTOHOME_CMD + ' --action=unmount')
barfab@chromium.org5c374632012-04-05 16:50:56 +0200193 # Ensure that the vault is not mounted.
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400194 if is_vault_mounted(user, allow_fail=True):
Sean Oe5d8fd02010-09-30 10:44:44 +0200195 raise ChromiumOSError('Cryptohome did not unmount the user.')
196
197
barfab@chromium.org5c374632012-04-05 16:50:56 +0200198def __get_mount_info(mount_point, allow_fail=False):
199 """Get information about the active mount at a given mount point."""
beeps569f8672013-08-07 10:18:51 -0700200 cryptohomed_path = '/proc/$(pgrep cryptohomed)/mounts'
201 try:
202 logging.info(utils.system_output('cat %s' % cryptohomed_path))
203 mount_line = utils.system_output(
204 'grep %s %s' % (mount_point, cryptohomed_path),
205 ignore_status=allow_fail)
206 except Exception as e:
207 logging.error(e)
208 raise ChromiumOSError('Could not get info about cryptohome vault '
209 'through %s. See logs for complete mount-point.'
210 % os.path.dirname(str(mount_point)))
Sourav Poddar574bd622010-05-26 14:22:26 +0530211 return mount_line.split()
212
213
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400214def __get_user_mount_info(user, allow_fail=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200215 """Get information about the active mounts for a given user.
216
217 Returns the active mounts at the user's user and system mount points. If no
218 user is given, the active mount at the shared mount point is returned
219 (regular users have a bind-mount at this mount point for backwards
220 compatibility; the guest user has a mount at this mount point only).
221 """
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400222 return [__get_mount_info(mount_point=user_path(user),
223 allow_fail=allow_fail),
224 __get_mount_info(mount_point=system_path(user),
225 allow_fail=allow_fail)]
Jim Hebertf08f88d2011-04-22 10:33:49 -0700226
barfab@chromium.org5c374632012-04-05 16:50:56 +0200227def is_vault_mounted(
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400228 user,
barfab@chromium.org5c374632012-04-05 16:50:56 +0200229 device_regex=constants.CRYPTOHOME_DEV_REGEX_ANY,
230 fs_regex=constants.CRYPTOHOME_FS_REGEX_ANY,
231 allow_fail=False):
232 """Check whether a vault is mounted for the given user.
233
234 If no user is given, the shared mount point is checked, determining whether
235 a vault is mounted for any user.
236 """
237 user_mount_info = __get_user_mount_info(user=user, allow_fail=allow_fail)
238 for mount_info in user_mount_info:
239 if (len(mount_info) < 3 or
240 not re.match(device_regex, mount_info[0]) or
241 not re.match(fs_regex, mount_info[2])):
242 return False
243 return True
Sourav Poddar574bd622010-05-26 14:22:26 +0530244
245
barfab@chromium.org5c374632012-04-05 16:50:56 +0200246def is_guest_vault_mounted(allow_fail=False):
247 """Check whether a vault backed by tmpfs is mounted for the guest user."""
248 return is_vault_mounted(
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400249 user=GUEST_USER_NAME,
barfab@chromium.org5c374632012-04-05 16:50:56 +0200250 device_regex=constants.CRYPTOHOME_DEV_REGEX_GUEST,
251 fs_regex=constants.CRYPTOHOME_FS_REGEX_TMPFS,
252 allow_fail=allow_fail)
253
254
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400255def get_mounted_vault_devices(user, allow_fail=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200256 """Get the device(s) backing the vault mounted for the given user.
257
258 Returns the devices mounted at the user's user and system mount points. If
259 no user is given, the device mounted at the shared mount point is returned.
260 """
261 return [mount_info[0]
262 for mount_info
263 in __get_user_mount_info(user=user, allow_fail=allow_fail)
264 if len(mount_info)]
Nirnimesh66814492011-06-27 18:00:33 -0700265
266
267def canonicalize(credential):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200268 """Perform basic canonicalization of |email_address|.
Nirnimesh66814492011-06-27 18:00:33 -0700269
barfab@chromium.org5c374632012-04-05 16:50:56 +0200270 Perform basic canonicalization of |email_address|, taking into account that
271 gmail does not consider '.' or caps inside a username to matter. It also
272 ignores everything after a '+'. For example,
273 c.masone+abc@gmail.com == cMaSone@gmail.com, per
Nirnimesh66814492011-06-27 18:00:33 -0700274 http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313
275 """
276 if not credential:
277 return None
278
279 parts = credential.split('@')
280 if len(parts) != 2:
barfab@chromium.org5c374632012-04-05 16:50:56 +0200281 raise error.TestError('Malformed email: ' + credential)
Nirnimesh66814492011-06-27 18:00:33 -0700282
283 (name, domain) = parts
284 name = name.partition('+')[0]
barfab@chromium.org5c374632012-04-05 16:50:56 +0200285 if (domain == constants.SPECIAL_CASE_DOMAIN):
Nirnimesh66814492011-06-27 18:00:33 -0700286 name = name.replace('.', '')
287 return '@'.join([name, domain]).lower()
Elly Jones686c2f42011-10-24 16:45:07 -0400288
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200289
Will Drewryd2fed972013-12-05 16:35:51 -0600290def crash_cryptohomed():
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600291 # Try to kill cryptohomed so we get something to work with.
292 pid = __run_cmd('pgrep cryptohomed')
293 try:
Will Drewry9e440792013-12-11 17:18:35 -0600294 pid = int(pid)
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600295 except ValueError, e: # empty or invalid string
Will Drewry9e440792013-12-11 17:18:35 -0600296 raise error.TestError('Cryptohomed was not running')
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600297 utils.system('kill -ABRT %d' % pid)
298 # CONT just in case cryptohomed had a spurious STOP.
299 utils.system('kill -CONT %d' % pid)
300 utils.poll_for_condition(
301 lambda: utils.system('ps -p %d' % pid,
302 ignore_status=True) != 0,
Will Drewry934d1532014-01-30 16:23:17 -0600303 timeout=180,
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600304 exception=error.TestError(
305 'Timeout waiting for cryptohomed to coredump'))
306
Will Drewryd2fed972013-12-05 16:35:51 -0600307
Will Drewry9e440792013-12-11 17:18:35 -0600308class CryptohomeProxy(DBusClient):
309 """A DBus proxy client for testing the Cryptohome DBus server.
310 """
311 CRYPTOHOME_BUS_NAME = 'org.chromium.Cryptohome'
312 CRYPTOHOME_OBJECT_PATH = '/org/chromium/Cryptohome'
313 CRYPTOHOME_INTERFACE = 'org.chromium.CryptohomeInterface'
314 ASYNC_CALL_STATUS_SIGNAL = 'AsyncCallStatus'
315 ASYNC_CALL_STATUS_SIGNAL_ARGUMENTS = (
316 'async_id', 'return_status', 'return_code'
317 )
318 DBUS_PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties'
319
Chris Masone19e305e2014-03-14 15:13:46 -0700320
Chris Masone64170f82014-03-14 15:47:05 -0700321 def __init__(self, bus_loop=None):
Will Drewry9e440792013-12-11 17:18:35 -0600322 self.main_loop = gobject.MainLoop()
Will Drewry78db9dc2014-04-01 16:34:23 -0500323 if bus_loop is None:
Chris Masone64170f82014-03-14 15:47:05 -0700324 bus_loop = DBusGMainLoop(set_as_default=True)
Will Drewry9e440792013-12-11 17:18:35 -0600325 self.bus = dbus.SystemBus(mainloop=bus_loop)
326 super(CryptohomeProxy, self).__init__(self.main_loop, self.bus,
327 self.CRYPTOHOME_BUS_NAME,
328 self.CRYPTOHOME_OBJECT_PATH)
329 self.iface = dbus.Interface(self.proxy_object,
330 self.CRYPTOHOME_INTERFACE)
331 self.properties = dbus.Interface(self.proxy_object,
332 self.DBUS_PROPERTIES_INTERFACE)
333 self.handle_signal(self.CRYPTOHOME_INTERFACE,
334 self.ASYNC_CALL_STATUS_SIGNAL,
335 self.ASYNC_CALL_STATUS_SIGNAL_ARGUMENTS)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400336
Chris Masone19e305e2014-03-14 15:13:46 -0700337
Will Drewryd2fed972013-12-05 16:35:51 -0600338 # Wrap all proxied calls to catch cryptohomed failures.
339 def __call(self, method, *args):
340 try:
Chris Masonef59d9df2014-03-14 12:05:32 -0700341 return method(*args, timeout=180)
Will Drewryd2fed972013-12-05 16:35:51 -0600342 except dbus.exceptions.DBusException, e:
343 if e.get_dbus_name() == 'org.freedesktop.DBus.Error.NoReply':
344 logging.error('Cryptohome is not responding. Sending ABRT')
345 crash_cryptohomed()
346 raise ChromiumOSError('cryptohomed aborted. Check crashes!')
347 raise e
348
Chris Masone19e305e2014-03-14 15:13:46 -0700349
Will Drewry9e440792013-12-11 17:18:35 -0600350 def __wait_for_specific_signal(self, signal, data):
351 """Wait for the |signal| with matching |data|
352 Returns the resulting dict on success or {} on error.
353 """
Will Drewryc4de5ff2014-02-03 13:26:57 -0600354 # Do not bubble up the timeout here, just return {}.
355 result = {}
356 try:
357 result = self.wait_for_signal(signal)
358 except utils.TimeoutError:
359 return {}
Will Drewry9e440792013-12-11 17:18:35 -0600360 for k in data.keys():
361 if not result.has_key(k) or result[k] != data[k]:
362 return {}
363 return result
364
Chris Masone19e305e2014-03-14 15:13:46 -0700365
Will Drewry9e440792013-12-11 17:18:35 -0600366 # Perform a data-less async call.
367 # TODO(wad) Add __async_data_call.
368 def __async_call(self, method, *args):
Will Drewryfef135a2014-05-23 16:02:14 -0500369 # Clear out any superfluous async call signals.
370 self.clear_signal_content(self.ASYNC_CALL_STATUS_SIGNAL)
Will Drewry9e440792013-12-11 17:18:35 -0600371 out = self.__call(method, *args)
372 logging.debug('Issued call ' + str(method) +
373 ' with async_id ' + str(out))
374 result = {}
375 try:
Will Drewry934d1532014-01-30 16:23:17 -0600376 # __wait_for_specific_signal has a 10s timeout
Will Drewry9e440792013-12-11 17:18:35 -0600377 result = utils.poll_for_condition(
378 lambda: self.__wait_for_specific_signal(
379 self.ASYNC_CALL_STATUS_SIGNAL, {'async_id' : out}),
Will Drewry934d1532014-01-30 16:23:17 -0600380 timeout=180,
Will Drewry9e440792013-12-11 17:18:35 -0600381 desc='matching %s signal' % self.ASYNC_CALL_STATUS_SIGNAL)
382 except utils.TimeoutError, e:
383 logging.error('Cryptohome timed out. Sending ABRT.')
384 crash_cryptohomed()
385 raise ChromiumOSError('cryptohomed aborted. Check crashes!')
386 return result
387
Chris Masone19e305e2014-03-14 15:13:46 -0700388
Will Drewry9e440792013-12-11 17:18:35 -0600389 def mount(self, user, password, create=False, async=True):
Elly Jones2f0ebba2011-10-27 13:43:20 -0400390 """Mounts a cryptohome.
391
392 Returns True if the mount succeeds or False otherwise.
393 TODO(ellyjones): Migrate mount_vault() to use a multi-user-safe
394 heuristic, then remove this method. See <crosbug.com/20778>.
395 """
Will Drewry9e440792013-12-11 17:18:35 -0600396 if async:
397 return self.__async_call(self.iface.AsyncMount, user, password,
398 create, False, [])['return_status']
Will Drewryd2fed972013-12-05 16:35:51 -0600399 out = self.__call(self.iface.Mount, user, password, create, False, [])
Will Drewry9e440792013-12-11 17:18:35 -0600400 # Sync returns (return code, return status)
401 return out[1] if len(out) > 1 else False
Elly Jones2f0ebba2011-10-27 13:43:20 -0400402
Chris Masone19e305e2014-03-14 15:13:46 -0700403
Elly Jones2f0ebba2011-10-27 13:43:20 -0400404 def unmount(self, user):
405 """Unmounts a cryptohome.
406
407 Returns True if the unmount suceeds or false otherwise.
408 TODO(ellyjones): Once there's a per-user unmount method, use it. See
409 <crosbug.com/20778>.
410 """
Will Drewryd2fed972013-12-05 16:35:51 -0600411 return self.__call(self.iface.Unmount)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400412
Chris Masone19e305e2014-03-14 15:13:46 -0700413
Elly Jones2f0ebba2011-10-27 13:43:20 -0400414 def is_mounted(self, user):
415 """Tests whether a user's cryptohome is mounted."""
416 return (utils.is_mountpoint(user_path(user))
417 and utils.is_mountpoint(system_path(user)))
418
Chris Masone19e305e2014-03-14 15:13:46 -0700419
Elly Jones2f0ebba2011-10-27 13:43:20 -0400420 def require_mounted(self, user):
421 """Raises a test failure if a user's cryptohome is not mounted."""
422 utils.require_mountpoint(user_path(user))
423 utils.require_mountpoint(system_path(user))
Elly Jones4458f442012-04-16 15:42:56 -0400424
Chris Masone19e305e2014-03-14 15:13:46 -0700425
Will Drewry9e440792013-12-11 17:18:35 -0600426 def migrate(self, user, oldkey, newkey, async=True):
Elly Jones4458f442012-04-16 15:42:56 -0400427 """Migrates the specified user's cryptohome from one key to another."""
Will Drewry9e440792013-12-11 17:18:35 -0600428 if async:
429 return self.__async_call(self.iface.AsyncMigrateKey,
430 user, oldkey, newkey)['return_status']
Will Drewryd2fed972013-12-05 16:35:51 -0600431 return self.__call(self.iface.MigrateKey, user, oldkey, newkey)
Elly Jones4458f442012-04-16 15:42:56 -0400432
Chris Masone19e305e2014-03-14 15:13:46 -0700433
Will Drewry9e440792013-12-11 17:18:35 -0600434 def remove(self, user, async=True):
435 if async:
436 return self.__async_call(self.iface.AsyncRemove,
437 user)['return_status']
Will Drewryd2fed972013-12-05 16:35:51 -0600438 return self.__call(self.iface.Remove, user)
Chris Masone19e305e2014-03-14 15:13:46 -0700439
440
441 def ensure_clean_cryptohome_for(self, user, password=None):
442 """Ensure a fresh cryptohome exists for user.
443
444 @param user: user who needs a shiny new cryptohome.
445 @param password: if unset, a random password will be used.
446 """
447 if not password:
448 password = ''.join(random.sample(string.ascii_lowercase, 6))
449 self.remove(user)
450 self.mount(user, password, create=True)