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