1 /*
2  * Copyright (C) 2023 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 "./context.h"
18 
19 #include <private/android_filesystem_config.h>  // For AID_APP_START.
20 #include <stdio.h>
21 #include <stdlib.h>
22 
23 #include <iostream>
24 #include <string>
25 
26 #include "./string-utils.h"
27 #include "./test-app.h"
28 
29 namespace shell_as {
30 
31 namespace {
32 
ParseIdFromProcStatusLine(char * line,uid_t * id)33 bool ParseIdFromProcStatusLine(char* line, uid_t* id) {
34   // The user and group ID lines of the status file look like:
35   //
36   // Uid: <real> <effective> <saved> <filesystem>
37   // Gid: <real> <effective> <saved> <filesystem>
38   std::vector<uid_t> ids;
39   if (!SplitIdsAndSkip(line, "\t\n ", /*num_to_skip=*/1, &ids) ||
40       ids.size() < 1) {
41     return false;
42   }
43   *id = ids[0];
44   return true;
45 }
46 
ParseGroupsFromProcStatusLine(char * line,std::vector<gid_t> * ids)47 bool ParseGroupsFromProcStatusLine(char* line, std::vector<gid_t>* ids) {
48   // The supplementary groups line of the status file looks like:
49   //
50   // Groups: <group1> <group2> <group3> ...
51   return SplitIdsAndSkip(line, "\t\n ", /*num_to_skip=*/1, ids);
52 }
53 
ParseProcStatusFile(const pid_t process_id,uid_t * real_user_id,gid_t * real_group_id,std::vector<gid_t> * supplementary_group_ids)54 bool ParseProcStatusFile(const pid_t process_id, uid_t* real_user_id,
55                          gid_t* real_group_id,
56                          std::vector<gid_t>* supplementary_group_ids) {
57   std::string proc_status_path =
58       std::string("/proc/") + std::to_string(process_id) + "/status";
59   FILE* status_file = fopen(proc_status_path.c_str(), "r");
60   if (status_file == nullptr) {
61     std::cerr << "Unable to open '" << proc_status_path << "'" << std::endl;
62   }
63   bool parsed_user = false;
64   bool parsed_group = false;
65   bool parsed_supplementary_groups = false;
66   while (true) {
67     size_t line_length = 0;
68     char* line = nullptr;
69     if (getline(&line, &line_length, status_file) < 0) {
70       free(line);
71       break;
72     }
73     if (strncmp("Uid:", line, 4) == 0) {
74       parsed_user = ParseIdFromProcStatusLine(line, real_user_id);
75     } else if (strncmp("Gid:", line, 4) == 0) {
76       parsed_group = ParseIdFromProcStatusLine(line, real_group_id);
77     } else if (strncmp("Groups:", line, 7) == 0) {
78       parsed_supplementary_groups =
79           ParseGroupsFromProcStatusLine(line, supplementary_group_ids);
80     }
81     free(line);
82   }
83   fclose(status_file);
84   return parsed_user && parsed_group && parsed_supplementary_groups;
85 }
86 
87 }  // namespace
88 
SecurityContextFromProcess(const pid_t process_id,SecurityContext * context)89 bool SecurityContextFromProcess(const pid_t process_id,
90                                 SecurityContext* context) {
91   char* selinux_context;
92   if (getpidcon(process_id, &selinux_context) != 0) {
93     std::cerr << "Unable to obtain SELinux context from process " << process_id
94               << std::endl;
95     return false;
96   }
97 
98   cap_t capabilities = cap_get_pid(process_id);
99   if (capabilities == nullptr) {
100     std::cerr << "Unable to obtain capability set from process " << process_id
101               << std::endl;
102     return false;
103   }
104 
105   uid_t user_id = 0;
106   gid_t group_id = 0;
107   std::vector<gid_t> supplementary_group_ids;
108   if (!ParseProcStatusFile(process_id, &user_id, &group_id,
109                            &supplementary_group_ids)) {
110     std::cerr << "Unable to obtain user and group IDs from process "
111               << process_id << std::endl;
112     return false;
113   }
114 
115   context->selinux_context = selinux_context;
116   context->user_id = user_id;
117   context->group_id = group_id;
118   context->supplementary_group_ids = supplementary_group_ids;
119   context->capabilities = capabilities;
120   return true;
121 }
122 
SecurityContextFromTestApp(SecurityContext * context)123 bool SecurityContextFromTestApp(SecurityContext* context) {
124   pid_t test_app_pid = 0;
125   if (!SetupAndStartTestApp(&test_app_pid)) {
126     std::cerr << "Unable to install test app." << std::endl;
127     return false;
128   }
129   return SecurityContextFromProcess(test_app_pid, context);
130 }
131 
SeccompFilterFromUserId(uid_t user_id)132 SeccompFilter SeccompFilterFromUserId(uid_t user_id) {
133   // Copied from:
134   // frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
135   return user_id >= AID_APP_START ? kAppFilter : kSystemFilter;
136 }
137 
138 }  // namespace shell_as
139