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 // There are two ways to check whether a file has fs-verity enabled: statx() and FS_IOC_GETFLAGS
74 // (See https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#statx and
75 // https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#fs-ioc-getflags.)
76 // We try statx() first, since it doesn't require opening the file.
77
78 struct statx out = {};
79 if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
80 return -errno;
81 }
82
83 if (out.stx_attributes_mask & STATX_ATTR_VERITY) {
84 return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
85 }
86
87 // The filesystem doesn't support STATX_ATTR_VERITY. This normally means that it doesn't
88 // support fs-verity, in which case we should simply return 0. Unfortunately, virtio-fs is an
89 // exception, since it doesn't support STATX_ATTR_VERITY but does support querying FS_VERITY_FL
90 // via FS_IOC_GETFLAGS. So we have to fall back to FS_IOC_GETFLAGS. Note: despite being an
91 // ioctl, FS_IOC_GETFLAGS doesn't require the "ioctl" SELinux permission but rather "getattr".
92
93 ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
94 if (rfd.get() < 0) {
95 ALOGE("open failed at %s", path.c_str());
96 return -errno;
97 }
98
99 unsigned int flags;
100 if (ioctl(rfd.get(), FS_IOC_GETFLAGS, &flags) < 0) {
101 if (errno == ENOTTY) {
102 // If the filesystem supports neither STATX_ATTR_VERITY nor FS_IOC_GETFLAGS, then assume
103 // that it doesn't support fs-verity.
104 return 0;
105 }
106 ALOGE("ioctl(FS_IOC_GETFLAGS) failed at %s", path.c_str());
107 return -errno;
108 }
109
110 return (flags & FS_VERITY_FL) != 0;
111 }
112
measureFsverity(JNIEnv * env,jobject,jstring filePath,jbyteArray digest)113 int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) {
114 static constexpr auto kDigestSha256 = 32;
115 using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>;
116
117 Storage bytes;
118 fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
119 data->digest_size = kDigestSha256; // the only input/output parameter
120
121 ScopedUtfChars path(env, filePath);
122 ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
123 if (rfd.get() < 0) {
124 return -errno;
125 }
126 if (::ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
127 return -errno;
128 }
129
130 if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) {
131 return -EINVAL;
132 }
133
134 if (digest != nullptr && data->digest_size > 0) {
135 auto digestSize = env->GetArrayLength(digest);
136 if (data->digest_size > digestSize) {
137 return -E2BIG;
138 }
139 env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest);
140 }
141
142 return 0;
143 }
144 const JNINativeMethod sMethods[] = {
145 {"enableFsverityNative", "(Ljava/lang/String;)I", (void *)enableFsverity},
146 {"enableFsverityForFdNative", "(I)I", (void *)enableFsverityForFd},
147 {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
148 {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity},
149 };
150
151 } // namespace
152
register_com_android_internal_security_VerityUtils(JNIEnv * env)153 int register_com_android_internal_security_VerityUtils(JNIEnv *env) {
154 return jniRegisterNativeMethods(env, "com/android/internal/security/VerityUtils", sMethods,
155 NELEM(sMethods));
156 }
157
158 } // namespace android
159