blob: f4aa6e21663bf9d1e4b23d03ed7952eeb49276bd [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
Eric Lif7b81922011-03-04 14:39:35 -08005import logging, os, re
Elly Jones2f0ebba2011-10-27 13:43:20 -04006import dbus
Eric Lic4d8f4a2010-12-10 09:49:23 -08007import common
8import constants as chromeos_constants
Eric Lif7b81922011-03-04 14:39:35 -08009from autotest_lib.client.bin import test, utils
Chris Masone5e06f182010-03-23 08:29:52 -070010from autotest_lib.client.common_lib import error
Eric Lic4d8f4a2010-12-10 09:49:23 -080011
Sean Oe5d8fd02010-09-30 10:44:44 +020012CRYPTOHOME_CMD = '/usr/sbin/cryptohome'
13
14class ChromiumOSError(error.InstallError):
15 """Generic error for ChromiumOS-specific exceptions."""
16 pass
17
18
19def __run_cmd(cmd):
20 return utils.system_output(cmd + ' 2>&1', retain_output=True,
21 ignore_status=True).strip()
22
23
24def get_user_hash(user):
25 """Get the hash for the test user account."""
26 hash_cmd = CRYPTOHOME_CMD + ' --action=obfuscate_user --user=%s' % user
27 return __run_cmd(hash_cmd)
28
29
Frank Farzand5e36312012-01-13 14:34:03 -080030def get_tpm_status():
31 """Get the TPM status.
32
33 Returns:
34 A TPM status dictionary, for example:
35 { 'Enabled': True,
36 'Owned': True,
37 'Being Owned': False,
38 'Ready': True,
39 'Password': ''
40 }
41 """
42 out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status')
43 status = {}
44 for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']:
45 match = re.search('TPM %s: (true|false)' % field, out)
46 if not match:
47 raise ChromiumOSError('Invalid TPM status: "%s".' % out)
48 status[field] = match.group(1) == 'true'
49 match = re.search('TPM Password: (\w*)', out)
50 status['Password'] = ''
51 if match:
52 status['Password'] = match.group(1)
53 return status
54
55
56def take_tpm_ownership():
57 """Take TPM owernship.
58
59 Blocks until TPM is owned.
60 """
61 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_take_ownership')
62 __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_wait_ownership')
63
64
Sean Oe5d8fd02010-09-30 10:44:44 +020065def remove_vault(user):
66 """Remove the test user account."""
67 logging.debug('user is %s', user)
68 user_hash = get_user_hash(user)
69 logging.debug('Removing vault for user %s - %s' % (user, user_hash))
70 cmd = CRYPTOHOME_CMD + ' --action=remove --force --user=%s' % user
71 __run_cmd(cmd)
72 # Ensure that the user directory does not exist
73 if os.path.exists(os.path.join('/home/.shadow/', user_hash)):
74 raise ChromiumOSError('Cryptohome could not remove the test user.')
75
76
77def mount_vault(user, password, create=False):
78 cmd = (CRYPTOHOME_CMD + ' --action=mount --user=%s --password=%s' %
79 (user, password))
80 if create:
81 cmd += ' --create'
82 __run_cmd(cmd)
83 # Ensure that the user directory exists
84 user_hash = get_user_hash(user)
85 if not os.path.exists(os.path.join('/home/.shadow/', user_hash)):
86 raise ChromiumOSError('Cryptohome vault not found after mount.')
87 # Ensure that the user directory is mounted
88 if not is_mounted(allow_fail=True):
89 raise ChromiumOSError('Cryptohome created the user but did not mount.')
90
91
92def test_auth(user, password):
93 cmd = (CRYPTOHOME_CMD + ' --action=test_auth --user=%s --password=%s' %
94 (user, password))
95 return 'Authentication succeeded' in __run_cmd(cmd)
96
97
Elly Jones686c2f42011-10-24 16:45:07 -040098def unmount_vault(user=None):
99 """
100 Unmount the directory. Once unmount-by-user is supported, the user
101 parameter will name the target user. See crosbug.com/20778
102 """
Sean Oe5d8fd02010-09-30 10:44:44 +0200103 cmd = (CRYPTOHOME_CMD + ' --action=unmount')
104 __run_cmd(cmd)
105 # Ensure that the user directory is not mounted
106 if is_mounted(allow_fail=True):
107 raise ChromiumOSError('Cryptohome did not unmount the user.')
108
109
110def __get_mount_parts(expected_mountpt=chromeos_constants.CRYPTOHOME_MOUNT_PT,
Sourav Poddar574bd622010-05-26 14:22:26 +0530111 allow_fail = False):
Will Drewry81ad6162010-04-01 10:26:07 -0500112 mount_line = utils.system_output(
113 'grep %s /proc/$(pgrep cryptohomed)/mounts' % expected_mountpt,
Sourav Poddar574bd622010-05-26 14:22:26 +0530114 ignore_status = allow_fail)
115 return mount_line.split()
116
117
Jim Hebertf08f88d2011-04-22 10:33:49 -0700118def current_mounted_vault(device=chromeos_constants.CRYPTOHOME_DEVICE_REGEX,
119 expected_mountpt=
120 chromeos_constants.CRYPTOHOME_MOUNT_PT,
121 allow_fail=False):
Frank Swiderski52653c32010-05-26 17:40:47 -0700122 mount_line = utils.system_output(
123 'grep %s /proc/$(pgrep cryptohomed)/mounts' % expected_mountpt,
124 ignore_status=allow_fail)
125 mount_parts = mount_line.split()
Jim Hebertf08f88d2011-04-22 10:33:49 -0700126 if len(mount_parts) > 0 and re.match(device, mount_parts[0]):
127 return mount_parts[0]
128 else:
129 return None
130
131
132def is_mounted(device=chromeos_constants.CRYPTOHOME_DEVICE_REGEX,
133 expected_mountpt=chromeos_constants.CRYPTOHOME_MOUNT_PT,
134 allow_fail=False):
135 return None != current_mounted_vault(device=device,
136 expected_mountpt=expected_mountpt,
137 allow_fail=allow_fail)
Sourav Poddar574bd622010-05-26 14:22:26 +0530138
139
Frank Swiderski52653c32010-05-26 17:40:47 -0700140def is_mounted_on_tmpfs(device = chromeos_constants.CRYPTOHOME_INCOGNITO,
Sourav Poddar574bd622010-05-26 14:22:26 +0530141 expected_mountpt =
142 chromeos_constants.CRYPTOHOME_MOUNT_PT,
143 allow_fail = False):
144 mount_parts = __get_mount_parts(device, allow_fail)
145 return (len(mount_parts) > 2 and device == mount_parts[0] and
146 'tmpfs' == mount_parts[2])
Nirnimesh66814492011-06-27 18:00:33 -0700147
148
149def canonicalize(credential):
150 """Perform basic canonicalization of |email_address|
151
152 Perform basic canonicalization of |email_address|, taking
153 into account that gmail does not consider '.' or caps inside a
154 username to matter. It also ignores everything after a '+'.
155 For example, c.masone+abc@gmail.com == cMaSone@gmail.com, per
156 http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313
157 """
158 if not credential:
159 return None
160
161 parts = credential.split('@')
162 if len(parts) != 2:
163 raise error.TestError('Malformed email: ' + credential)
164
165 (name, domain) = parts
166 name = name.partition('+')[0]
167 if (domain == chromeos_constants.SPECIAL_CASE_DOMAIN):
168 name = name.replace('.', '')
169 return '@'.join([name, domain]).lower()
Elly Jones686c2f42011-10-24 16:45:07 -0400170
171def user_path(user):
172 return utils.system_output('cryptohome-path user %s' % user)
173
174def system_path(user):
175 return utils.system_output('cryptohome-path system %s' % user)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400176
177
178class CryptohomeProxy:
179 def __init__(self):
180 BUSNAME = 'org.chromium.Cryptohome'
181 PATH = '/org/chromium/Cryptohome'
182 INTERFACE = 'org.chromium.CryptohomeInterface'
183 bus = dbus.SystemBus()
184 obj = bus.get_object(BUSNAME, PATH)
185 self.iface = dbus.Interface(obj, INTERFACE)
186
187 def mount(self, user, password, create=False):
188 """Mounts a cryptohome.
189
190 Returns True if the mount succeeds or False otherwise.
191 TODO(ellyjones): Migrate mount_vault() to use a multi-user-safe
192 heuristic, then remove this method. See <crosbug.com/20778>.
193 """
194 return self.iface.Mount(user, password, create, False, [])[1]
195
196 def unmount(self, user):
197 """Unmounts a cryptohome.
198
199 Returns True if the unmount suceeds or false otherwise.
200 TODO(ellyjones): Once there's a per-user unmount method, use it. See
201 <crosbug.com/20778>.
202 """
Elly Jones5c3c2b02011-12-21 14:26:28 -0500203 return self.iface.Unmount()
Elly Jones2f0ebba2011-10-27 13:43:20 -0400204
205 def is_mounted(self, user):
206 """Tests whether a user's cryptohome is mounted."""
207 return (utils.is_mountpoint(user_path(user))
208 and utils.is_mountpoint(system_path(user)))
209
210 def require_mounted(self, user):
211 """Raises a test failure if a user's cryptohome is not mounted."""
212 utils.require_mountpoint(user_path(user))
213 utils.require_mountpoint(system_path(user))