blob: df8e1aefcacd8f1cb61c2fe55452d26e32e1bf2d [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
Kris Rambish82ee1c02014-12-10 17:02:39 -080078def get_tpm_more_status():
79 """Get more of the TPM status.
80
81 Returns:
82 A TPM more status dictionary, for example:
83 { 'dictionary_attack_lockout_in_effect': False,
84 'attestation_prepared': False,
85 'boot_lockbox_finalized': False,
86 'enabled': True,
87 'owned': True,
88 'dictionary_attack_counter': 0,
89 'dictionary_attack_lockout_seconds_remaining': 0,
90 'dictionary_attack_threshold': 10,
91 'attestation_enrolled': False,
92 'initialized': True,
93 'verified_boot_measured': False,
94 'install_lockbox_finalized': True
95 }
96 """
97 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_more_status | grep :')
98 status = {}
99 for line in out.splitlines():
100 items = line.strip().split(':')
101 if items[1].strip() == 'false':
102 value = False
103 elif items[1].strip() == 'true':
104 value = True
105 else:
106 value = int(items[1].strip())
107 status[items[0]] = value
108 return status
109
110
111def is_tpm_lockout_in_effect():
112 """Returns true if the TPM lockout is in effect; false otherwise."""
113 status = get_tpm_more_status()
114 return status['dictionary_attack_lockout_in_effect']
115
116
David Pursell2a2ef342014-10-17 10:34:56 -0700117def get_login_status():
118 """Query the login status
119
120 Returns:
121 A login status dictionary containing:
122 { 'owner_user_exists': True|False,
123 'boot_lockbox_finalized': True|False
124 }
125 """
126 out = __run_cmd(CRYPTOHOME_CMD + ' --action=get_login_status')
127 status = {}
128 for field in ['owner_user_exists', 'boot_lockbox_finalized']:
129 match = re.search('%s: (true|false)' % field, out)
130 if not match:
131 raise ChromiumOSError('Invalid login status: "%s".' % out)
132 status[field] = match.group(1) == 'true'
133 return status
134
135
Darren Krahn5f880f62012-10-02 15:17:59 -0700136def get_tpm_attestation_status():
137 """Get the TPM attestation status. Works similar to get_tpm_status().
138 """
139 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_attestation_status')
140 status = {}
141 for field in ['Prepared', 'Enrolled']:
142 match = re.search('Attestation %s: (true|false)' % field, out)
143 if not match:
144 raise ChromiumOSError('Invalid attestation status: "%s".' % out)
145 status[field] = match.group(1) == 'true'
146 return status
147
148
Frank Farzand5e36312012-01-13 14:34:03 -0800149def take_tpm_ownership():
150 """Take TPM owernship.
151
152 Blocks until TPM is owned.
153 """
154 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_take_ownership')
155 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_wait_ownership')
156
157
Darren Krahn0e73e7f2012-09-05 15:35:15 -0700158def verify_ek():
159 """Verify the TPM endorsement key.
160
161 Returns true if EK is valid.
162 """
163 cmd = CRYPTOHOME_CMD + ' --action=tpm_verify_ek'
164 return (utils.system(cmd, ignore_status=True) == 0)
165
166
Sean Oe5d8fd02010-09-30 10:44:44 +0200167def remove_vault(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200168 """Remove the given user's vault from the shadow directory."""
Sean Oe5d8fd02010-09-30 10:44:44 +0200169 logging.debug('user is %s', user)
170 user_hash = get_user_hash(user)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200171 logging.debug('Removing vault for user %s with hash %s' % (user, user_hash))
Sean Oe5d8fd02010-09-30 10:44:44 +0200172 cmd = CRYPTOHOME_CMD + ' --action=remove --force --user=%s' % user
173 __run_cmd(cmd)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200174 # Ensure that the vault does not exist.
175 if os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash)):
Darren Krahne6c44b92014-03-31 12:11:08 -0700176 raise ChromiumOSError('Cryptohome could not remove the user\'s vault.')
Sean Oe5d8fd02010-09-30 10:44:44 +0200177
178
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200179def remove_all_vaults():
180 """Remove any existing vaults from the shadow directory.
181
182 This function must be run with root privileges.
183 """
barfab@chromium.org5c374632012-04-05 16:50:56 +0200184 for item in os.listdir(constants.SHADOW_ROOT):
185 abs_item = os.path.join(constants.SHADOW_ROOT, item)
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200186 if os.path.isdir(os.path.join(abs_item, 'vault')):
187 logging.debug('Removing vault for user with hash %s' % item)
188 shutil.rmtree(abs_item)
189
190
Sean Oe5d8fd02010-09-30 10:44:44 +0200191def mount_vault(user, password, create=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200192 """Mount the given user's vault."""
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400193 args = [CRYPTOHOME_CMD, '--action=mount', '--user=%s' % user,
Chris Masone3543e512013-11-04 13:09:30 -0800194 '--password=%s' % password, '--async']
Sean Oe5d8fd02010-09-30 10:44:44 +0200195 if create:
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400196 args.append('--create')
Chris Masone3543e512013-11-04 13:09:30 -0800197 logging.info(__run_cmd(' '.join(args)))
barfab@chromium.org5c374632012-04-05 16:50:56 +0200198 # Ensure that the vault exists in the shadow directory.
Sean Oe5d8fd02010-09-30 10:44:44 +0200199 user_hash = get_user_hash(user)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200200 if not os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash)):
Sean Oe5d8fd02010-09-30 10:44:44 +0200201 raise ChromiumOSError('Cryptohome vault not found after mount.')
barfab@chromium.org5c374632012-04-05 16:50:56 +0200202 # Ensure that the vault is mounted.
203 if not is_vault_mounted(
204 user=user,
205 device_regex=constants.CRYPTOHOME_DEV_REGEX_REGULAR_USER,
206 allow_fail=True):
207 raise ChromiumOSError('Cryptohome created a vault but did not mount.')
Sean Oe5d8fd02010-09-30 10:44:44 +0200208
209
Chris Masone5d010aa2013-05-06 14:38:42 -0700210def mount_guest():
211 """Mount the given user's vault."""
Chris Masone3543e512013-11-04 13:09:30 -0800212 args = [CRYPTOHOME_CMD, '--action=mount_guest', '--async']
213 logging.info(__run_cmd(' '.join(args)))
Chris Masone5d010aa2013-05-06 14:38:42 -0700214 # Ensure that the guest tmpfs is mounted.
215 if not is_guest_vault_mounted(allow_fail=True):
216 raise ChromiumOSError('Cryptohome did not mount tmpfs.')
217
218
Sean Oe5d8fd02010-09-30 10:44:44 +0200219def test_auth(user, password):
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400220 cmd = [CRYPTOHOME_CMD, '--action=test_auth', '--user=%s' % user,
221 '--password=%s' % password, '--async']
222 return 'Authentication succeeded' in utils.system_output(cmd)
Sean Oe5d8fd02010-09-30 10:44:44 +0200223
224
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400225def unmount_vault(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200226 """Unmount the given user's vault.
227
228 Once unmounting for a specific user is supported, the user parameter will
229 name the target user. See crosbug.com/20778.
Elly Jones686c2f42011-10-24 16:45:07 -0400230 """
Chris Masone3543e512013-11-04 13:09:30 -0800231 __run_cmd(CRYPTOHOME_CMD + ' --action=unmount')
barfab@chromium.org5c374632012-04-05 16:50:56 +0200232 # Ensure that the vault is not mounted.
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400233 if is_vault_mounted(user, allow_fail=True):
Sean Oe5d8fd02010-09-30 10:44:44 +0200234 raise ChromiumOSError('Cryptohome did not unmount the user.')
235
236
barfab@chromium.org5c374632012-04-05 16:50:56 +0200237def __get_mount_info(mount_point, allow_fail=False):
238 """Get information about the active mount at a given mount point."""
beeps569f8672013-08-07 10:18:51 -0700239 cryptohomed_path = '/proc/$(pgrep cryptohomed)/mounts'
240 try:
241 logging.info(utils.system_output('cat %s' % cryptohomed_path))
242 mount_line = utils.system_output(
243 'grep %s %s' % (mount_point, cryptohomed_path),
244 ignore_status=allow_fail)
245 except Exception as e:
246 logging.error(e)
247 raise ChromiumOSError('Could not get info about cryptohome vault '
248 'through %s. See logs for complete mount-point.'
249 % os.path.dirname(str(mount_point)))
Sourav Poddar574bd622010-05-26 14:22:26 +0530250 return mount_line.split()
251
252
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400253def __get_user_mount_info(user, allow_fail=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200254 """Get information about the active mounts for a given user.
255
256 Returns the active mounts at the user's user and system mount points. If no
257 user is given, the active mount at the shared mount point is returned
258 (regular users have a bind-mount at this mount point for backwards
259 compatibility; the guest user has a mount at this mount point only).
260 """
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400261 return [__get_mount_info(mount_point=user_path(user),
262 allow_fail=allow_fail),
263 __get_mount_info(mount_point=system_path(user),
264 allow_fail=allow_fail)]
Jim Hebertf08f88d2011-04-22 10:33:49 -0700265
barfab@chromium.org5c374632012-04-05 16:50:56 +0200266def is_vault_mounted(
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400267 user,
barfab@chromium.org5c374632012-04-05 16:50:56 +0200268 device_regex=constants.CRYPTOHOME_DEV_REGEX_ANY,
269 fs_regex=constants.CRYPTOHOME_FS_REGEX_ANY,
270 allow_fail=False):
271 """Check whether a vault is mounted for the given user.
272
273 If no user is given, the shared mount point is checked, determining whether
274 a vault is mounted for any user.
275 """
276 user_mount_info = __get_user_mount_info(user=user, allow_fail=allow_fail)
277 for mount_info in user_mount_info:
278 if (len(mount_info) < 3 or
279 not re.match(device_regex, mount_info[0]) or
280 not re.match(fs_regex, mount_info[2])):
281 return False
282 return True
Sourav Poddar574bd622010-05-26 14:22:26 +0530283
284
barfab@chromium.org5c374632012-04-05 16:50:56 +0200285def is_guest_vault_mounted(allow_fail=False):
286 """Check whether a vault backed by tmpfs is mounted for the guest user."""
287 return is_vault_mounted(
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400288 user=GUEST_USER_NAME,
barfab@chromium.org5c374632012-04-05 16:50:56 +0200289 device_regex=constants.CRYPTOHOME_DEV_REGEX_GUEST,
290 fs_regex=constants.CRYPTOHOME_FS_REGEX_TMPFS,
291 allow_fail=allow_fail)
292
293
Elly Fong-Jones6cb26ad2013-05-21 12:09:23 -0400294def get_mounted_vault_devices(user, allow_fail=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200295 """Get the device(s) backing the vault mounted for the given user.
296
297 Returns the devices mounted at the user's user and system mount points. If
298 no user is given, the device mounted at the shared mount point is returned.
299 """
300 return [mount_info[0]
301 for mount_info
302 in __get_user_mount_info(user=user, allow_fail=allow_fail)
303 if len(mount_info)]
Nirnimesh66814492011-06-27 18:00:33 -0700304
305
306def canonicalize(credential):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200307 """Perform basic canonicalization of |email_address|.
Nirnimesh66814492011-06-27 18:00:33 -0700308
barfab@chromium.org5c374632012-04-05 16:50:56 +0200309 Perform basic canonicalization of |email_address|, taking into account that
310 gmail does not consider '.' or caps inside a username to matter. It also
311 ignores everything after a '+'. For example,
312 c.masone+abc@gmail.com == cMaSone@gmail.com, per
Nirnimesh66814492011-06-27 18:00:33 -0700313 http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313
314 """
315 if not credential:
316 return None
317
318 parts = credential.split('@')
319 if len(parts) != 2:
barfab@chromium.org5c374632012-04-05 16:50:56 +0200320 raise error.TestError('Malformed email: ' + credential)
Nirnimesh66814492011-06-27 18:00:33 -0700321
322 (name, domain) = parts
323 name = name.partition('+')[0]
barfab@chromium.org5c374632012-04-05 16:50:56 +0200324 if (domain == constants.SPECIAL_CASE_DOMAIN):
Nirnimesh66814492011-06-27 18:00:33 -0700325 name = name.replace('.', '')
326 return '@'.join([name, domain]).lower()
Elly Jones686c2f42011-10-24 16:45:07 -0400327
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200328
Will Drewryd2fed972013-12-05 16:35:51 -0600329def crash_cryptohomed():
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600330 # Try to kill cryptohomed so we get something to work with.
331 pid = __run_cmd('pgrep cryptohomed')
332 try:
Will Drewry9e440792013-12-11 17:18:35 -0600333 pid = int(pid)
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600334 except ValueError, e: # empty or invalid string
Will Drewry9e440792013-12-11 17:18:35 -0600335 raise error.TestError('Cryptohomed was not running')
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600336 utils.system('kill -ABRT %d' % pid)
337 # CONT just in case cryptohomed had a spurious STOP.
338 utils.system('kill -CONT %d' % pid)
339 utils.poll_for_condition(
340 lambda: utils.system('ps -p %d' % pid,
341 ignore_status=True) != 0,
Will Drewry934d1532014-01-30 16:23:17 -0600342 timeout=180,
Will Drewrydc2b0dd2013-12-10 16:41:04 -0600343 exception=error.TestError(
344 'Timeout waiting for cryptohomed to coredump'))
345
Will Drewryd2fed972013-12-05 16:35:51 -0600346
Will Drewry9e440792013-12-11 17:18:35 -0600347class CryptohomeProxy(DBusClient):
348 """A DBus proxy client for testing the Cryptohome DBus server.
349 """
350 CRYPTOHOME_BUS_NAME = 'org.chromium.Cryptohome'
351 CRYPTOHOME_OBJECT_PATH = '/org/chromium/Cryptohome'
352 CRYPTOHOME_INTERFACE = 'org.chromium.CryptohomeInterface'
353 ASYNC_CALL_STATUS_SIGNAL = 'AsyncCallStatus'
354 ASYNC_CALL_STATUS_SIGNAL_ARGUMENTS = (
355 'async_id', 'return_status', 'return_code'
356 )
357 DBUS_PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties'
358
Chris Masone19e305e2014-03-14 15:13:46 -0700359
Chris Masone64170f82014-03-14 15:47:05 -0700360 def __init__(self, bus_loop=None):
Will Drewry9e440792013-12-11 17:18:35 -0600361 self.main_loop = gobject.MainLoop()
Will Drewry78db9dc2014-04-01 16:34:23 -0500362 if bus_loop is None:
Chris Masone64170f82014-03-14 15:47:05 -0700363 bus_loop = DBusGMainLoop(set_as_default=True)
Will Drewry9e440792013-12-11 17:18:35 -0600364 self.bus = dbus.SystemBus(mainloop=bus_loop)
365 super(CryptohomeProxy, self).__init__(self.main_loop, self.bus,
366 self.CRYPTOHOME_BUS_NAME,
367 self.CRYPTOHOME_OBJECT_PATH)
368 self.iface = dbus.Interface(self.proxy_object,
369 self.CRYPTOHOME_INTERFACE)
370 self.properties = dbus.Interface(self.proxy_object,
371 self.DBUS_PROPERTIES_INTERFACE)
372 self.handle_signal(self.CRYPTOHOME_INTERFACE,
373 self.ASYNC_CALL_STATUS_SIGNAL,
374 self.ASYNC_CALL_STATUS_SIGNAL_ARGUMENTS)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400375
Chris Masone19e305e2014-03-14 15:13:46 -0700376
Will Drewryd2fed972013-12-05 16:35:51 -0600377 # Wrap all proxied calls to catch cryptohomed failures.
378 def __call(self, method, *args):
379 try:
Chris Masonef59d9df2014-03-14 12:05:32 -0700380 return method(*args, timeout=180)
Will Drewryd2fed972013-12-05 16:35:51 -0600381 except dbus.exceptions.DBusException, e:
382 if e.get_dbus_name() == 'org.freedesktop.DBus.Error.NoReply':
383 logging.error('Cryptohome is not responding. Sending ABRT')
384 crash_cryptohomed()
385 raise ChromiumOSError('cryptohomed aborted. Check crashes!')
386 raise e
387
Chris Masone19e305e2014-03-14 15:13:46 -0700388
Will Drewry9e440792013-12-11 17:18:35 -0600389 def __wait_for_specific_signal(self, signal, data):
390 """Wait for the |signal| with matching |data|
391 Returns the resulting dict on success or {} on error.
392 """
Will Drewryc4de5ff2014-02-03 13:26:57 -0600393 # Do not bubble up the timeout here, just return {}.
394 result = {}
395 try:
396 result = self.wait_for_signal(signal)
397 except utils.TimeoutError:
398 return {}
Will Drewry9e440792013-12-11 17:18:35 -0600399 for k in data.keys():
400 if not result.has_key(k) or result[k] != data[k]:
401 return {}
402 return result
403
Chris Masone19e305e2014-03-14 15:13:46 -0700404
Will Drewry9e440792013-12-11 17:18:35 -0600405 # Perform a data-less async call.
406 # TODO(wad) Add __async_data_call.
407 def __async_call(self, method, *args):
Will Drewryfef135a2014-05-23 16:02:14 -0500408 # Clear out any superfluous async call signals.
409 self.clear_signal_content(self.ASYNC_CALL_STATUS_SIGNAL)
Will Drewry9e440792013-12-11 17:18:35 -0600410 out = self.__call(method, *args)
411 logging.debug('Issued call ' + str(method) +
412 ' with async_id ' + str(out))
413 result = {}
414 try:
Will Drewry934d1532014-01-30 16:23:17 -0600415 # __wait_for_specific_signal has a 10s timeout
Will Drewry9e440792013-12-11 17:18:35 -0600416 result = utils.poll_for_condition(
417 lambda: self.__wait_for_specific_signal(
418 self.ASYNC_CALL_STATUS_SIGNAL, {'async_id' : out}),
Will Drewry934d1532014-01-30 16:23:17 -0600419 timeout=180,
Will Drewry9e440792013-12-11 17:18:35 -0600420 desc='matching %s signal' % self.ASYNC_CALL_STATUS_SIGNAL)
421 except utils.TimeoutError, e:
422 logging.error('Cryptohome timed out. Sending ABRT.')
423 crash_cryptohomed()
424 raise ChromiumOSError('cryptohomed aborted. Check crashes!')
425 return result
426
Chris Masone19e305e2014-03-14 15:13:46 -0700427
Will Drewry9e440792013-12-11 17:18:35 -0600428 def mount(self, user, password, create=False, async=True):
Elly Jones2f0ebba2011-10-27 13:43:20 -0400429 """Mounts a cryptohome.
430
431 Returns True if the mount succeeds or False otherwise.
432 TODO(ellyjones): Migrate mount_vault() to use a multi-user-safe
433 heuristic, then remove this method. See <crosbug.com/20778>.
434 """
Will Drewry9e440792013-12-11 17:18:35 -0600435 if async:
436 return self.__async_call(self.iface.AsyncMount, user, password,
437 create, False, [])['return_status']
Will Drewryd2fed972013-12-05 16:35:51 -0600438 out = self.__call(self.iface.Mount, user, password, create, False, [])
Will Drewry9e440792013-12-11 17:18:35 -0600439 # Sync returns (return code, return status)
440 return out[1] if len(out) > 1 else False
Elly Jones2f0ebba2011-10-27 13:43:20 -0400441
Chris Masone19e305e2014-03-14 15:13:46 -0700442
Elly Jones2f0ebba2011-10-27 13:43:20 -0400443 def unmount(self, user):
444 """Unmounts a cryptohome.
445
446 Returns True if the unmount suceeds or false otherwise.
447 TODO(ellyjones): Once there's a per-user unmount method, use it. See
448 <crosbug.com/20778>.
449 """
Will Drewryd2fed972013-12-05 16:35:51 -0600450 return self.__call(self.iface.Unmount)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400451
Chris Masone19e305e2014-03-14 15:13:46 -0700452
Elly Jones2f0ebba2011-10-27 13:43:20 -0400453 def is_mounted(self, user):
454 """Tests whether a user's cryptohome is mounted."""
455 return (utils.is_mountpoint(user_path(user))
456 and utils.is_mountpoint(system_path(user)))
457
Chris Masone19e305e2014-03-14 15:13:46 -0700458
Elly Jones2f0ebba2011-10-27 13:43:20 -0400459 def require_mounted(self, user):
460 """Raises a test failure if a user's cryptohome is not mounted."""
461 utils.require_mountpoint(user_path(user))
462 utils.require_mountpoint(system_path(user))
Elly Jones4458f442012-04-16 15:42:56 -0400463
Chris Masone19e305e2014-03-14 15:13:46 -0700464
Will Drewry9e440792013-12-11 17:18:35 -0600465 def migrate(self, user, oldkey, newkey, async=True):
Elly Jones4458f442012-04-16 15:42:56 -0400466 """Migrates the specified user's cryptohome from one key to another."""
Will Drewry9e440792013-12-11 17:18:35 -0600467 if async:
468 return self.__async_call(self.iface.AsyncMigrateKey,
469 user, oldkey, newkey)['return_status']
Will Drewryd2fed972013-12-05 16:35:51 -0600470 return self.__call(self.iface.MigrateKey, user, oldkey, newkey)
Elly Jones4458f442012-04-16 15:42:56 -0400471
Chris Masone19e305e2014-03-14 15:13:46 -0700472
Will Drewry9e440792013-12-11 17:18:35 -0600473 def remove(self, user, async=True):
474 if async:
475 return self.__async_call(self.iface.AsyncRemove,
476 user)['return_status']
Will Drewryd2fed972013-12-05 16:35:51 -0600477 return self.__call(self.iface.Remove, user)
Chris Masone19e305e2014-03-14 15:13:46 -0700478
479
480 def ensure_clean_cryptohome_for(self, user, password=None):
481 """Ensure a fresh cryptohome exists for user.
482
483 @param user: user who needs a shiny new cryptohome.
484 @param password: if unset, a random password will be used.
485 """
486 if not password:
487 password = ''.join(random.sample(string.ascii_lowercase, 6))
488 self.remove(user)
489 self.mount(user, password, create=True)