blob: 4f4e44fce93fceacd6ba71c6d29438246f926d53 [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
Elly Jones2f0ebba2011-10-27 13:43:20 -04005import dbus
barfab@chromium.org5c374632012-04-05 16:50:56 +02006import constants
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +02007import logging
8import os
9import re
10import shutil
barfab@chromium.org5c374632012-04-05 16:50:56 +020011from autotest_lib.client.bin import utils
Chris Masone5e06f182010-03-23 08:29:52 -070012from autotest_lib.client.common_lib import error
Eric Lic4d8f4a2010-12-10 09:49:23 -080013
Sean Oe5d8fd02010-09-30 10:44:44 +020014CRYPTOHOME_CMD = '/usr/sbin/cryptohome'
15
16class ChromiumOSError(error.InstallError):
17 """Generic error for ChromiumOS-specific exceptions."""
18 pass
19
20
21def __run_cmd(cmd):
22 return utils.system_output(cmd + ' 2>&1', retain_output=True,
23 ignore_status=True).strip()
24
25
26def get_user_hash(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +020027 """Get the user hash for the given user."""
Sean Oe5d8fd02010-09-30 10:44:44 +020028 hash_cmd = CRYPTOHOME_CMD + ' --action=obfuscate_user --user=%s' % user
29 return __run_cmd(hash_cmd)
30
31
barfab@chromium.org5c374632012-04-05 16:50:56 +020032def user_path(user):
33 """Get the user mount point for the given user."""
34 return utils.system_output('cryptohome-path user %s' % user)
35
36
37def system_path(user):
38 """Get the system mount point for the given user."""
39 return utils.system_output('cryptohome-path system %s' % user)
40
41
Frank Farzand5e36312012-01-13 14:34:03 -080042def get_tpm_status():
43 """Get the TPM status.
44
45 Returns:
46 A TPM status dictionary, for example:
47 { 'Enabled': True,
48 'Owned': True,
49 'Being Owned': False,
50 'Ready': True,
51 'Password': ''
52 }
53 """
54 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status')
55 status = {}
56 for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']:
57 match = re.search('TPM %s: (true|false)' % field, out)
58 if not match:
59 raise ChromiumOSError('Invalid TPM status: "%s".' % out)
60 status[field] = match.group(1) == 'true'
61 match = re.search('TPM Password: (\w*)', out)
62 status['Password'] = ''
63 if match:
64 status['Password'] = match.group(1)
65 return status
66
67
68def take_tpm_ownership():
69 """Take TPM owernship.
70
71 Blocks until TPM is owned.
72 """
73 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_take_ownership')
74 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_wait_ownership')
75
76
Sean Oe5d8fd02010-09-30 10:44:44 +020077def remove_vault(user):
barfab@chromium.org5c374632012-04-05 16:50:56 +020078 """Remove the given user's vault from the shadow directory."""
Sean Oe5d8fd02010-09-30 10:44:44 +020079 logging.debug('user is %s', user)
80 user_hash = get_user_hash(user)
barfab@chromium.org5c374632012-04-05 16:50:56 +020081 logging.debug('Removing vault for user %s with hash %s' % (user, user_hash))
Sean Oe5d8fd02010-09-30 10:44:44 +020082 cmd = CRYPTOHOME_CMD + ' --action=remove --force --user=%s' % user
83 __run_cmd(cmd)
barfab@chromium.org5c374632012-04-05 16:50:56 +020084 # Ensure that the vault does not exist.
85 if os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash)):
86 raise ChromiumOSError('Cryptohome could not remove the user''s vault.')
Sean Oe5d8fd02010-09-30 10:44:44 +020087
88
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +020089def remove_all_vaults():
90 """Remove any existing vaults from the shadow directory.
91
92 This function must be run with root privileges.
93 """
barfab@chromium.org5c374632012-04-05 16:50:56 +020094 for item in os.listdir(constants.SHADOW_ROOT):
95 abs_item = os.path.join(constants.SHADOW_ROOT, item)
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +020096 if os.path.isdir(os.path.join(abs_item, 'vault')):
97 logging.debug('Removing vault for user with hash %s' % item)
98 shutil.rmtree(abs_item)
99
100
Sean Oe5d8fd02010-09-30 10:44:44 +0200101def mount_vault(user, password, create=False):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200102 """Mount the given user's vault."""
Sean Oe5d8fd02010-09-30 10:44:44 +0200103 cmd = (CRYPTOHOME_CMD + ' --action=mount --user=%s --password=%s' %
104 (user, password))
105 if create:
106 cmd += ' --create'
107 __run_cmd(cmd)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200108 # Ensure that the vault exists in the shadow directory.
Sean Oe5d8fd02010-09-30 10:44:44 +0200109 user_hash = get_user_hash(user)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200110 if not os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash)):
Sean Oe5d8fd02010-09-30 10:44:44 +0200111 raise ChromiumOSError('Cryptohome vault not found after mount.')
barfab@chromium.org5c374632012-04-05 16:50:56 +0200112 # Ensure that the vault is mounted.
113 if not is_vault_mounted(
114 user=user,
115 device_regex=constants.CRYPTOHOME_DEV_REGEX_REGULAR_USER,
116 allow_fail=True):
117 raise ChromiumOSError('Cryptohome created a vault but did not mount.')
Sean Oe5d8fd02010-09-30 10:44:44 +0200118
119
120def test_auth(user, password):
121 cmd = (CRYPTOHOME_CMD + ' --action=test_auth --user=%s --password=%s' %
122 (user, password))
123 return 'Authentication succeeded' in __run_cmd(cmd)
124
125
Elly Jones686c2f42011-10-24 16:45:07 -0400126def unmount_vault(user=None):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200127 """Unmount the given user's vault.
128
129 Once unmounting for a specific user is supported, the user parameter will
130 name the target user. See crosbug.com/20778.
Elly Jones686c2f42011-10-24 16:45:07 -0400131 """
Sean Oe5d8fd02010-09-30 10:44:44 +0200132 cmd = (CRYPTOHOME_CMD + ' --action=unmount')
133 __run_cmd(cmd)
barfab@chromium.org5c374632012-04-05 16:50:56 +0200134 # Ensure that the vault is not mounted.
135 if is_vault_mounted(allow_fail=True):
Sean Oe5d8fd02010-09-30 10:44:44 +0200136 raise ChromiumOSError('Cryptohome did not unmount the user.')
137
138
barfab@chromium.org5c374632012-04-05 16:50:56 +0200139def __get_mount_info(mount_point, allow_fail=False):
140 """Get information about the active mount at a given mount point."""
Will Drewry81ad6162010-04-01 10:26:07 -0500141 mount_line = utils.system_output(
barfab@chromium.org5c374632012-04-05 16:50:56 +0200142 'grep %s /proc/$(pgrep cryptohomed)/mounts' % mount_point,
143 ignore_status=allow_fail)
Sourav Poddar574bd622010-05-26 14:22:26 +0530144 return mount_line.split()
145
146
barfab@chromium.org5c374632012-04-05 16:50:56 +0200147def __get_user_mount_info(user=None, allow_fail=False):
148 """Get information about the active mounts for a given user.
149
150 Returns the active mounts at the user's user and system mount points. If no
151 user is given, the active mount at the shared mount point is returned
152 (regular users have a bind-mount at this mount point for backwards
153 compatibility; the guest user has a mount at this mount point only).
154 """
155 if user:
156 return [__get_mount_info(mount_point=user_path(user),
157 allow_fail=allow_fail),
158 __get_mount_info(mount_point=system_path(user),
159 allow_fail=allow_fail)]
Jim Hebertf08f88d2011-04-22 10:33:49 -0700160 else:
barfab@chromium.org5c374632012-04-05 16:50:56 +0200161 return [__get_mount_info(mount_point=constants.CRYPTOHOME_MOUNT_PT,
162 allow_fail=allow_fail)]
Jim Hebertf08f88d2011-04-22 10:33:49 -0700163
164
barfab@chromium.org5c374632012-04-05 16:50:56 +0200165def is_vault_mounted(
166 user=None,
167 device_regex=constants.CRYPTOHOME_DEV_REGEX_ANY,
168 fs_regex=constants.CRYPTOHOME_FS_REGEX_ANY,
169 allow_fail=False):
170 """Check whether a vault is mounted for the given user.
171
172 If no user is given, the shared mount point is checked, determining whether
173 a vault is mounted for any user.
174 """
175 user_mount_info = __get_user_mount_info(user=user, allow_fail=allow_fail)
176 for mount_info in user_mount_info:
177 if (len(mount_info) < 3 or
178 not re.match(device_regex, mount_info[0]) or
179 not re.match(fs_regex, mount_info[2])):
180 return False
181 return True
Sourav Poddar574bd622010-05-26 14:22:26 +0530182
183
barfab@chromium.org5c374632012-04-05 16:50:56 +0200184def is_guest_vault_mounted(allow_fail=False):
185 """Check whether a vault backed by tmpfs is mounted for the guest user."""
186 return is_vault_mounted(
187 user=None,
188 device_regex=constants.CRYPTOHOME_DEV_REGEX_GUEST,
189 fs_regex=constants.CRYPTOHOME_FS_REGEX_TMPFS,
190 allow_fail=allow_fail)
191
192
193def get_mounted_vault_devices(user=None, allow_fail=False):
194 """Get the device(s) backing the vault mounted for the given user.
195
196 Returns the devices mounted at the user's user and system mount points. If
197 no user is given, the device mounted at the shared mount point is returned.
198 """
199 return [mount_info[0]
200 for mount_info
201 in __get_user_mount_info(user=user, allow_fail=allow_fail)
202 if len(mount_info)]
Nirnimesh66814492011-06-27 18:00:33 -0700203
204
205def canonicalize(credential):
barfab@chromium.org5c374632012-04-05 16:50:56 +0200206 """Perform basic canonicalization of |email_address|.
Nirnimesh66814492011-06-27 18:00:33 -0700207
barfab@chromium.org5c374632012-04-05 16:50:56 +0200208 Perform basic canonicalization of |email_address|, taking into account that
209 gmail does not consider '.' or caps inside a username to matter. It also
210 ignores everything after a '+'. For example,
211 c.masone+abc@gmail.com == cMaSone@gmail.com, per
Nirnimesh66814492011-06-27 18:00:33 -0700212 http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313
213 """
214 if not credential:
215 return None
216
217 parts = credential.split('@')
218 if len(parts) != 2:
barfab@chromium.org5c374632012-04-05 16:50:56 +0200219 raise error.TestError('Malformed email: ' + credential)
Nirnimesh66814492011-06-27 18:00:33 -0700220
221 (name, domain) = parts
222 name = name.partition('+')[0]
barfab@chromium.org5c374632012-04-05 16:50:56 +0200223 if (domain == constants.SPECIAL_CASE_DOMAIN):
Nirnimesh66814492011-06-27 18:00:33 -0700224 name = name.replace('.', '')
225 return '@'.join([name, domain]).lower()
Elly Jones686c2f42011-10-24 16:45:07 -0400226
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200227
Elly Jones2f0ebba2011-10-27 13:43:20 -0400228class CryptohomeProxy:
229 def __init__(self):
230 BUSNAME = 'org.chromium.Cryptohome'
231 PATH = '/org/chromium/Cryptohome'
232 INTERFACE = 'org.chromium.CryptohomeInterface'
233 bus = dbus.SystemBus()
234 obj = bus.get_object(BUSNAME, PATH)
235 self.iface = dbus.Interface(obj, INTERFACE)
236
237 def mount(self, user, password, create=False):
238 """Mounts a cryptohome.
239
240 Returns True if the mount succeeds or False otherwise.
241 TODO(ellyjones): Migrate mount_vault() to use a multi-user-safe
242 heuristic, then remove this method. See <crosbug.com/20778>.
243 """
244 return self.iface.Mount(user, password, create, False, [])[1]
245
246 def unmount(self, user):
247 """Unmounts a cryptohome.
248
249 Returns True if the unmount suceeds or false otherwise.
250 TODO(ellyjones): Once there's a per-user unmount method, use it. See
251 <crosbug.com/20778>.
252 """
Elly Jones5c3c2b02011-12-21 14:26:28 -0500253 return self.iface.Unmount()
Elly Jones2f0ebba2011-10-27 13:43:20 -0400254
255 def is_mounted(self, user):
256 """Tests whether a user's cryptohome is mounted."""
257 return (utils.is_mountpoint(user_path(user))
258 and utils.is_mountpoint(system_path(user)))
259
260 def require_mounted(self, user):
261 """Raises a test failure if a user's cryptohome is not mounted."""
262 utils.require_mountpoint(user_path(user))
263 utils.require_mountpoint(system_path(user))