1 /*
2  * Copyright (C) 2018 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 #define LOG_TAG "VerityUtils"
18 
19 #include <android-base/unique_fd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <linux/fs.h>
23 #include <linux/fsverity.h>
24 #include <linux/stat.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <nativehelper/ScopedUtfChars.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <utils/Log.h>
32 
33 #include <type_traits>
34 
35 #include "jni.h"
36 
37 namespace android {
38 
39 namespace {
40 
enableFsverityForFd(JNIEnv * env,jobject clazz,jint fd)41 int enableFsverityForFd(JNIEnv *env, jobject clazz, jint fd) {
42     if (fd < 0) {
43         return errno;
44     }
45 
46     fsverity_enable_arg arg = {};
47     arg.version = 1;
48     arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; // hardcoded in measureFsverity below
49     arg.block_size = 4096;
50     arg.salt_size = 0;
51     arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
52 
53     if (ioctl(fd, FS_IOC_ENABLE_VERITY, &arg) < 0) {
54         return errno;
55     }
56     return 0;
57 }
58 
enableFsverity(JNIEnv * env,jobject clazz,jstring filePath)59 int enableFsverity(JNIEnv *env, jobject clazz, jstring filePath) {
60     ScopedUtfChars path(env, filePath);
61     if (path.c_str() == nullptr) {
62         return EINVAL;
63     }
64     ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
65     return enableFsverityForFd(env, clazz, rfd.get());
66 }
67 
68 // Returns whether the file has fs-verity enabled.
69 // 0 if it is not present, 1 if is present, and -errno if there was an error.
statxForFsverity(JNIEnv * env,jobject,jstring filePath)70 int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) {
71     ScopedUtfChars path(env, filePath);
72 
73     // Call statx and check STATX_ATTR_VERITY.
74     struct statx out = {};
75     if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
76         return -errno;
77     }
78 
79     if (out.stx_attributes_mask & STATX_ATTR_VERITY) {
80         return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
81     }
82 
83     // STATX_ATTR_VERITY is not supported for the file path.
84     // In this case, call ioctl(FS_IOC_GETFLAGS) and check FS_VERITY_FL.
85     ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
86     if (rfd.get() < 0) {
87         ALOGE("open failed at %s", path.c_str());
88         return -errno;
89     }
90 
91     unsigned int flags;
92     if (ioctl(rfd.get(), FS_IOC_GETFLAGS, &flags) < 0) {
93         ALOGE("ioctl(FS_IOC_GETFLAGS) failed at %s", path.c_str());
94         return -errno;
95     }
96 
97     return (flags & FS_VERITY_FL) != 0;
98 }
99 
measureFsverity(JNIEnv * env,jobject,jstring filePath,jbyteArray digest)100 int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) {
101     static constexpr auto kDigestSha256 = 32;
102     using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>;
103 
104     Storage bytes;
105     fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
106     data->digest_size = kDigestSha256; // the only input/output parameter
107 
108     ScopedUtfChars path(env, filePath);
109     ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
110     if (rfd.get() < 0) {
111         return -errno;
112     }
113     if (::ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
114         return -errno;
115     }
116 
117     if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) {
118         return -EINVAL;
119     }
120 
121     if (digest != nullptr && data->digest_size > 0) {
122         auto digestSize = env->GetArrayLength(digest);
123         if (data->digest_size > digestSize) {
124             return -E2BIG;
125         }
126         env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest);
127     }
128 
129     return 0;
130 }
131 const JNINativeMethod sMethods[] = {
132         {"enableFsverityNative", "(Ljava/lang/String;)I", (void *)enableFsverity},
133         {"enableFsverityForFdNative", "(I)I", (void *)enableFsverityForFd},
134         {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
135         {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity},
136 };
137 
138 } // namespace
139 
register_com_android_internal_security_VerityUtils(JNIEnv * env)140 int register_com_android_internal_security_VerityUtils(JNIEnv *env) {
141     return jniRegisterNativeMethods(env, "com/android/internal/security/VerityUtils", sMethods,
142                                     NELEM(sMethods));
143 }
144 
145 } // namespace android
146