1 /*
2  * Copyright (C) 2017 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 <jni.h>
18 #include <nativehelper/JNIHelp.h>
19 #include <nativehelper/ScopedLocalRef.h>
20 #include <nativehelper/ScopedUtfChars.h>
21 #include <selinux/selinux.h>
22 #include <android/log.h>
23 #include <sys/socket.h>
24 #include <linux/netlink.h>
25 #include <linux/rtnetlink.h>
26 #include <memory>
27 
28 struct SecurityContext_Delete {
operator ()SecurityContext_Delete29     void operator()(char* p) const {
30         freecon(p);
31     }
32 };
33 typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext;
34 
35 /**
36  * Function: checkNetlinkRouteGetlink
37  * Purpose: Checks to see if RTM_GETLINK is allowed on a netlink route socket.
38  * Returns: 13 (expected) if RTM_GETLINK fails with permission denied.
39  *          0 if socket creation fails
40  *          -1 if bind() succeeds.
41  */
checkNetlinkRouteGetlink()42 static jint checkNetlinkRouteGetlink() {
43     int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
44     if (sock < 0) {
45         __android_log_print(ANDROID_LOG_ERROR, "SELLinuxTargetSdkTest", "socket creation failed.");
46         return 0;
47     }
48     struct NetlinkMessage {
49         nlmsghdr hdr;
50         rtgenmsg msg;
51     } request;
52     memset(&request, 0, sizeof(request));
53     request.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
54     request.hdr.nlmsg_type = RTM_GETLINK;
55     request.hdr.nlmsg_len = sizeof(request);
56     request.msg.rtgen_family = AF_UNSPEC;
57 
58     int ret = send(sock, &request, sizeof(request), 0);
59     if (ret < 0) {
60         return errno;
61     }
62 
63     return -1;
64 }
65 
66 /**
67  * Function: checkNetlinkRouteGetneigh
68  * Purpose: Checks to see if RTM_GETNEIGH{TBL} is allowed on a netlink route socket.
69  * Returns: 3 (expected) if RTM_GETNEIGH and RTM_GETNEIGHTBL both fail with permission denied.
70  *          1 if only RTM_GETNEIGH fails with permission denied.
71  *          2 if only RTM_GETNEIGHTBL fails with permission denied.
72  *          0 if both succeed.
73  *          -1 if socket creation fails
74  */
checkNetlinkRouteGetneigh()75 static jint checkNetlinkRouteGetneigh() {
76     int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
77     if (sock < 0)
78     {
79         __android_log_print(ANDROID_LOG_ERROR, "SELinuxTargetSdkTest", "socket creation failed.");
80         return -1;
81     }
82     struct NetlinkMessage
83     {
84         nlmsghdr hdr;
85         rtgenmsg msg;
86     } request;
87     memset(&request, 0, sizeof(request));
88     request.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
89     request.hdr.nlmsg_len = sizeof(request);
90     request.msg.rtgen_family = AF_UNSPEC;
91 
92     int return_value = 0;
93 
94     request.hdr.nlmsg_type = RTM_GETNEIGH;
95     int ret = send(sock, &request, sizeof(request), 0);
96     if (ret < 0 && errno == 13)
97     {
98         return_value |= 1;
99     }
100 
101     request.hdr.nlmsg_type = RTM_GETNEIGHTBL;
102     ret = send(sock, &request, sizeof(request), 0);
103     if (ret < 0 && errno == 13)
104     {
105         return_value |= 1 << 1;
106     }
107 
108     return return_value;
109 }
110 
111 /**
112  * Function: checkNetlinkRouteBind
113  * Purpose: Checks to see if bind() is allowed on a netlink route socket.
114  * Returns: 13 (expected) if bind() fails with permission denied.
115  *          0 if socket creation fails
116  *          -1 if bind() succeeds.
117  */
checkNetlinkRouteBind()118 static jint checkNetlinkRouteBind() {
119     int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
120     if (sock < 0) {
121         __android_log_print(ANDROID_LOG_ERROR, "SELLinuxTargetSdkTest", "socket creation failed.");
122         return 0;
123     }
124 
125     struct sockaddr_nl addr;
126     addr.nl_family = AF_NETLINK;
127     addr.nl_pid = getpid();
128     addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR;
129 
130     int ret = bind(sock,(struct sockaddr *)&addr,sizeof(addr));
131     if (ret < 0) {
132         return errno;
133     }
134 
135     return -1;
136 }
137 
138 /*
139  * Function: getFileContext
140  * Purpose: retrieves the context associated with the given path in the file system
141  * Parameters:
142  *        path: given path in the file system
143  * Returns:
144  *        string representing the security context string of the file object
145  *        the string may be NULL if an error occured
146  * Exceptions: NullPointerException if the path object is null
147  */
getFileContext(JNIEnv * env,jobject,jstring pathStr)148 static jstring getFileContext(JNIEnv *env, jobject, jstring pathStr) {
149     ScopedUtfChars path(env, pathStr);
150     if (path.c_str() == NULL) {
151         return NULL;
152     }
153 
154     char* tmp = NULL;
155     int ret = getfilecon(path.c_str(), &tmp);
156     Unique_SecurityContext context(tmp);
157 
158     ScopedLocalRef<jstring> securityString(env, NULL);
159     if (ret != -1) {
160         securityString.reset(env->NewStringUTF(context.get()));
161     }
162 
163     return securityString.release();
164 }
165 
166 static JNINativeMethod gMethods[] = {
167     { "getFileContext", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getFileContext },
168     { "checkNetlinkRouteBind", "()I", (void*) checkNetlinkRouteBind },
169     { "checkNetlinkRouteGetlink", "()I", (void*) checkNetlinkRouteGetlink },
170     { "checkNetlinkRouteGetneigh", "()I", (void*) checkNetlinkRouteGetneigh },
171 };
172 
register_android_security_SELinuxTargetSdkTest(JNIEnv * env)173 int register_android_security_SELinuxTargetSdkTest(JNIEnv* env)
174 {
175     jclass clazz = env->FindClass("android/security/SELinuxTargetSdkTestBase");
176 
177     return env->RegisterNatives(clazz, gMethods,
178             sizeof(gMethods) / sizeof(JNINativeMethod));
179 }
180