1 /*
2  * Copyright (C) 2019 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 "Access.h"
18 
19 #include <android-base/logging.h>
20 #include <binder/IPCThreadState.h>
21 #include <log/log_safetynet.h>
22 #include <selinux/android.h>
23 #include <selinux/avc.h>
24 
25 namespace android {
26 
27 #ifdef VENDORSERVICEMANAGER
28 constexpr bool kIsVendor = true;
29 #else
30 constexpr bool kIsVendor = false;
31 #endif
32 
getPidcon(pid_t pid)33 static std::string getPidcon(pid_t pid) {
34     android_errorWriteLog(0x534e4554, "121035042");
35 
36     char* lookup = nullptr;
37     if (getpidcon(pid, &lookup) < 0) {
38         LOG(ERROR) << "SELinux: getpidcon(pid=" << pid << ") failed to retrieve pid context";
39         return "";
40     }
41     std::string result = lookup;
42     freecon(lookup);
43     return result;
44 }
45 
getSehandle()46 static struct selabel_handle* getSehandle() {
47     static struct selabel_handle* gSehandle = nullptr;
48 
49     if (gSehandle != nullptr && selinux_status_updated()) {
50         selabel_close(gSehandle);
51         gSehandle = nullptr;
52     }
53 
54     if (gSehandle == nullptr) {
55         gSehandle = kIsVendor
56             ? selinux_android_vendor_service_context_handle()
57             : selinux_android_service_context_handle();
58     }
59 
60     CHECK(gSehandle != nullptr);
61     return gSehandle;
62 }
63 
64 struct AuditCallbackData {
65     const Access::CallingContext* context;
66     const std::string* tname;
67 };
68 
auditCallback(void * data,security_class_t,char * buf,size_t len)69 static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
70     const AuditCallbackData* ad = reinterpret_cast<AuditCallbackData*>(data);
71 
72     if (!ad) {
73         LOG(ERROR) << "No service manager audit data";
74         return 0;
75     }
76 
77     snprintf(buf, len, "pid=%d uid=%d name=%s", ad->context->debugPid, ad->context->uid,
78         ad->tname->c_str());
79     return 0;
80 }
81 
Access()82 Access::Access() {
83     union selinux_callback cb;
84 
85     cb.func_audit = auditCallback;
86     selinux_set_callback(SELINUX_CB_AUDIT, cb);
87 
88     cb.func_log = kIsVendor ? selinux_vendor_log_callback : selinux_log_callback;
89     selinux_set_callback(SELINUX_CB_LOG, cb);
90 
91     CHECK(selinux_status_open(true /*fallback*/) >= 0);
92 
93     CHECK(getcon(&mThisProcessContext) == 0);
94 }
95 
~Access()96 Access::~Access() {
97     freecon(mThisProcessContext);
98 }
99 
getCallingContext()100 Access::CallingContext Access::getCallingContext() {
101     IPCThreadState* ipc = IPCThreadState::self();
102 
103     const char* callingSid = ipc->getCallingSid();
104     pid_t callingPid = ipc->getCallingPid();
105 
106     return CallingContext {
107         .debugPid = callingPid,
108         .uid = ipc->getCallingUid(),
109         .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid),
110     };
111 }
112 
canFind(const CallingContext & ctx,const std::string & name)113 bool Access::canFind(const CallingContext& ctx,const std::string& name) {
114     return actionAllowedFromLookup(ctx, name, "find");
115 }
116 
canAdd(const CallingContext & ctx,const std::string & name)117 bool Access::canAdd(const CallingContext& ctx, const std::string& name) {
118     return actionAllowedFromLookup(ctx, name, "add");
119 }
120 
canList(const CallingContext & ctx)121 bool Access::canList(const CallingContext& ctx) {
122     return actionAllowed(ctx, mThisProcessContext, "list", "service_manager");
123 }
124 
actionAllowed(const CallingContext & sctx,const char * tctx,const char * perm,const std::string & tname)125 bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm,
126         const std::string& tname) {
127     const char* tclass = "service_manager";
128 
129     AuditCallbackData data = {
130         .context = &sctx,
131         .tname = &tname,
132     };
133 
134     return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm,
135         reinterpret_cast<void*>(&data));
136 }
137 
actionAllowedFromLookup(const CallingContext & sctx,const std::string & name,const char * perm)138 bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) {
139     char *tctx = nullptr;
140     if (selabel_lookup(getSehandle(), &tctx, name.c_str(), SELABEL_CTX_ANDROID_SERVICE) != 0) {
141         LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n";
142         return false;
143     }
144 
145     bool allowed = actionAllowed(sctx, tctx, perm, name);
146     freecon(tctx);
147     return allowed;
148 }
149 
150 }  // android
151