1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "common/libs/utils/users.h"
18 
19 #include <grp.h>
20 #include <pwd.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <algorithm>
25 #include <cerrno>
26 #include <cstdlib>
27 #include <cstring>
28 #include <mutex>
29 #include <ostream>
30 #include <string>
31 #include <vector>
32 
33 #include <android-base/file.h>
34 #include <android-base/logging.h>
35 
36 #include "common/libs/utils/contains.h"
37 
38 namespace cuttlefish {
39 namespace {
GetSuplementaryGroups()40 std::vector<gid_t> GetSuplementaryGroups() {
41   int num_groups = getgroups(0, nullptr);
42   if (num_groups < 0) {
43     LOG(ERROR) << "Unable to get number of supplementary groups: "
44                << std::strerror(errno);
45     return {};
46   }
47   std::vector<gid_t> groups(num_groups + 1);
48   int retval = getgroups(groups.size(), groups.data());
49   if (retval < 0) {
50     LOG(ERROR) << "Error obtaining list of supplementary groups (list size: "
51                << groups.size() << "): " << std::strerror(errno);
52     return {};
53   }
54   return groups;
55 }
56 }  // namespace
57 
GroupIdFromName(const std::string & group_name)58 gid_t GroupIdFromName(const std::string& group_name) {
59   struct group grp{};
60   struct group* grp_p{};
61   std::vector<char> buffer(100);
62   int result = 0;
63   while(true) {
64     result = getgrnam_r(group_name.c_str(), &grp, buffer.data(), buffer.size(),
65                         &grp_p);
66     if (result != ERANGE) {
67       break;
68     }
69     buffer.resize(2*buffer.size());
70   }
71   if (result == 0) {
72     if (grp_p != nullptr) {
73       return grp.gr_gid;
74     } else {
75       // Caller may be checking with non-existent group name
76       return -1;
77     }
78   } else {
79     LOG(ERROR) << "Unable to get group id for group " << group_name << ": "
80                << std::strerror(result);
81     return -1;
82   }
83 }
84 
InGroup(const std::string & group)85 bool InGroup(const std::string& group) {
86   auto gid = GroupIdFromName(group);
87   if (gid == static_cast<gid_t>(-1)) {
88     return false;
89   }
90 
91   if (gid == getegid()) {
92     return true;
93   }
94 
95   auto groups = GetSuplementaryGroups();
96   return Contains(groups, gid);
97 }
98 
SystemWideUserHome()99 Result<std::string> SystemWideUserHome() {
100   auto uid = getuid();
101   // getpwuid() is not thread-safe, so we need a lock across all calls
102   static std::mutex getpwuid_mutex;
103   std::string home_dir;
104   {
105     std::lock_guard<std::mutex> lock(getpwuid_mutex);
106     const auto entry = getpwuid(uid);
107     if (entry) {
108       home_dir = entry->pw_dir;
109     }
110     endpwent();
111     if (home_dir.empty()) {
112       return CF_ERRNO("Failed to find the home directory using " << uid);
113     }
114   }
115   std::string home_realpath;
116   if (!android::base::Realpath(home_dir, &home_realpath)) {
117     return CF_ERRNO("Failed to convert " << home_dir << " to its Realpath");
118   }
119   return home_realpath;
120 }
121 
122 } // namespace cuttlefish
123