1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "grp_pwd_file.h" 30 31 #include <fcntl.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <sys/mman.h> 35 #include <sys/stat.h> 36 37 #include <async_safe/log.h> 38 39 #include "private/ErrnoRestorer.h" 40 41 // This file mmap's /*/etc/passwd and /*/etc/group in order to return their contents without any 42 // allocations. Note that these files and the strings contained within them are explicitly not 43 // null-terminated. ':'s are used to deliminate fields and '\n's are used to deliminate lines. 44 // There is a check that the file ends with '\n', such that terminating loops at '\n' ensures that 45 // memory will be not ready before the mmap region. 46 47 namespace { 48 49 void CopyFieldToString(char* dest, const char* source, size_t max) { 50 while (*source != ':' && *source != '\n' && max > 1) { 51 *dest++ = *source++; 52 --max; 53 } 54 *dest = '\0'; 55 } 56 57 bool FieldToUid(const char* field, uid_t* uid) { 58 if (field == nullptr) { 59 return false; 60 } 61 62 char* end = nullptr; 63 errno = 0; 64 uid_t result = strtoul(field, &end, 0); 65 if (errno != 0 || field == end || *end != ':') { 66 return false; 67 } 68 *uid = result; 69 return true; 70 } 71 72 // Returns a pointer to one past the end of line. 73 const char* ParseLine(const char* begin, const char* end, const char** fields, size_t num_fields) { 74 size_t fields_written = 0; 75 const char* position = begin; 76 fields[fields_written++] = position; 77 78 while (position < end && fields_written < num_fields) { 79 if (*position == '\n') { 80 return position + 1; 81 } 82 if (*position == ':') { 83 fields[fields_written++] = position + 1; 84 } 85 position++; 86 } 87 88 while (position < end && *position != '\n') { 89 position++; 90 } 91 92 return position + 1; 93 } 94 95 struct PasswdLine { 96 const char* name() const { 97 return fields[0]; 98 } 99 // Password is not supported. 100 const char* uid() const { 101 return fields[2]; 102 } 103 const char* gid() const { 104 return fields[3]; 105 } 106 // User Info is not supported 107 const char* dir() const { 108 return fields[5]; 109 } 110 const char* shell() const { 111 return fields[6]; 112 } 113 114 bool ToPasswdState(passwd_state_t* passwd_state) { 115 if (name() == nullptr || dir() == nullptr || shell() == nullptr) { 116 return false; 117 } 118 119 uid_t uid; 120 if (!FieldToUid(this->uid(), &uid)) { 121 return false; 122 } 123 124 gid_t gid; 125 if (!FieldToUid(this->gid(), &gid)) { 126 return false; 127 } 128 129 passwd_state->passwd_.pw_uid = uid; 130 passwd_state->passwd_.pw_gid = gid; 131 132 CopyFieldToString(passwd_state->name_buffer_, name(), sizeof(passwd_state->name_buffer_)); 133 passwd_state->passwd_.pw_name = passwd_state->name_buffer_; 134 135 passwd_state->passwd_.pw_passwd = nullptr; 136 137 #ifdef __LP64__ 138 passwd_state->passwd_.pw_gecos = nullptr; 139 #endif 140 141 CopyFieldToString(passwd_state->dir_buffer_, dir(), sizeof(passwd_state->dir_buffer_)); 142 passwd_state->passwd_.pw_dir = passwd_state->dir_buffer_; 143 144 CopyFieldToString(passwd_state->sh_buffer_, shell(), sizeof(passwd_state->sh_buffer_)); 145 passwd_state->passwd_.pw_shell = passwd_state->sh_buffer_; 146 147 return true; 148 } 149 150 static constexpr size_t kNumFields = 7; 151 const char* fields[kNumFields] = {}; 152 }; 153 154 struct GroupLine { 155 const char* name() const { 156 return fields[0]; 157 } 158 // Password is not supported. 159 const char* gid() const { 160 return fields[2]; 161 } 162 // User list is not supported (returns simply name) 163 164 bool ToGroupState(group_state_t* group_state) { 165 if (name() == nullptr || gid() == nullptr) { 166 return false; 167 } 168 169 gid_t gid; 170 if (!FieldToUid(this->gid(), &gid)) { 171 return false; 172 } 173 174 group_state->group_.gr_gid = gid; 175 176 CopyFieldToString(group_state->group_name_buffer_, name(), 177 sizeof(group_state->group_name_buffer_)); 178 group_state->group_.gr_name = group_state->group_name_buffer_; 179 180 group_state->group_.gr_passwd = nullptr; 181 182 group_state->group_.gr_mem = group_state->group_members_; 183 group_state->group_.gr_mem[0] = group_state->group_.gr_name; 184 group_state->group_.gr_mem[1] = nullptr; 185 186 return true; 187 } 188 189 static constexpr size_t kNumFields = 4; 190 const char* fields[kNumFields] = {}; 191 }; 192 193 } // namespace 194 195 MmapFile::MmapFile(const char* filename, const char* required_prefix) 196 : filename_(filename), required_prefix_(required_prefix) { 197 lock_.init(false); 198 } 199 200 void MmapFile::Unmap() { 201 if (status_ == FileStatus::Initialized) { 202 size_t size = end_ - start_ + 1; 203 munmap(const_cast<char*>(start_), size); 204 status_ = FileStatus::Uninitialized; 205 start_ = nullptr; 206 end_ = nullptr; 207 } 208 } 209 210 bool MmapFile::GetFile(const char** start, const char** end) { 211 LockGuard guard(lock_); 212 if (status_ == FileStatus::Initialized) { 213 *start = start_; 214 *end = end_; 215 return true; 216 } 217 if (status_ == FileStatus::Error) { 218 return false; 219 } 220 221 if (!DoMmap()) { 222 status_ = FileStatus::Error; 223 return false; 224 } 225 226 status_ = FileStatus::Initialized; 227 *start = start_; 228 *end = end_; 229 return true; 230 } 231 232 bool MmapFile::DoMmap() { 233 int fd = open(filename_, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); 234 235 struct stat fd_stat; 236 if (fstat(fd, &fd_stat) == -1) { 237 close(fd); 238 return false; 239 } 240 241 auto mmap_size = fd_stat.st_size; 242 243 void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0); 244 close(fd); 245 246 if (map_result == MAP_FAILED) { 247 return false; 248 } 249 250 start_ = static_cast<const char*>(map_result); 251 end_ = start_ + mmap_size - 1; 252 253 if (*end_ != '\n') { 254 munmap(map_result, mmap_size); 255 return false; 256 } 257 258 return true; 259 } 260 261 template <typename Line, typename Predicate> 262 bool MmapFile::Find(Line* line, Predicate predicate) { 263 const char* start; 264 const char* end; 265 if (!GetFile(&start, &end)) { 266 return false; 267 } 268 269 const char* line_beginning = start; 270 271 while (line_beginning < end) { 272 line_beginning = ParseLine(line_beginning, end, line->fields, line->kNumFields); 273 // To comply with Treble, users/groups from the vendor partition need to be prefixed with 274 // vendor_. 275 if (required_prefix_ != nullptr) { 276 if (strncmp(line->fields[0], required_prefix_, strlen(required_prefix_)) != 0) { 277 char name[kGrpPwdBufferSize]; 278 CopyFieldToString(name, line->fields[0], sizeof(name)); 279 async_safe_format_log(ANDROID_LOG_ERROR, "libc", 280 "Found user/group name '%s' in '%s' without required prefix '%s'", 281 name, filename_, required_prefix_); 282 continue; 283 } 284 } 285 if (predicate(line)) return true; 286 } 287 288 return false; 289 } 290 291 template <typename Line> 292 bool MmapFile::FindById(uid_t uid, Line* line) { 293 return Find(line, [uid](const auto& line) { 294 uid_t line_id; 295 if (!FieldToUid(line->fields[2], &line_id)) { 296 return false; 297 } 298 299 return line_id == uid; 300 }); 301 } 302 303 template <typename Line> 304 bool MmapFile::FindByName(const char* name, Line* line) { 305 return Find(line, [name](const auto& line) { 306 const char* line_name = line->fields[0]; 307 if (line_name == nullptr) { 308 return false; 309 } 310 311 const char* match_name = name; 312 while (*line_name != '\n' && *line_name != ':' && *match_name != '\0') { 313 if (*line_name++ != *match_name++) { 314 return false; 315 } 316 } 317 318 return *line_name == ':' && *match_name == '\0'; 319 }); 320 } 321 322 PasswdFile::PasswdFile(const char* filename, const char* required_prefix) 323 : mmap_file_(filename, required_prefix) { 324 } 325 326 bool PasswdFile::FindById(uid_t id, passwd_state_t* passwd_state) { 327 ErrnoRestorer errno_restorer; 328 PasswdLine passwd_line; 329 return mmap_file_.FindById(id, &passwd_line) && passwd_line.ToPasswdState(passwd_state); 330 } 331 332 bool PasswdFile::FindByName(const char* name, passwd_state_t* passwd_state) { 333 ErrnoRestorer errno_restorer; 334 PasswdLine passwd_line; 335 return mmap_file_.FindByName(name, &passwd_line) && passwd_line.ToPasswdState(passwd_state); 336 } 337 338 GroupFile::GroupFile(const char* filename, const char* required_prefix) 339 : mmap_file_(filename, required_prefix) { 340 } 341 342 bool GroupFile::FindById(gid_t id, group_state_t* group_state) { 343 ErrnoRestorer errno_restorer; 344 GroupLine group_line; 345 return mmap_file_.FindById(id, &group_line) && group_line.ToGroupState(group_state); 346 } 347 348 bool GroupFile::FindByName(const char* name, group_state_t* group_state) { 349 ErrnoRestorer errno_restorer; 350 GroupLine group_line; 351 return mmap_file_.FindByName(name, &group_line) && group_line.ToGroupState(group_state); 352 } 353