blob: fd59b413a032130edd5f16fdd54cf4c4f890c641 [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
Eric Lic4d8f4a2010-12-10 09:49:23 -08006import common
7import constants as chromeos_constants
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +02008import logging
9import os
10import re
11import shutil
Eric Lif7b81922011-03-04 14:39:35 -080012from autotest_lib.client.bin import test, utils
Chris Masone5e06f182010-03-23 08:29:52 -070013from autotest_lib.client.common_lib import error
Eric Lic4d8f4a2010-12-10 09:49:23 -080014
Sean Oe5d8fd02010-09-30 10:44:44 +020015CRYPTOHOME_CMD = '/usr/sbin/cryptohome'
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +020016SHADOW_ROOT = '/home/.shadow'
Sean Oe5d8fd02010-09-30 10:44:44 +020017
18class ChromiumOSError(error.InstallError):
19 """Generic error for ChromiumOS-specific exceptions."""
20 pass
21
22
23def __run_cmd(cmd):
24 return utils.system_output(cmd + ' 2>&1', retain_output=True,
25 ignore_status=True).strip()
26
27
28def get_user_hash(user):
29 """Get the hash for the test user account."""
30 hash_cmd = CRYPTOHOME_CMD + ' --action=obfuscate_user --user=%s' % user
31 return __run_cmd(hash_cmd)
32
33
Frank Farzand5e36312012-01-13 14:34:03 -080034def get_tpm_status():
35 """Get the TPM status.
36
37 Returns:
38 A TPM status dictionary, for example:
39 { 'Enabled': True,
40 'Owned': True,
41 'Being Owned': False,
42 'Ready': True,
43 'Password': ''
44 }
45 """
46 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status')
47 status = {}
48 for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']:
49 match = re.search('TPM %s: (true|false)' % field, out)
50 if not match:
51 raise ChromiumOSError('Invalid TPM status: "%s".' % out)
52 status[field] = match.group(1) == 'true'
53 match = re.search('TPM Password: (\w*)', out)
54 status['Password'] = ''
55 if match:
56 status['Password'] = match.group(1)
57 return status
58
59
60def take_tpm_ownership():
61 """Take TPM owernship.
62
63 Blocks until TPM is owned.
64 """
65 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_take_ownership')
66 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_wait_ownership')
67
68
Sean Oe5d8fd02010-09-30 10:44:44 +020069def remove_vault(user):
70 """Remove the test user account."""
71 logging.debug('user is %s', user)
72 user_hash = get_user_hash(user)
73 logging.debug('Removing vault for user %s - %s' % (user, user_hash))
74 cmd = CRYPTOHOME_CMD + ' --action=remove --force --user=%s' % user
75 __run_cmd(cmd)
76 # Ensure that the user directory does not exist
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +020077 if os.path.exists(os.path.join(SHADOW_ROOT, user_hash)):
Sean Oe5d8fd02010-09-30 10:44:44 +020078 raise ChromiumOSError('Cryptohome could not remove the test user.')
79
80
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +020081def remove_all_vaults():
82 """Remove any existing vaults from the shadow directory.
83
84 This function must be run with root privileges.
85 """
86 for item in os.listdir(SHADOW_ROOT):
87 abs_item = os.path.join(SHADOW_ROOT, item)
88 if os.path.isdir(os.path.join(abs_item, 'vault')):
89 logging.debug('Removing vault for user with hash %s' % item)
90 shutil.rmtree(abs_item)
91
92
Sean Oe5d8fd02010-09-30 10:44:44 +020093def mount_vault(user, password, create=False):
94 cmd = (CRYPTOHOME_CMD + ' --action=mount --user=%s --password=%s' %
95 (user, password))
96 if create:
97 cmd += ' --create'
98 __run_cmd(cmd)
99 # Ensure that the user directory exists
100 user_hash = get_user_hash(user)
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200101 if not os.path.exists(os.path.join(SHADOW_ROOT, user_hash)):
Sean Oe5d8fd02010-09-30 10:44:44 +0200102 raise ChromiumOSError('Cryptohome vault not found after mount.')
103 # Ensure that the user directory is mounted
104 if not is_mounted(allow_fail=True):
105 raise ChromiumOSError('Cryptohome created the user but did not mount.')
106
107
108def test_auth(user, password):
109 cmd = (CRYPTOHOME_CMD + ' --action=test_auth --user=%s --password=%s' %
110 (user, password))
111 return 'Authentication succeeded' in __run_cmd(cmd)
112
113
Elly Jones686c2f42011-10-24 16:45:07 -0400114def unmount_vault(user=None):
115 """
116 Unmount the directory. Once unmount-by-user is supported, the user
117 parameter will name the target user. See crosbug.com/20778
118 """
Sean Oe5d8fd02010-09-30 10:44:44 +0200119 cmd = (CRYPTOHOME_CMD + ' --action=unmount')
120 __run_cmd(cmd)
121 # Ensure that the user directory is not mounted
122 if is_mounted(allow_fail=True):
123 raise ChromiumOSError('Cryptohome did not unmount the user.')
124
125
126def __get_mount_parts(expected_mountpt=chromeos_constants.CRYPTOHOME_MOUNT_PT,
Sourav Poddar574bd622010-05-26 14:22:26 +0530127 allow_fail = False):
Will Drewry81ad6162010-04-01 10:26:07 -0500128 mount_line = utils.system_output(
129 'grep %s /proc/$(pgrep cryptohomed)/mounts' % expected_mountpt,
Sourav Poddar574bd622010-05-26 14:22:26 +0530130 ignore_status = allow_fail)
131 return mount_line.split()
132
133
Jim Hebertf08f88d2011-04-22 10:33:49 -0700134def current_mounted_vault(device=chromeos_constants.CRYPTOHOME_DEVICE_REGEX,
135 expected_mountpt=
136 chromeos_constants.CRYPTOHOME_MOUNT_PT,
137 allow_fail=False):
Frank Swiderski52653c32010-05-26 17:40:47 -0700138 mount_line = utils.system_output(
139 'grep %s /proc/$(pgrep cryptohomed)/mounts' % expected_mountpt,
140 ignore_status=allow_fail)
141 mount_parts = mount_line.split()
Jim Hebertf08f88d2011-04-22 10:33:49 -0700142 if len(mount_parts) > 0 and re.match(device, mount_parts[0]):
143 return mount_parts[0]
144 else:
145 return None
146
147
148def is_mounted(device=chromeos_constants.CRYPTOHOME_DEVICE_REGEX,
149 expected_mountpt=chromeos_constants.CRYPTOHOME_MOUNT_PT,
150 allow_fail=False):
151 return None != current_mounted_vault(device=device,
152 expected_mountpt=expected_mountpt,
153 allow_fail=allow_fail)
Sourav Poddar574bd622010-05-26 14:22:26 +0530154
155
Frank Swiderski52653c32010-05-26 17:40:47 -0700156def is_mounted_on_tmpfs(device = chromeos_constants.CRYPTOHOME_INCOGNITO,
Sourav Poddar574bd622010-05-26 14:22:26 +0530157 expected_mountpt =
158 chromeos_constants.CRYPTOHOME_MOUNT_PT,
159 allow_fail = False):
160 mount_parts = __get_mount_parts(device, allow_fail)
161 return (len(mount_parts) > 2 and device == mount_parts[0] and
162 'tmpfs' == mount_parts[2])
Nirnimesh66814492011-06-27 18:00:33 -0700163
164
165def canonicalize(credential):
166 """Perform basic canonicalization of |email_address|
167
168 Perform basic canonicalization of |email_address|, taking
169 into account that gmail does not consider '.' or caps inside a
170 username to matter. It also ignores everything after a '+'.
171 For example, c.masone+abc@gmail.com == cMaSone@gmail.com, per
172 http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313
173 """
174 if not credential:
175 return None
176
177 parts = credential.split('@')
178 if len(parts) != 2:
179 raise error.TestError('Malformed email: ' + credential)
180
181 (name, domain) = parts
182 name = name.partition('+')[0]
183 if (domain == chromeos_constants.SPECIAL_CASE_DOMAIN):
184 name = name.replace('.', '')
185 return '@'.join([name, domain]).lower()
Elly Jones686c2f42011-10-24 16:45:07 -0400186
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200187
Elly Jones686c2f42011-10-24 16:45:07 -0400188def user_path(user):
189 return utils.system_output('cryptohome-path user %s' % user)
190
barfab@chromium.orgcf2151e2012-04-04 15:39:34 +0200191
Elly Jones686c2f42011-10-24 16:45:07 -0400192def system_path(user):
193 return utils.system_output('cryptohome-path system %s' % user)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400194
195
196class CryptohomeProxy:
197 def __init__(self):
198 BUSNAME = 'org.chromium.Cryptohome'
199 PATH = '/org/chromium/Cryptohome'
200 INTERFACE = 'org.chromium.CryptohomeInterface'
201 bus = dbus.SystemBus()
202 obj = bus.get_object(BUSNAME, PATH)
203 self.iface = dbus.Interface(obj, INTERFACE)
204
205 def mount(self, user, password, create=False):
206 """Mounts a cryptohome.
207
208 Returns True if the mount succeeds or False otherwise.
209 TODO(ellyjones): Migrate mount_vault() to use a multi-user-safe
210 heuristic, then remove this method. See <crosbug.com/20778>.
211 """
212 return self.iface.Mount(user, password, create, False, [])[1]
213
214 def unmount(self, user):
215 """Unmounts a cryptohome.
216
217 Returns True if the unmount suceeds or false otherwise.
218 TODO(ellyjones): Once there's a per-user unmount method, use it. See
219 <crosbug.com/20778>.
220 """
Elly Jones5c3c2b02011-12-21 14:26:28 -0500221 return self.iface.Unmount()
Elly Jones2f0ebba2011-10-27 13:43:20 -0400222
223 def is_mounted(self, user):
224 """Tests whether a user's cryptohome is mounted."""
225 return (utils.is_mountpoint(user_path(user))
226 and utils.is_mountpoint(system_path(user)))
227
228 def require_mounted(self, user):
229 """Raises a test failure if a user's cryptohome is not mounted."""
230 utils.require_mountpoint(user_path(user))
231 utils.require_mountpoint(system_path(user))