• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 read beyond the mmap region.
46 
47 namespace {
48 
CopyFieldToString(char * dest,const char * source,size_t max)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 
FieldToUid(const char * field,uid_t * uid)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.
ParseLine(const char * begin,const char * end,const char ** fields,size_t num_fields)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 {
name__anonb1bbe8fa0111::PasswdLine96   const char* name() const {
97     return fields[0];
98   }
99   // Password is not supported.
uid__anonb1bbe8fa0111::PasswdLine100   const char* uid() const {
101     return fields[2];
102   }
gid__anonb1bbe8fa0111::PasswdLine103   const char* gid() const {
104     return fields[3];
105   }
106   // User Info is not supported
dir__anonb1bbe8fa0111::PasswdLine107   const char* dir() const {
108     return fields[5];
109   }
shell__anonb1bbe8fa0111::PasswdLine110   const char* shell() const {
111     return fields[6];
112   }
113 
ToPasswdState__anonb1bbe8fa0111::PasswdLine114   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 {
name__anonb1bbe8fa0111::GroupLine155   const char* name() const {
156     return fields[0];
157   }
158   // Password is not supported.
gid__anonb1bbe8fa0111::GroupLine159   const char* gid() const {
160     return fields[2];
161   }
162   // User list is not supported (returns simply name)
163 
ToGroupState__anonb1bbe8fa0111::GroupLine164   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 
MmapFile(const char * filename,const char * required_prefix)195 MmapFile::MmapFile(const char* filename, const char* required_prefix)
196     : filename_(filename), required_prefix_(required_prefix) {
197   lock_.init(false);
198 }
199 
Unmap()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 
GetFile(const char ** start,const char ** end)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 
DoMmap()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>
Find(Line * line,Predicate 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>
FindById(uid_t uid,Line * 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>
FindByName(const char * name,Line * 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 
PasswdFile(const char * filename,const char * required_prefix)322 PasswdFile::PasswdFile(const char* filename, const char* required_prefix)
323     : mmap_file_(filename, required_prefix) {
324 }
325 
FindById(uid_t id,passwd_state_t * passwd_state)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 
FindByName(const char * name,passwd_state_t * passwd_state)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 
GroupFile(const char * filename,const char * required_prefix)338 GroupFile::GroupFile(const char* filename, const char* required_prefix)
339     : mmap_file_(filename, required_prefix) {
340 }
341 
FindById(gid_t id,group_state_t * group_state)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 
FindByName(const char * name,group_state_t * group_state)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