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 
5 #include "brillo/cryptohome.h"
6 
7 #include <openssl/sha.h>
8 #include <stdint.h>
9 
10 #include <algorithm>
11 #include <cstring>
12 #include <limits>
13 #include <vector>
14 
15 #include <base/files/file_util.h>
16 #include <base/strings/string_number_conversions.h>
17 #include <base/strings/stringprintf.h>
18 
19 using base::FilePath;
20 
21 namespace brillo {
22 namespace cryptohome {
23 namespace home {
24 
25 const char kGuestUserName[] = "$guest";
26 
27 // Path to user homes mounted with the mount_hidden option. The user home mount
28 // will be located at:
29 // kHiddenUserHomeBaseDir/<sanitized_user_name>/kHiddenUserHomeMountSubdir
30 const char kHiddenUserHomeBaseDir[] = "/home/.shadow";
31 const char kHiddenUserHomeMountSubdir[] = "mount";
32 
33 // Subdirectory of a user home mount where daemon-specific data is stored.
34 // This is used to assemble daemon data storage paths for hidden user home
35 // mounts.
36 const char kHiddenUserHomeRootSubdir[] = "root";
37 
38 static char g_user_home_prefix[PATH_MAX] = "/home/user/";
39 static char g_root_home_prefix[PATH_MAX] = "/home/root/";
40 static char g_system_salt_path[PATH_MAX] = "/home/.shadow/salt";
41 
42 static std::string* salt = nullptr;
43 
EnsureSystemSaltIsLoaded()44 static bool EnsureSystemSaltIsLoaded() {
45   if (salt && !salt->empty())
46     return true;
47   FilePath salt_path(g_system_salt_path);
48   int64_t file_size;
49   if (!base::GetFileSize(salt_path, &file_size)) {
50     PLOG(ERROR) << "Could not get size of system salt: " << g_system_salt_path;
51     return false;
52   }
53   if (file_size > static_cast<int64_t>(std::numeric_limits<int>::max())) {
54     LOG(ERROR) << "System salt too large: " << file_size;
55     return false;
56   }
57   std::vector<char> buf;
58   buf.resize(file_size);
59   unsigned int data_read = base::ReadFile(salt_path, buf.data(), file_size);
60   if (data_read != file_size) {
61     PLOG(ERROR) << "Could not read entire file: " << data_read
62                 << " != " << file_size;
63     return false;
64   }
65 
66   if (!salt)
67     salt = new std::string();
68   salt->assign(buf.data(), file_size);
69   return true;
70 }
71 
SanitizeUserName(const std::string & username)72 std::string SanitizeUserName(const std::string& username) {
73   if (!EnsureSystemSaltIsLoaded())
74     return std::string();
75 
76   unsigned char binmd[SHA_DIGEST_LENGTH];
77   std::string lowercase(username);
78   std::transform(
79       lowercase.begin(), lowercase.end(), lowercase.begin(), ::tolower);
80   SHA_CTX ctx;
81   SHA1_Init(&ctx);
82   SHA1_Update(&ctx, salt->data(), salt->size());
83   SHA1_Update(&ctx, lowercase.data(), lowercase.size());
84   SHA1_Final(binmd, &ctx);
85   std::string final = base::HexEncode(binmd, sizeof(binmd));
86   // Stay compatible with CryptoLib::HexEncodeToBuffer()
87   std::transform(final.begin(), final.end(), final.begin(), ::tolower);
88   return final;
89 }
90 
GetUserPathPrefix()91 FilePath GetUserPathPrefix() {
92   return FilePath(g_user_home_prefix);
93 }
94 
GetRootPathPrefix()95 FilePath GetRootPathPrefix() {
96   return FilePath(g_root_home_prefix);
97 }
98 
GetHashedUserPath(const std::string & hashed_username)99 FilePath GetHashedUserPath(const std::string& hashed_username) {
100   return FilePath(
101       base::StringPrintf("%s%s", g_user_home_prefix, hashed_username.c_str()));
102 }
103 
GetUserPath(const std::string & username)104 FilePath GetUserPath(const std::string& username) {
105   if (!EnsureSystemSaltIsLoaded())
106     return FilePath();
107   return GetHashedUserPath(SanitizeUserName(username));
108 }
109 
GetRootPath(const std::string & username)110 FilePath GetRootPath(const std::string& username) {
111   if (!EnsureSystemSaltIsLoaded())
112     return FilePath();
113   return FilePath(base::StringPrintf(
114       "%s%s", g_root_home_prefix, SanitizeUserName(username).c_str()));
115 }
116 
GetDaemonPath(const std::string & username,const std::string & daemon)117 FilePath GetDaemonPath(const std::string& username, const std::string& daemon) {
118   if (!EnsureSystemSaltIsLoaded())
119     return FilePath();
120   return GetRootPath(username).Append(daemon);
121 }
122 
GetDaemonPathForHiddenUserHome(const std::string & username,const std::string & daemon)123 FilePath GetDaemonPathForHiddenUserHome(const std::string& username,
124                                         const std::string& daemon) {
125   if (!EnsureSystemSaltIsLoaded())
126     return FilePath();
127 
128   return FilePath(kHiddenUserHomeBaseDir)
129       .Append(SanitizeUserName(username))
130       .Append(kHiddenUserHomeMountSubdir)
131       .Append(kHiddenUserHomeRootSubdir)
132       .Append(daemon);
133 }
134 
IsSanitizedUserName(const std::string & sanitized)135 bool IsSanitizedUserName(const std::string& sanitized) {
136   std::vector<uint8_t> bytes;
137   return (sanitized.length() == 2 * SHA_DIGEST_LENGTH) &&
138          base::HexStringToBytes(sanitized, &bytes);
139 }
140 
SetUserHomePrefix(const std::string & prefix)141 void SetUserHomePrefix(const std::string& prefix) {
142   if (prefix.length() < sizeof(g_user_home_prefix)) {
143     snprintf(
144         g_user_home_prefix, sizeof(g_user_home_prefix), "%s", prefix.c_str());
145   }
146 }
147 
SetRootHomePrefix(const std::string & prefix)148 void SetRootHomePrefix(const std::string& prefix) {
149   if (prefix.length() < sizeof(g_root_home_prefix)) {
150     snprintf(
151         g_root_home_prefix, sizeof(g_root_home_prefix), "%s", prefix.c_str());
152   }
153 }
154 
GetSystemSalt()155 std::string* GetSystemSalt() {
156   return salt;
157 }
158 
SetSystemSalt(std::string * value)159 void SetSystemSalt(std::string* value) {
160   salt = value;
161 }
162 
163 }  // namespace home
164 }  // namespace cryptohome
165 }  // namespace brillo
166