1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import pwd 8import stat 9 10from autotest_lib.client.bin import test, utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.common_lib.cros import chrome 13from autotest_lib.client.cros import cryptohome 14 15 16class security_ProfilePermissions(test.test): 17 """Check permissions of files of logged in and guest user.""" 18 version = 1 19 _HOMEDIR_MODE = 0710 20 21 def initialize(self, logged_in): 22 self._logged_in = logged_in 23 24 def check_owner_mode(self, path, expected_owner, expected_mode): 25 """ 26 Checks if the file/directory at 'path' is owned by 'expected_owner' 27 with permissions matching 'expected_mode'. 28 Returns True if they match, else False. 29 Logs any mismatches to logging.error. 30 31 @param path: file path to test. 32 @param expected_owner: expected owner of the file. 33 @param expected_mode: expected permission mode of the file. 34 35 """ 36 s = os.stat(path) 37 actual_owner = pwd.getpwuid(s.st_uid).pw_name 38 actual_mode = stat.S_IMODE(s.st_mode) 39 if (expected_owner != actual_owner or 40 expected_mode != actual_mode): 41 logging.error("%s - Expected %s:%s, saw %s:%s", 42 path, expected_owner, oct(expected_mode), 43 actual_owner, oct(actual_mode)) 44 return False 45 else: 46 return True 47 48 49 def run_once(self): 50 with chrome.Chrome(logged_in=self._logged_in) as cr: 51 username = (cr.username if self._logged_in 52 else cryptohome.GUEST_USER_NAME) 53 54 """Check permissions within cryptohome for anything too permissive. 55 """ 56 passes = [] 57 58 homepath = "/home/chronos" 59 passes.append(self.check_owner_mode(homepath, "chronos", 0755)) 60 61 user_mountpt = cryptohome.user_path(username) 62 passes.append(self.check_owner_mode(user_mountpt, "chronos", 63 self._HOMEDIR_MODE)) 64 65 # TODO(benchan): Refactor the following code to use some helper 66 # functions instead of find commands. 67 68 # An array of shell commands, each representing a test that 69 # passes if it emits no output. The first test is the main one. 70 # In general, writable by anyone else is bad, as is owned by 71 # anyone else. Any exceptions to that are pruned out of the 72 # first test and checked individually by subsequent tests. 73 cmds = [ 74 ('find -L "%s" -path "%s" -o ' 75 # Avoid false-positives on SingletonLock, SingletonCookie, etc. 76 ' \\( -name "Singleton*" -a -type l \\) -o ' 77 ' -path "%s/user" -prune -o ' 78 ' -path "%s/Downloads" -prune -o ' 79 ' -path "%s/flimflam" -prune -o ' 80 ' -path "%s/shill" -prune -o ' 81 ' -path "%s/.chaps" -prune -o ' 82 ' -path "%s/u-*" -prune -o ' 83 ' -path "%s/crash" -prune -o ' 84 ' \\( -perm /022 -o \\! -user chronos \\) -ls') % 85 (homepath, homepath, homepath, user_mountpt, user_mountpt, 86 user_mountpt, user_mountpt, homepath, homepath), 87 # /home/chronos/user and /home/chronos/user/Downloads are owned 88 # by the chronos-access group and with a group execute 89 # permission. 90 'find -L "%s" -maxdepth 0 \\( \\! -perm 710 ' 91 '-o \\! -user chronos -o \\! -group chronos-access \\) -ls' % 92 user_mountpt, 93 'find -L "%s/Downloads" -maxdepth 0 \\( \\! -perm 710 ' 94 '-o \\! -user chronos -o \\! -group chronos-access \\) -ls' % 95 user_mountpt, 96 'find -L "%s/flimflam" \\( -perm /077 -o \\! -user root \\) -ls' 97 % user_mountpt, 98 'find -L "%s/shill" \\( -perm /077 -o \\! -user root \\) -ls' % 99 user_mountpt, 100 'find -L "%s/.chaps -name auth_data_salt -prune -o ' 101 '\\! -user chaps -o \\! -group chronos-access -o -perm /027 -ls' 102 % user_mountpt, 103 'find -L "%s/.chaps -name auth_data_salt -a ' 104 '\\( \\! -user root -o -perm /077 \\) -ls' % user_mountpt, 105 ] 106 107 for cmd in cmds: 108 cmd_output = utils.system_output(cmd, ignore_status=True) 109 if cmd_output: 110 passes.append(False) 111 logging.error(cmd_output) 112 113 # This next section only applies if we have a real vault mounted 114 # (ie, not a BWSI tmpfs). 115 if cryptohome.is_permanent_vault_mounted(username): 116 # Also check the permissions of the underlying vault and 117 # supporting directory structure. 118 mountpath = cryptohome.get_mounted_vault_path(username) 119 120 # On ecryptfs backend, there's a 'vault' directory storing the 121 # encrypted data. If it exists, check its ownership as well. 122 vaultpath = os.path.join(mountpath, '../vault') 123 if os.path.exists(vaultpath): 124 passes.append(self.check_owner_mode(vaultpath, 125 "root", 0700)) 126 passes.append(self.check_owner_mode(mountpath, "root", 0700)) 127 passes.append(self.check_owner_mode(mountpath + "/../master.0", 128 "root", 0600)) 129 passes.append(self.check_owner_mode(mountpath + "/../", 130 "root", 0700)) 131 passes.append(self.check_owner_mode(mountpath + "/../../", 132 "root", 0700)) 133 134 if False in passes: 135 raise error.TestFail( 136 'Bad permissions found on cryptohome files') 137