├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── WORKSPACE └── pathauditor ├── BUILD ├── file_event.cc ├── file_event.h ├── file_event_test.cc ├── libc ├── BUILD ├── logging.cc ├── logging.h ├── path_auditor_libc.cc └── template_generator.py ├── pathauditor.cc ├── pathauditor.h ├── process_information.cc ├── process_information.h └── util ├── BUILD ├── cleanup.h ├── flag.h ├── path.cc ├── path.h ├── status_macros.h ├── status_macros_test.cc ├── status_matchers.h ├── strerror.cc └── strerror.h /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual) 6 | (CLA), which you can do online. The CLA is necessary mainly because you own the 7 | copyright to your changes, even after your contribution becomes part of our 8 | codebase, so we need your permission to use and distribute your code. We also 9 | need to be sure of various other things—for instance that you'll tell us if you 10 | know that your code infringes on other people's patents. You don't have to sign 11 | the CLA until after you've submitted your code for review and a member has 12 | approved it, but you must do it before we can put your code into our codebase. 13 | Before you start working on a larger contribution, you should get in touch with 14 | us first through the issue tracker with your idea so that we can help out and 15 | possibly guide you. Coordinating up front makes it much easier to avoid 16 | frustration later on. 17 | 18 | ### Code reviews 19 | All submissions, including submissions by project members, require review. We 20 | use Github pull requests for this purpose. 21 | 22 | ### The small print 23 | Contributions made by corporations are covered by a different agreement than 24 | the one above, the 25 | [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:19.04 2 | 3 | # We log to syslog, install it 4 | RUN apt-get update && apt-get -y install syslog-ng 5 | 6 | # Install bazel 7 | RUN apt-get -y install pkg-config zip g++ zlib1g-dev unzip python3 wget 8 | RUN wget https://github.com/bazelbuild/bazel/releases/download/1.1.0/bazel-1.1.0-installer-linux-x86_64.sh && chmod u+x bazel-1.1.0-installer-linux-x86_64.sh && ./bazel-1.1.0-installer-linux-x86_64.sh 9 | 10 | COPY . /pathauditor/ 11 | 12 | RUN cd /pathauditor && bazel build //pathauditor/libc:libpath_auditor.so 13 | 14 | CMD service syslog-ng start && /bin/bash 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PathAuditor 2 | 3 | The PathAuditor is a tool meant to find file access related vulnerabilities by 4 | auditing libc functions. 5 | 6 | The idea is roughly as follows: 7 | 8 | * Audit every call to filesystem related libc functions performed by the 9 | binary. 10 | * Check if the path used in the syscall is user-writable. In this case an 11 | unprivileged user could have replaced a directory or file with a symlink. 12 | * Log all violations as potential vulnerabilities. 13 | 14 | We're using LD\_PRELOAD to hook all filesystem related library calls and log 15 | any encountered violations to syslog. 16 | 17 | This is not an officially supported Google product. 18 | 19 | ## Example Vulnerability 20 | 21 | Let's look at an example of the kind of vulnerability that this tool can detect. 22 | [CVE-2019-3461](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-3461) 23 | was a bug in tmpreaper, a tool that traverses /tmp and deletes old files. It's 24 | usually run as a cron job as root. Since it doesn't want to delete files 25 | outside of tmp, it was using the following code to check if a directory is a 26 | mount point: 27 | 28 | ```c 29 | if (S_ISDIR (sb.st_mode)) { 30 | char *dst; 31 | 32 | if ((dst = malloc(strlen(ent->d_name) + 3)) == NULL) 33 | message (LOG_FATAL, "malloc failed.\n"); 34 | strcpy(dst, ent->d_name); 35 | strcat(dst, "/X"); 36 | rename(ent->d_name, dst); 37 | if (errno == EXDEV) { 38 | free(dst); 39 | message (LOG_VERBOSE, 40 | "File on different device skipped: `%s/%s'\n", 41 | dirname, ent->d_name); 42 | continue; 43 | } 44 | // [...] 45 | ``` 46 | 47 | In short, this code calls `rename("/tmp/foo", "/tmp/foo/x")` which will return 48 | `EXDEV` if `"/tmp/foo"` is a mount point. 49 | PathAuditor would flag this call as a potential vulnerability if `"/tmp/foo"` 50 | is owned by any user except root. 51 | To understand why, we have to think about what happens in the kernel when the 52 | rename syscall is executed (simplified): 53 | 54 | 1. The kernel traverses the path `"/tmp/foo"` for the first argument. 55 | 2. The kernel traverses the path `"/tmp/foo/x"` for the second argument. 56 | 3. If the source and target are on different filesystems, return EXDEV. 57 | 4. Otherwise, move the file from the first to the second directory. 58 | 59 | There's a race condition here since `"/tmp/foo"` will be resolved twice. If it's 60 | user-controlled, the user can replace it with a different file at any point in 61 | time. 62 | In particular, we want `"/tmp/foo"` to be a directory at first to pass the 63 | `if(S_ISDIR)` check in the tmpreaper code. We then replace it with a file just 64 | before the code enters the syscall. When the kernel resolves the first argument, 65 | it will see a file with user-controlled content. 66 | Now we replace it again, this time with a symlink to an arbitrary directory on 67 | the same filesystem. The kernel will resolve the path a second time, follow the 68 | symlink and move the controlled file to a chosen directory. 69 | 70 | The same filesystem restriction is because rename does not work between 71 | filesystems. But on some Linux distributions /tmp is just a folder on the rootfs 72 | by default and you could use this bug to move a file to /etc/cron, which will 73 | get executed as root. 74 | 75 | ## How to run 76 | 77 | To try it out, you need to build libpath\_auditor.so with [bazel](https://bazel.build) 78 | and load it into a binary using LD\_PRELOAD. Any violations will be logged to 79 | syslog, so make sure that you have it running. 80 | 81 | ```sh 82 | bazel build //pathauditor/libc:libpath_auditor.so 83 | LD_PRELOAD=/path/to/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar 84 | tail /var/log/syslog 85 | ``` 86 | 87 | It's also possible to run this on all processes on the system by adding it to 88 | /etc/ld.so.preload. Though be warned that this is only recommended on test 89 | systems as it can lead to instability. 90 | 91 | As a quickstart, you can try out the docker container shipped with this project: 92 | 93 | ```sh 94 | docker build -t pathauditor-example . 95 | docker run -it pathauditor-example 96 | # LD_PRELOAD=/pathauditor/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar 97 | # cat /var/log/syslog 98 | ``` 99 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 16 | 17 | # Absl 18 | http_archive( 19 | name = "com_google_absl", 20 | sha256 = "306639352ec60dcbfc695405e989e1f63d0e55001582a5185b0a8caf2e8ea9ca", # 2019-07-18 21 | strip_prefix = "abseil-cpp-20200923.2", 22 | urls = ["https://github.com/abseil/abseil-cpp/archive/20200923.2.zip"], 23 | ) 24 | 25 | # GoogleTest/GoogleMock 26 | http_archive( 27 | name = "com_google_googletest", 28 | sha256 = "baed63b97595c32667694de0a434f8f23da59609c4a44f3360ba94b0abd5c583", 29 | strip_prefix = "googletest-8ffb7e5c88b20a297a2e786c480556467496463b", 30 | urls = ["https://github.com/google/googletest/archive/8ffb7e5c88b20a297a2e786c480556467496463b.zip"], # 2019-05-30 31 | ) 32 | 33 | # gflags 34 | http_archive( 35 | name = "com_github_gflags_gflags", 36 | sha256 = "53b16091efa386ab11e33f018eef0ed489e0ab63554455293cbb0cc2a5f50e98", 37 | strip_prefix = "gflags-28f50e0fed19872e0fd50dd23ce2ee8cd759338e", 38 | urls = ["https://github.com/gflags/gflags/archive/28f50e0fed19872e0fd50dd23ce2ee8cd759338e.zip"], # 2019-01-25 39 | ) 40 | 41 | # Google logging 42 | http_archive( 43 | name = "com_google_glog", 44 | sha256 = "74010e549e3555a11d3eb22b80f0040fa4f013a4b254b2d5ede12afcc92e690b", 45 | strip_prefix = "glog-41f4bf9cbc3e8995d628b459f6a239df43c2b84a", 46 | urls = ["https://github.com/google/glog/archive/41f4bf9cbc3e8995d628b459f6a239df43c2b84a.zip"], # 2019-02-02 47 | ) 48 | -------------------------------------------------------------------------------- /pathauditor/BUILD: -------------------------------------------------------------------------------- 1 | # The pathauditor is a project meant to detect insecure file access patterns 2 | # performed by privileged processes. 3 | 4 | package(default_visibility = ["//visibility:public"]) 5 | 6 | licenses(["notice"]) 7 | 8 | exports_files(["LICENSE"]) 9 | 10 | cc_library( 11 | name = "pathauditor", 12 | srcs = ["pathauditor.cc"], 13 | hdrs = ["pathauditor.h"], 14 | deps = [ 15 | ":file_event", 16 | ":process_information", 17 | "//pathauditor/util:cleanup", 18 | "//pathauditor/util:path", 19 | "//pathauditor/util:status_macros", 20 | "@com_google_absl//absl/container:fixed_array", 21 | "@com_google_absl//absl/status:statusor", 22 | "@com_google_absl//absl/strings", 23 | "@com_google_absl//absl/types:optional", 24 | "@com_google_glog//:glog", 25 | ], 26 | ) 27 | 28 | cc_library( 29 | name = "process_information", 30 | srcs = ["process_information.cc"], 31 | hdrs = ["process_information.h"], 32 | deps = [ 33 | "//pathauditor/util:path", 34 | "//pathauditor/util:status_macros", 35 | "@com_google_absl//absl/status:statusor", 36 | "@com_google_absl//absl/strings", 37 | "@com_google_absl//absl/types:optional", 38 | ], 39 | ) 40 | 41 | cc_library( 42 | name = "file_event", 43 | srcs = ["file_event.cc"], 44 | hdrs = ["file_event.h"], 45 | deps = [ 46 | "@com_google_absl//absl/status:statusor", 47 | "@com_google_absl//absl/strings", 48 | ], 49 | ) 50 | 51 | cc_test( 52 | name = "file_event_test", 53 | srcs = ["file_event_test.cc"], 54 | deps = [ 55 | ":file_event", 56 | "//pathauditor/util:status_matchers", 57 | "@com_google_absl//absl/status", 58 | "@com_google_googletest//:gtest_main", 59 | ], 60 | ) 61 | -------------------------------------------------------------------------------- /pathauditor/file_event.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/file_event.h" 16 | 17 | #include "absl/status/statusor.h" 18 | #include "absl/strings/string_view.h" 19 | 20 | namespace pathauditor { 21 | 22 | absl::StatusOr FileEvent::Arg(size_t idx) const { 23 | if (idx >= args.size()) { 24 | return absl::OutOfRangeError( 25 | absl::StrCat("Index ", idx, " out of range (size ", args.size(), ").")); 26 | } 27 | return args[idx]; 28 | } 29 | 30 | absl::StatusOr FileEvent::PathArg(size_t idx) const { 31 | if (idx >= path_args.size()) { 32 | return absl::OutOfRangeError(absl::StrCat( 33 | "Index ", idx, " out of range (size ", path_args.size(), ").")); 34 | } 35 | return path_args[idx]; 36 | } 37 | 38 | } // namespace pathauditor 39 | -------------------------------------------------------------------------------- /pathauditor/file_event.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_FILE_EVENT_H_ 16 | #define PATHAUDITOR_FILE_EVENT_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/status/statusor.h" 23 | #include "absl/strings/str_join.h" 24 | #include "absl/strings/string_view.h" 25 | 26 | namespace pathauditor { 27 | 28 | // The FileEvent represents a fs-related syscall (open/rename/...). 29 | struct FileEvent { 30 | FileEvent(int syscall_nr, const std::vector& args, 31 | const std::vector& path_args) 32 | : syscall_nr(syscall_nr), args(args), path_args(path_args) {} 33 | 34 | int syscall_nr; 35 | std::vector args; 36 | std::vector path_args; 37 | 38 | absl::StatusOr Arg(size_t idx) const; 39 | absl::StatusOr PathArg(size_t idx) const; 40 | }; 41 | 42 | inline std::ostream& operator<<(std::ostream& os, FileEvent e) { 43 | return os << "syscall_nr: " << e.syscall_nr << ", args: [" 44 | << absl::StrJoin(e.args, ", ") << "], " 45 | << "path_args: [" << absl::StrJoin(e.path_args, ", ") << "]"; 46 | } 47 | 48 | } // namespace pathauditor 49 | 50 | #endif // PATHAUDITOR_FILE_EVENT_H_ 51 | -------------------------------------------------------------------------------- /pathauditor/file_event_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/file_event.h" 16 | 17 | #include 18 | 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | #include "absl/status/status.h" 22 | #include "pathauditor/util/status_matchers.h" 23 | 24 | namespace pathauditor { 25 | namespace { 26 | 27 | using ::testing::Eq; 28 | using pathauditor::IsOkAndHolds; 29 | using pathauditor::StatusIs; 30 | 31 | TEST(FileEventTest, ReturnsSyscallNr) { 32 | FileEvent event(SYS_open, {0}, {"/foo"}); 33 | EXPECT_THAT(event.syscall_nr, Eq(SYS_open)); 34 | } 35 | 36 | TEST(FileEventTest, ArgumentAccess) { 37 | FileEvent event(SYS_open, {10, 20}, {"/foo", "/bar"}); 38 | EXPECT_THAT(event.Arg(0), IsOkAndHolds(10)); 39 | EXPECT_THAT(event.Arg(1), IsOkAndHolds(20)); 40 | EXPECT_THAT(event.PathArg(0), IsOkAndHolds("/foo")); 41 | EXPECT_THAT(event.PathArg(1), IsOkAndHolds("/bar")); 42 | } 43 | 44 | TEST(FileEventTest, EmptyArguments) { 45 | FileEvent event(SYS_open, {}, {}); 46 | EXPECT_THAT(event.Arg(0), StatusIs(absl::StatusCode::kOutOfRange)); 47 | EXPECT_THAT(event.PathArg(0), StatusIs(absl::StatusCode::kOutOfRange)); 48 | } 49 | 50 | TEST(FileEventTest, NegativeIndex) { 51 | FileEvent event(SYS_open, {0, 0}, {"/foo", "/bar"}); 52 | EXPECT_THAT(event.Arg(-1), StatusIs(absl::StatusCode::kOutOfRange)); 53 | EXPECT_THAT(event.PathArg(-1), StatusIs(absl::StatusCode::kOutOfRange)); 54 | } 55 | 56 | } // namespace 57 | } // namespace pathauditor 58 | -------------------------------------------------------------------------------- /pathauditor/libc/BUILD: -------------------------------------------------------------------------------- 1 | # LD_PRELOAD based path auditor for libc. 2 | 3 | licenses(["notice"]) 4 | 5 | cc_binary( 6 | name = "libpath_auditor.so", 7 | srcs = ["path_auditor_libc.cc"], 8 | linkopts = ["-ldl"], 9 | linkshared = True, 10 | visibility = ["//visibility:public"], 11 | deps = [ 12 | ":logging", 13 | "@com_google_absl//absl/status:statusor", 14 | "//pathauditor", 15 | "//pathauditor:file_event", 16 | "//pathauditor:process_information", 17 | "//pathauditor/util:status_macros", 18 | ], 19 | ) 20 | 21 | cc_library( 22 | name = "logging", 23 | srcs = ["logging.cc"], 24 | hdrs = ["logging.h"], 25 | deps = [ 26 | "@com_google_absl//absl/debugging:stacktrace", 27 | "@com_google_absl//absl/debugging:symbolize", 28 | "@com_google_absl//absl/status", 29 | "@com_google_absl//absl/strings:str_format", 30 | "//pathauditor:file_event", 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /pathauditor/libc/logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/libc/logging.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "absl/debugging/stacktrace.h" 28 | #include "absl/debugging/symbolize.h" 29 | #include "absl/strings/str_format.h" 30 | 31 | namespace { 32 | 33 | static const size_t kCmdlineMax = 1024; 34 | static const size_t kMaxStackFrames = 20; 35 | static const size_t kMaxSymbolLen = 64; 36 | 37 | static const std::string &GetCmdline() { 38 | static std::string cmdline; 39 | 40 | if (!cmdline.empty()) { 41 | return cmdline; 42 | } 43 | 44 | int fd = syscall(SYS_open, "/proc/self/cmdline", O_RDONLY); 45 | if (fd == -1) { 46 | cmdline = "(unknown)"; 47 | return cmdline; 48 | } 49 | char cmdline_buf[kCmdlineMax]; 50 | ssize_t bytes = read(fd, cmdline_buf, sizeof(cmdline_buf) - 1); 51 | close(fd); 52 | if (bytes == -1) { 53 | cmdline = "(unknown)"; 54 | return cmdline; 55 | } 56 | cmdline_buf[bytes] = 0; 57 | 58 | // /proc/self/cmdline separates arguments with null bytes. Turn them into 59 | // spaces. 60 | for (ssize_t i = 0; i < bytes - 1; i++) { 61 | if (cmdline_buf[i] == '\x00') { 62 | cmdline_buf[i] = ' '; 63 | } 64 | } 65 | cmdline = cmdline_buf; 66 | 67 | return cmdline; 68 | } 69 | 70 | static int GetUid() { return syscall(SYS_getuid); } 71 | 72 | static std::string CurrentStackTrace() { 73 | void *stack_trace[kMaxStackFrames]; 74 | int frame_cnt = absl::GetStackTrace(stack_trace, kMaxStackFrames, 2); 75 | 76 | std::vector stack_trace_lines; 77 | for (int i = 0; i < frame_cnt; i++) { 78 | char tmp[kMaxSymbolLen] = ""; 79 | const char *symbol = "(unknown)"; 80 | if (absl::Symbolize(stack_trace[i], tmp, sizeof(tmp))) { 81 | symbol = tmp; 82 | } 83 | stack_trace_lines.push_back(absl::StrCat( 84 | " ", absl::Hex(stack_trace[i], absl::kZeroPad12), " ", symbol)); 85 | } 86 | 87 | return absl::StrJoin(stack_trace_lines, "\n"); 88 | } 89 | 90 | } // namespace 91 | 92 | namespace pathauditor { 93 | 94 | void LogInsecureAccess(const FileEvent &event, const char *function_name) { 95 | // for testing that functions get audited 96 | const char *env_p = std::getenv("PATHAUDITOR_TEST"); 97 | if (env_p) { 98 | std::cerr << "AUDITING:" << function_name << std::endl; 99 | return; 100 | } 101 | 102 | openlog("pathauditor", LOG_PID, 0); 103 | 104 | std::string args = absl::StrJoin(event.args, ", "); 105 | std::string path_args = absl::StrJoin(event.path_args, ", "); 106 | std::string event_info = absl::StrFormat( 107 | "function %s, cmdline %s, syscall_nr %d, args %s, path args %s, uid %d, " 108 | "stack trace:\n%s", 109 | function_name, GetCmdline(), event.syscall_nr, args, path_args, GetUid(), 110 | CurrentStackTrace()); 111 | 112 | syslog(LOG_WARNING, "InsecureAccess: %s", event_info.c_str()); 113 | } 114 | 115 | void LogError(const absl::Status &status) { 116 | openlog("pathauditor", LOG_PID, 0); 117 | syslog(LOG_WARNING, "Cannot audit: %s", 118 | std::string(status.message()).c_str()); 119 | } 120 | 121 | } // namespace pathauditor 122 | -------------------------------------------------------------------------------- /pathauditor/libc/logging.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_LIBC_LOGGING_H_ 16 | #define PATHAUDITOR_LIBC_LOGGING_H_ 17 | 18 | #include "absl/status/status.h" 19 | #include "pathauditor/file_event.h" 20 | 21 | namespace pathauditor { 22 | 23 | void LogInsecureAccess(const FileEvent &event, const char *function_name); 24 | 25 | void LogError(const absl::Status &status); 26 | 27 | } // namespace pathauditor 28 | 29 | #endif // PATHAUDITOR_LIBC_LOGGING_H_ 30 | -------------------------------------------------------------------------------- /pathauditor/libc/path_auditor_libc.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "absl/status/statusor.h" 29 | #include "pathauditor/file_event.h" 30 | #include "pathauditor/libc/logging.h" 31 | #include "pathauditor/pathauditor.h" 32 | #include "pathauditor/process_information.h" 33 | #include "pathauditor/util/status_macros.h" 34 | 35 | typedef int (*orig_open_type)(const char *file, int oflag, ...); 36 | typedef int (*orig_open64_type)(const char *file, int oflag, ...); 37 | typedef int (*orig_openat_type)(int dirfd, const char *file, int oflag, ...); 38 | typedef int (*orig_openat64_type)(int dirfd, const char *file, int oflag, ...); 39 | typedef int (*orig_creat_type)(const char *file, mode_t mode); 40 | typedef int (*orig_creat64_type)(const char *file, mode_t mode); 41 | typedef int (*orig_chdir_type)(const char *path); 42 | typedef int (*orig_chmod_type)(const char *file, mode_t mode); 43 | typedef int (*orig_chown_type)(const char *file, uid_t owner, gid_t group); 44 | typedef int (*orig_execv_type)(const char *path, char *const argv[]); 45 | typedef int (*orig_execl_type)(const char *path, const char *arg, ...); 46 | typedef int (*orig_execve_type)(const char *path, char *const argv[], 47 | char *const envp[]); 48 | typedef int (*orig_execle_type)(const char *path, const char *arg, ...); 49 | typedef int (*orig_execvp_type)(const char *file, char *const argv[]); 50 | typedef int (*orig_execlp_type)(const char *file, const char *arg, ...); 51 | typedef FILE *(*orig_fopen_type)(const char *filename, const char *modes); 52 | typedef FILE *(*orig_fopen64_type)(const char *filename, const char *modes); 53 | typedef FILE *(*orig_freopen_type)(const char *filename, const char *modes, 54 | FILE *stream); 55 | typedef FILE *(*orig_freopen64_type)(const char *filename, const char *modes, 56 | FILE *stream); 57 | typedef int (*orig_truncate_type)(const char *file, off_t length); 58 | typedef int (*orig_truncate64_type)(const char *file, off64_t length); 59 | typedef int (*orig_mkdir_type)(const char *path, mode_t mode); 60 | typedef int (*orig_mkdirat_type)(int fd, const char *path, mode_t mode); 61 | typedef int (*orig_link_type)(const char *from, const char *to); 62 | typedef int (*orig_linkat_type)(int fromfd, const char *from, int tofd, 63 | const char *to, int flags); 64 | typedef int (*orig_unlink_type)(const char *name); 65 | typedef int (*orig_unlinkat_type)(int dirfd, const char *name, int flags); 66 | typedef int (*orig_remove_type)(const char *filename); 67 | typedef int (*orig_rmdir_type)(const char *path); 68 | typedef int (*orig_mount_type)(const char *special_file, const char *dir, 69 | const char *fstype, unsigned rwflag, void *data); 70 | typedef int (*orig_umount_type)(const char *special_file); 71 | typedef int (*orig_umount2_type)(const char *special_file, int flags); 72 | typedef int (*orig_rename_type)(const char *oldpath, const char *newpath); 73 | typedef int (*orig_renameat_type)(int olddirfd, const char *oldpath, 74 | int newdirfd, const char *newpath); 75 | typedef int (*orig_symlink_type)(const char *from, const char *to); 76 | typedef int (*orig_symlinkat_type)(const char *from, int newdirfd, 77 | const char *to); 78 | typedef int (*orig_lchown_type)(const char *file, uid_t owner, gid_t group); 79 | typedef int (*orig_chroot_type)(const char *path); 80 | typedef int (*orig_fchmodat_type)(int fd, const char *file, mode_t mode, 81 | int flag); 82 | typedef int (*orig_fchownat_type)(int fd, const char *file, uid_t owner, 83 | gid_t group, int flag); 84 | 85 | namespace { 86 | 87 | std::atomic mallocInitialized = {false}; 88 | 89 | __attribute__((constructor)) 90 | void ensureMallocInitialized() { 91 | free(malloc(1)); 92 | mallocInitialized.store(true, std::memory_order_release); 93 | } 94 | 95 | } // namespace 96 | 97 | namespace pathauditor { 98 | 99 | // boolean var to avoid sanitizing function calls made while handling another 100 | // call; otherwise results in infinite recursion 101 | ABSL_CONST_INIT thread_local bool sanitizing = false; 102 | 103 | void LibcFileEventIsUserControlled(const FileEvent &file_event, 104 | const char *function_name) { 105 | if (sanitizing) { 106 | return; 107 | } 108 | sanitizing = true; 109 | 110 | absl::StatusOr result = 111 | FileEventIsUserControlled(SameProcessInformation(), file_event); 112 | if (!result.ok()) { 113 | LogError(result.status()); 114 | } else if (*result) { 115 | LogInsecureAccess(file_event, function_name); 116 | } 117 | 118 | sanitizing = false; 119 | } 120 | 121 | } // namespace pathauditor 122 | 123 | extern "C" { 124 | int open(const char *file, int oflag, ...) { 125 | mode_t mode = 0; 126 | if (oflag & O_CREAT || oflag & O_TMPFILE) { 127 | va_list args; 128 | va_start(args, oflag); 129 | mode = va_arg(args, mode_t); 130 | va_end(args); 131 | } 132 | 133 | if (!mallocInitialized.load(std::memory_order_acquire)) { 134 | return syscall(SYS_open, file, oflag, mode); 135 | } 136 | 137 | std::vector path_args = {file}; 138 | std::vector args = {0, static_cast(oflag), mode}; 139 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 140 | 141 | pathauditor::LibcFileEventIsUserControlled(file_event, "open"); 142 | 143 | orig_open_type orig_open; 144 | orig_open = reinterpret_cast(dlsym(RTLD_NEXT, "open")); 145 | return orig_open(file, oflag, mode); 146 | } 147 | 148 | int open64(const char *file, int oflag, ...) { 149 | mode_t mode = 0; 150 | if (oflag & O_CREAT || oflag & O_TMPFILE) { 151 | va_list args; 152 | va_start(args, oflag); 153 | mode = va_arg(args, mode_t); 154 | va_end(args); 155 | } 156 | 157 | std::vector path_args = {file}; 158 | std::vector args = {0, static_cast(oflag), mode}; 159 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 160 | 161 | pathauditor::LibcFileEventIsUserControlled(file_event, "open64"); 162 | orig_open64_type orig_open64; 163 | orig_open64 = reinterpret_cast(dlsym(RTLD_NEXT, "open64")); 164 | return orig_open64(file, oflag, mode); 165 | } 166 | 167 | int openat(int dirfd, const char *file, int oflag, ...) { 168 | mode_t mode = 0; 169 | if (oflag & O_CREAT || oflag & O_TMPFILE) { 170 | va_list args; 171 | va_start(args, oflag); 172 | mode = va_arg(args, mode_t); 173 | va_end(args); 174 | } 175 | 176 | std::vector path_args = {file}; 177 | std::vector args = {static_cast(dirfd), 0, 178 | static_cast(oflag), mode}; 179 | pathauditor::FileEvent file_event(SYS_openat, args, path_args); 180 | 181 | pathauditor::LibcFileEventIsUserControlled(file_event, "openat"); 182 | orig_openat_type orig_openat; 183 | orig_openat = reinterpret_cast(dlsym(RTLD_NEXT, "openat")); 184 | return orig_openat(dirfd, file, oflag, mode); 185 | } 186 | 187 | int openat64(int dirfd, const char *file, int oflag, ...) { 188 | mode_t mode = 0; 189 | if (oflag & O_CREAT || oflag & O_TMPFILE) { 190 | va_list args; 191 | va_start(args, oflag); 192 | mode = va_arg(args, mode_t); 193 | va_end(args); 194 | } 195 | 196 | std::vector path_args = {file}; 197 | std::vector args = {static_cast(dirfd), 0, 198 | static_cast(oflag), mode}; 199 | pathauditor::FileEvent file_event(SYS_openat, args, path_args); 200 | 201 | pathauditor::LibcFileEventIsUserControlled(file_event, "openat64"); 202 | orig_openat64_type orig_openat64; 203 | orig_openat64 = 204 | reinterpret_cast(dlsym(RTLD_NEXT, "openat64")); 205 | return orig_openat64(dirfd, file, oflag, mode); 206 | } 207 | 208 | int creat(const char *file, mode_t mode) { 209 | std::vector path_args = {file}; 210 | uint64_t flags = O_CREAT | O_WRONLY | O_TRUNC; 211 | std::vector args = {0, flags, mode}; 212 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 213 | 214 | pathauditor::LibcFileEventIsUserControlled(file_event, "creat"); 215 | 216 | orig_creat_type orig_creat; 217 | orig_creat = reinterpret_cast(dlsym(RTLD_NEXT, "creat")); 218 | return orig_creat(file, mode); 219 | } 220 | 221 | int creat64(const char *file, mode_t mode) { 222 | std::vector path_args = {file}; 223 | uint64_t flags = O_CREAT | O_WRONLY | O_TRUNC; 224 | std::vector args = {0, flags, mode}; 225 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 226 | 227 | pathauditor::LibcFileEventIsUserControlled(file_event, "creat64"); 228 | 229 | orig_creat64_type orig_creat64; 230 | orig_creat64 = 231 | reinterpret_cast(dlsym(RTLD_NEXT, "creat64")); 232 | return orig_creat64(file, mode); 233 | } 234 | 235 | int chdir(const char *path) { 236 | std::vector path_args = {path}; 237 | std::vector args = {0}; 238 | pathauditor::FileEvent file_event(SYS_chdir, args, path_args); 239 | 240 | pathauditor::LibcFileEventIsUserControlled(file_event, "chdir"); 241 | 242 | orig_chdir_type orig_chdir; 243 | orig_chdir = reinterpret_cast(dlsym(RTLD_NEXT, "chdir")); 244 | return orig_chdir(path); 245 | } 246 | 247 | int chmod(const char *file, mode_t mode) { 248 | std::vector path_args = {file}; 249 | std::vector args = {0, mode}; 250 | pathauditor::FileEvent file_event(SYS_chmod, args, path_args); 251 | 252 | pathauditor::LibcFileEventIsUserControlled(file_event, "chmod"); 253 | 254 | orig_chmod_type orig_chmod; 255 | orig_chmod = reinterpret_cast(dlsym(RTLD_NEXT, "chmod")); 256 | return orig_chmod(file, mode); 257 | } 258 | 259 | int fchmodat(int fd, const char *file, mode_t mode, int flag) { 260 | std::vector path_args = {file}; 261 | std::vector args = {static_cast(fd), 0, mode, 262 | static_cast(flag)}; 263 | pathauditor::FileEvent file_event(SYS_fchmodat, args, path_args); 264 | 265 | pathauditor::LibcFileEventIsUserControlled(file_event, "fchmodat"); 266 | 267 | orig_fchmodat_type orig_fchmodat; 268 | orig_fchmodat = 269 | reinterpret_cast(dlsym(RTLD_NEXT, "fchmodat")); 270 | return orig_fchmodat(fd, file, mode, flag); 271 | } 272 | 273 | int chown(const char *file, uid_t owner, gid_t group) { 274 | std::vector path_args = {file}; 275 | std::vector args = {0, owner, group}; 276 | pathauditor::FileEvent file_event{SYS_chown, args, path_args}; 277 | 278 | pathauditor::LibcFileEventIsUserControlled(file_event, "chown"); 279 | 280 | orig_chown_type orig_chown; 281 | orig_chown = reinterpret_cast(dlsym(RTLD_NEXT, "chown")); 282 | return orig_chown(file, owner, group); 283 | } 284 | 285 | int lchown(const char *file, uid_t owner, gid_t group) { 286 | std::vector path_args = {file}; 287 | std::vector args = {0, owner, group}; 288 | pathauditor::FileEvent file_event{SYS_lchown, args, path_args}; 289 | 290 | pathauditor::LibcFileEventIsUserControlled(file_event, "lchown"); 291 | 292 | orig_lchown_type orig_lchown; 293 | orig_lchown = reinterpret_cast(dlsym(RTLD_NEXT, "lchown")); 294 | return orig_lchown(file, owner, group); 295 | } 296 | 297 | int fchownat(int fd, const char *file, uid_t owner, gid_t group, int flag) { 298 | std::vector path_args = {file}; 299 | std::vector args = {static_cast(fd), 0, owner, group, 300 | static_cast(flag)}; 301 | pathauditor::FileEvent file_event{SYS_fchownat, args, path_args}; 302 | 303 | pathauditor::LibcFileEventIsUserControlled(file_event, "fchownat"); 304 | 305 | orig_fchownat_type orig_fchownat; 306 | orig_fchownat = 307 | reinterpret_cast(dlsym(RTLD_NEXT, "fchownat")); 308 | return orig_fchownat(fd, file, owner, group, flag); 309 | } 310 | 311 | int execl(const char *path, const char *arg, ...) { 312 | std::vector argv; 313 | argv.push_back(const_cast(arg)); 314 | 315 | va_list va_args; 316 | va_start(va_args, arg); 317 | char *va_arg; 318 | do { 319 | va_arg = va_arg(va_args, char *); 320 | argv.push_back(va_arg); 321 | } while (va_arg != nullptr); 322 | va_end(va_args); 323 | 324 | std::vector path_args = {path}; 325 | std::vector args = {0, reinterpret_cast(&argv[0]), 0}; 326 | pathauditor::FileEvent file_event{SYS_execve, args, path_args}; 327 | pathauditor::LibcFileEventIsUserControlled(file_event, "execl"); 328 | 329 | // cannot call execl with variable args; call execve instead 330 | orig_execve_type orig_execve; 331 | orig_execve = reinterpret_cast(dlsym(RTLD_NEXT, "execve")); 332 | return orig_execve(path, &argv[0], nullptr); 333 | } 334 | 335 | int execv(const char *path, char *const argv[]) { 336 | std::vector path_args = {path}; 337 | std::vector args = {0, reinterpret_cast(argv), 0}; 338 | pathauditor::FileEvent file_event{SYS_execve, args, path_args}; 339 | 340 | pathauditor::LibcFileEventIsUserControlled(file_event, "execv"); 341 | 342 | orig_execv_type orig_execv; 343 | orig_execv = reinterpret_cast(dlsym(RTLD_NEXT, "execv")); 344 | return orig_execv(path, argv); 345 | } 346 | 347 | int execve(const char *path, char *const argv[], char *const envp[]) { 348 | std::vector path_args = {path}; 349 | std::vector args = {0, reinterpret_cast(argv), 350 | reinterpret_cast(envp)}; 351 | pathauditor::FileEvent file_event{SYS_execve, args, path_args}; 352 | 353 | pathauditor::LibcFileEventIsUserControlled(file_event, "execve"); 354 | 355 | orig_execve_type orig_execve; 356 | orig_execve = reinterpret_cast(dlsym(RTLD_NEXT, "execve")); 357 | return orig_execve(path, argv, envp); 358 | } 359 | 360 | int execle(const char *path, const char *arg, ...) { 361 | // same as execl but last argument is envp 362 | orig_execle_type orig_execle; 363 | orig_execle = reinterpret_cast(dlsym(RTLD_NEXT, "execle")); 364 | return orig_execle(path, arg); 365 | } 366 | 367 | int execvp(const char *file, char *const argv[]) { 368 | // complicated handling of PATH env var 369 | orig_execvp_type orig_execvp; 370 | orig_execvp = reinterpret_cast(dlsym(RTLD_NEXT, "execvp")); 371 | return orig_execvp(file, argv); 372 | } 373 | 374 | int execlp(const char *file, const char *arg, ...) { 375 | // complicated handling of PATH env var 376 | orig_execlp_type orig_execlp; 377 | orig_execlp = reinterpret_cast(dlsym(RTLD_NEXT, "execlp")); 378 | return orig_execlp(file, arg); 379 | } 380 | 381 | FILE *fopen(const char *filename, const char *modes) { 382 | std::vector path_args = {filename}; 383 | // the mode doesn't matter for pathauditor 384 | std::vector args = {0, O_RDONLY}; 385 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 386 | 387 | pathauditor::LibcFileEventIsUserControlled(file_event, "fopen"); 388 | 389 | orig_fopen_type orig_fopen; 390 | orig_fopen = reinterpret_cast(dlsym(RTLD_NEXT, "fopen")); 391 | return orig_fopen(filename, modes); 392 | } 393 | 394 | FILE *fopen64(const char *filename, const char *modes) { 395 | std::vector path_args = {filename}; 396 | std::vector args = { 397 | 0, O_RDONLY}; // the mode doesn't matter for pathauditor 398 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 399 | 400 | pathauditor::LibcFileEventIsUserControlled(file_event, "fopen64"); 401 | 402 | orig_fopen64_type orig_fopen64; 403 | orig_fopen64 = 404 | reinterpret_cast(dlsym(RTLD_NEXT, "fopen64")); 405 | return orig_fopen64(filename, modes); 406 | } 407 | 408 | FILE *freopen(const char *filename, const char *modes, FILE *stream) { 409 | std::vector path_args = {filename}; 410 | std::vector args = { 411 | 0, O_RDONLY}; // the mode doesn't matter for pathauditor 412 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 413 | 414 | pathauditor::LibcFileEventIsUserControlled(file_event, "freopen"); 415 | 416 | orig_freopen_type orig_freopen; 417 | orig_freopen = 418 | reinterpret_cast(dlsym(RTLD_NEXT, "freopen")); 419 | return orig_freopen(filename, modes, stream); 420 | } 421 | 422 | FILE *freopen64(const char *filename, const char *modes, FILE *stream) { 423 | std::vector path_args = {filename}; 424 | std::vector args = { 425 | 0, O_RDONLY}; // the mode doesn't matter for pathauditor 426 | pathauditor::FileEvent file_event(SYS_open, args, path_args); 427 | 428 | pathauditor::LibcFileEventIsUserControlled(file_event, "fopen64"); 429 | 430 | orig_freopen64_type orig_freopen64; 431 | orig_freopen64 = 432 | reinterpret_cast(dlsym(RTLD_NEXT, "freopen64")); 433 | return orig_freopen64(filename, modes, stream); 434 | } 435 | 436 | int truncate(const char *file, off_t length) { 437 | std::vector path_args = {file}; 438 | std::vector args = {0, static_cast(length)}; 439 | pathauditor::FileEvent file_event(SYS_truncate, args, path_args); 440 | 441 | pathauditor::LibcFileEventIsUserControlled(file_event, "truncate"); 442 | 443 | orig_truncate_type orig_truncate; 444 | orig_truncate = 445 | reinterpret_cast(dlsym(RTLD_NEXT, "truncate")); 446 | return orig_truncate(file, length); 447 | } 448 | 449 | int truncate64(const char *file, off64_t length) { 450 | std::vector path_args = {file}; 451 | std::vector args = {0, static_cast(length)}; 452 | pathauditor::FileEvent file_event(SYS_truncate, args, path_args); 453 | 454 | pathauditor::LibcFileEventIsUserControlled(file_event, "truncate64"); 455 | orig_truncate64_type orig_truncate64; 456 | orig_truncate64 = 457 | reinterpret_cast(dlsym(RTLD_NEXT, "truncate64")); 458 | return orig_truncate64(file, length); 459 | } 460 | 461 | int mkdir(const char *path, mode_t mode) { 462 | std::vector path_args = {path}; 463 | std::vector args = {0, mode}; 464 | pathauditor::FileEvent file_event{SYS_mkdir, args, path_args}; 465 | 466 | pathauditor::LibcFileEventIsUserControlled(file_event, "mkdir"); 467 | 468 | orig_mkdir_type orig_mkdir; 469 | orig_mkdir = reinterpret_cast(dlsym(RTLD_NEXT, "mkdir")); 470 | return orig_mkdir(path, mode); 471 | } 472 | 473 | int mkdirat(int fd, const char *path, mode_t mode) { 474 | std::vector path_args = {path}; 475 | std::vector args = {static_cast(fd), 0, mode}; 476 | pathauditor::FileEvent file_event{SYS_mkdirat, args, path_args}; 477 | 478 | pathauditor::LibcFileEventIsUserControlled(file_event, "mkdirat"); 479 | 480 | orig_mkdirat_type orig_mkdirat; 481 | orig_mkdirat = 482 | reinterpret_cast(dlsym(RTLD_NEXT, "mkdirat")); 483 | return orig_mkdirat(fd, path, mode); 484 | } 485 | 486 | int link(const char *from, const char *to) { 487 | std::vector path_args = {from, to}; 488 | std::vector args = {}; 489 | pathauditor::FileEvent file_event{SYS_link, args, path_args}; 490 | 491 | pathauditor::LibcFileEventIsUserControlled(file_event, "link"); 492 | 493 | orig_link_type orig_link; 494 | orig_link = reinterpret_cast(dlsym(RTLD_NEXT, "link")); 495 | return orig_link(from, to); 496 | } 497 | 498 | int linkat(int fromfd, const char *from, int tofd, const char *to, int flags) { 499 | std::vector path_args = {from, to}; 500 | std::vector args = {static_cast(fromfd), 0, 501 | static_cast(tofd), 0, 502 | static_cast(flags)}; 503 | pathauditor::FileEvent file_event{SYS_linkat, args, path_args}; 504 | 505 | pathauditor::LibcFileEventIsUserControlled(file_event, "linkat"); 506 | 507 | orig_linkat_type orig_linkat; 508 | orig_linkat = reinterpret_cast(dlsym(RTLD_NEXT, "linkat")); 509 | return orig_linkat(fromfd, from, tofd, to, flags); 510 | } 511 | 512 | int unlink(const char *name) { 513 | std::vector path_args = {name}; 514 | std::vector args = {0}; 515 | pathauditor::FileEvent file_event{SYS_unlink, args, path_args}; 516 | 517 | pathauditor::LibcFileEventIsUserControlled(file_event, "unlink"); 518 | 519 | orig_unlink_type orig_unlink; 520 | orig_unlink = reinterpret_cast(dlsym(RTLD_NEXT, "unlink")); 521 | return orig_unlink(name); 522 | } 523 | 524 | int unlinkat(int dirfd, const char *name, int flags) { 525 | std::vector path_args = {name}; 526 | std::vector args = {static_cast(dirfd), 0, 527 | static_cast(flags)}; 528 | pathauditor::FileEvent file_event{SYS_unlinkat, args, path_args}; 529 | 530 | pathauditor::LibcFileEventIsUserControlled(file_event, "unlinkat"); 531 | 532 | orig_unlinkat_type orig_unlinkat; 533 | orig_unlinkat = 534 | reinterpret_cast(dlsym(RTLD_NEXT, "unlinkat")); 535 | return orig_unlinkat(dirfd, name, flags); 536 | } 537 | 538 | int remove(const char *filename) { 539 | std::vector path_args = {filename}; 540 | 541 | struct stat stat_buf; 542 | // different behaviour if directory/regular file 543 | if (stat(filename, &stat_buf)) { 544 | std::cerr << "cannot stat " << filename << "\n"; 545 | } else { 546 | if (S_ISDIR(stat_buf.st_mode)) { 547 | std::vector args = {static_cast(AT_FDCWD), 0, 548 | AT_REMOVEDIR}; 549 | pathauditor::FileEvent file_event{SYS_unlinkat, args, path_args}; 550 | pathauditor::LibcFileEventIsUserControlled(file_event, "remove"); 551 | } else { 552 | std::vector args = {0}; 553 | pathauditor::FileEvent file_event{SYS_unlink, args, path_args}; 554 | pathauditor::LibcFileEventIsUserControlled(file_event, "remove"); 555 | } 556 | } 557 | 558 | orig_remove_type orig_remove; 559 | orig_remove = reinterpret_cast(dlsym(RTLD_NEXT, "remove")); 560 | return orig_remove(filename); 561 | } 562 | 563 | int rmdir(const char *path) { 564 | std::vector path_args = {path}; 565 | std::vector args = {static_cast(AT_FDCWD), 0, AT_REMOVEDIR}; 566 | pathauditor::FileEvent file_event{SYS_unlinkat, args, path_args}; 567 | 568 | pathauditor::LibcFileEventIsUserControlled(file_event, "rmdir"); 569 | 570 | orig_rmdir_type orig_rmdir; 571 | orig_rmdir = reinterpret_cast(dlsym(RTLD_NEXT, "rmdir")); 572 | return orig_rmdir(path); 573 | } 574 | 575 | int mount(const char *special_file, const char *dir, const char *fstype, 576 | unsigned rwflag, void *data) { 577 | if (special_file == nullptr) { 578 | special_file = ""; 579 | } 580 | std::vector path_args = {special_file, dir}; 581 | std::vector args = {0, 0, reinterpret_cast(fstype), 582 | static_cast(rwflag), 583 | reinterpret_cast(data)}; 584 | pathauditor::FileEvent file_event{SYS_mount, args, path_args}; 585 | 586 | pathauditor::LibcFileEventIsUserControlled(file_event, "mount"); 587 | 588 | orig_mount_type orig_mount; 589 | orig_mount = reinterpret_cast(dlsym(RTLD_NEXT, "mount")); 590 | return orig_mount(special_file, dir, fstype, rwflag, data); 591 | } 592 | 593 | int umount(const char *special_file) { 594 | std::vector path_args = {special_file}; 595 | std::vector args = {0, 0}; 596 | pathauditor::FileEvent file_event{SYS_umount2, args, path_args}; 597 | 598 | pathauditor::LibcFileEventIsUserControlled(file_event, "umount"); 599 | 600 | orig_umount_type orig_umount; 601 | orig_umount = reinterpret_cast(dlsym(RTLD_NEXT, "umount")); 602 | return orig_umount(special_file); 603 | } 604 | 605 | int umount2(const char *special_file, int flags) { 606 | std::vector path_args = {special_file}; 607 | std::vector args = {0, static_cast(flags)}; 608 | pathauditor::FileEvent file_event{SYS_umount2, args, path_args}; 609 | 610 | pathauditor::LibcFileEventIsUserControlled(file_event, "umount2"); 611 | 612 | orig_umount2_type orig_umount2; 613 | orig_umount2 = 614 | reinterpret_cast(dlsym(RTLD_NEXT, "umount2")); 615 | return orig_umount2(special_file, flags); 616 | } 617 | 618 | int rename(const char *oldpath, const char *newpath) { 619 | std::vector path_args = {oldpath, newpath}; 620 | std::vector args = {}; 621 | pathauditor::FileEvent file_event{SYS_rename, args, path_args}; 622 | 623 | pathauditor::LibcFileEventIsUserControlled(file_event, "rename"); 624 | 625 | orig_rename_type orig_rename; 626 | orig_rename = reinterpret_cast(dlsym(RTLD_NEXT, "rename")); 627 | return orig_rename(oldpath, newpath); 628 | } 629 | 630 | int renameat(int olddirfd, const char *oldpath, int newdirfd, 631 | const char *newpath) { 632 | std::vector path_args = {oldpath, newpath}; 633 | std::vector args = {static_cast(olddirfd), 0, 634 | static_cast(newdirfd), 0}; 635 | pathauditor::FileEvent file_event{SYS_renameat, args, path_args}; 636 | 637 | pathauditor::LibcFileEventIsUserControlled(file_event, "renameat"); 638 | 639 | orig_renameat_type orig_renameat; 640 | orig_renameat = 641 | reinterpret_cast(dlsym(RTLD_NEXT, "renameat")); 642 | return orig_renameat(olddirfd, oldpath, newdirfd, newpath); 643 | } 644 | 645 | int symlink(const char *from, const char *to) { 646 | std::vector path_args = {from, to}; 647 | std::vector args = {}; 648 | pathauditor::FileEvent file_event{SYS_symlink, args, path_args}; 649 | 650 | pathauditor::LibcFileEventIsUserControlled(file_event, "symlink"); 651 | 652 | orig_symlink_type orig_symlink; 653 | orig_symlink = 654 | reinterpret_cast(dlsym(RTLD_NEXT, "symlink")); 655 | return orig_symlink(from, to); 656 | } 657 | 658 | int symlinkat(const char *from, int newdirfd, const char *to) { 659 | std::vector path_args = {from, to}; 660 | std::vector args = {0, static_cast(newdirfd), 0}; 661 | pathauditor::FileEvent file_event{SYS_symlinkat, args, path_args}; 662 | 663 | pathauditor::LibcFileEventIsUserControlled(file_event, "symlinkat"); 664 | 665 | orig_symlinkat_type orig_symlinkat; 666 | orig_symlinkat = 667 | reinterpret_cast(dlsym(RTLD_NEXT, "symlinkat")); 668 | return orig_symlinkat(from, newdirfd, to); 669 | } 670 | 671 | int chroot(const char *path) { 672 | std::vector path_args = {path}; 673 | std::vector args = {0}; 674 | pathauditor::FileEvent file_event{SYS_chroot, args, path_args}; 675 | 676 | pathauditor::LibcFileEventIsUserControlled(file_event, "chroot"); 677 | 678 | orig_chroot_type orig_chroot; 679 | orig_chroot = reinterpret_cast(dlsym(RTLD_NEXT, "chroot")); 680 | return orig_chroot(path); 681 | } 682 | } 683 | -------------------------------------------------------------------------------- /pathauditor/libc/template_generator.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #!/usr/bin/python3 16 | """A utility script that generates libc function stubs. 17 | 18 | It automates the process of writing a wrapper for libc functions that are 19 | relevant for PathAuditor. 20 | 21 | run with: python3 template_generator.py path_auditor_libc.cc 22 | 23 | IMPORTANT: the script depends on a file from a third-party library that's not 24 | imported into Google's third_party: https://github.com/zachriggle/functions 25 | """ 26 | 27 | import argparse 28 | from functions import functions 29 | 30 | 31 | def argname(arg): 32 | """Format illegal argument names (like 'new').""" 33 | name = arg.name 34 | if arg.name == 'new' or arg.name == 'old': 35 | name += 'path' 36 | 37 | return name 38 | 39 | 40 | def args(func): 41 | """Return a list of function's arguments with types.""" 42 | arg_strings = [] 43 | for arg in func.args: 44 | name = argname(arg) 45 | 46 | if name == 'vararg': 47 | arg_strings.append('...') 48 | elif arg.type == 'char' and arg.derefcnt == 1: 49 | arg_strings.append(f'const char *{name}') 50 | elif arg.type == 'char' and arg.derefcnt == 2: 51 | arg_strings.append(f'char *const {name}[]') 52 | else: 53 | arg_strings.append(f'{arg.type} {"*"*arg.derefcnt}{name}') 54 | 55 | return arg_strings 56 | 57 | 58 | def typedef(func): 59 | """Produce a typedef string for a function.""" 60 | return (f'typedef {func.type}{"*"*func.derefcnt} (*orig_{func.name}_type)' 61 | f'({", ".join(args(func))});') 62 | 63 | 64 | def dlsym_call(func): 65 | """Produce a string with a dlsym call and the original function call.""" 66 | fun_args = ', '.join(argname(arg) for arg in func.args) 67 | return (f' orig_{func.name}_type orig_{func.name};\n' 68 | f' orig_{func.name} = reinterpret_cast' 69 | f'(dlsym(RTLD_NEXT, \"{func.name}\"));\n' 70 | f' return orig_{func.name}({fun_args});') 71 | 72 | 73 | def signature(func): 74 | """Produce a string with the function's signature and body.""" 75 | return (f'{func.type}{"*"*func.derefcnt} {func.name}({", ".join(args(func))})' 76 | ' {\n' 77 | f'{dlsym_call(func)}\n' 78 | '}') 79 | 80 | 81 | def main(): 82 | parser = argparse.ArgumentParser(description='Generate libc function stubs') 83 | parser.add_argument('file', type=str, help='Output file') 84 | 85 | cmdline_args = parser.parse_args() 86 | 87 | func_names = [ 88 | 'open', 89 | 'open64', 90 | 'openat', 91 | 'openat64', 92 | 'creat', 93 | 'creat64', 94 | 'chdir', 95 | 'chmod', 96 | 'chown', 97 | 'execl', 98 | 'execve', 99 | 'execle', 100 | 'execvp', 101 | 'execlp', 102 | 'fopen', 103 | 'fopen64', 104 | 'freopen', 105 | 'freopen64', 106 | 'truncate', 107 | 'truncate64', 108 | 'mkdir', 109 | 'link', 110 | 'linkat', 111 | 'unlink', 112 | 'unlinkat', 113 | 'chroot', 114 | 'lchown', 115 | 'remove', 116 | 'rmdir', 117 | 'mount', 118 | 'umount', 119 | 'umount2', 120 | 'rename', 121 | 'renameat', 122 | 'symlink', 123 | 'symlinkat', 124 | 'mkdirat', 125 | 'fchmodat', 126 | 'fchownat', 127 | ] 128 | 129 | func_info = [functions[name] for name in func_names] 130 | assert len(func_names) == len(func_info) 131 | 132 | with open(cmdline_args.file, 'w') as f: 133 | f.write('#define _GNU_SOURCE\n' 134 | '#include \n' 135 | '#include \n' 136 | '#include \n\n') 137 | 138 | for func in func_info: 139 | f.write(typedef(func) + '\n') 140 | 141 | f.write('\nextern \"C\" {\n') 142 | for func in func_info: 143 | f.write(signature(func) + '\n\n') 144 | f.write('}') 145 | 146 | 147 | if __name__ == '__main__': 148 | main() 149 | -------------------------------------------------------------------------------- /pathauditor/pathauditor.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/pathauditor.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | // include fs.h last since it clashes with mount.h 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | #include 35 | #include "pathauditor/util/path.h" 36 | #include "pathauditor/util/cleanup.h" 37 | #include "absl/container/fixed_array.h" 38 | #include "absl/status/statusor.h" 39 | #include "absl/strings/str_cat.h" 40 | #include "absl/strings/str_split.h" 41 | #include "absl/strings/string_view.h" 42 | #include "pathauditor/util/status_macros.h" 43 | 44 | #ifndef FS_IOC_GETFLAGS 45 | #define FS_IOC_GETFLAGS 0x80086601 46 | #endif 47 | 48 | #ifndef FS_IOC_SETFLAGS 49 | #define FS_IOC_SETFLAGS 0x40086602 50 | #endif 51 | 52 | #ifndef FS_IMMUTABLE_FL 53 | #define FS_IMMUTABLE_FL 16 54 | #endif 55 | 56 | #ifndef CGROUP2_SUPER_MAGIC 57 | #define CGROUP2_SUPER_MAGIC 0x63677270 58 | #endif 59 | 60 | namespace pathauditor { 61 | 62 | namespace { 63 | 64 | // O_PATH is not enough since we want to check the immutable flag and the ioctl 65 | // fails with an O_PATH file descriptor. 66 | constexpr int kDirOpenFlags = O_RDONLY; 67 | 68 | static int GetEuid() { 69 | return syscall(SYS_geteuid); 70 | } 71 | 72 | absl::StatusOr FdIsImmutable(int fd) { 73 | int32_t flags; 74 | if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) { 75 | if (errno == ENOTTY) { 76 | // ENOTTY is returned if the filesystem doesn't support this option 77 | return false; 78 | } 79 | return absl::FailedPreconditionError("ioctl(FS_IOC_GETFLATS) failed."); 80 | } 81 | return flags & FS_IMMUTABLE_FL; 82 | } 83 | 84 | absl::StatusOr ResolveDirFd(const ProcessInformation &proc_info, 85 | absl::string_view path, 86 | absl::optional at_fd) { 87 | int dir_fd; 88 | if (IsAbsolutePath(path)) { 89 | PATHAUDITOR_ASSIGN_OR_RETURN(dir_fd, proc_info.RootFileDescriptor(kDirOpenFlags)); 90 | } else if (!at_fd.has_value() || at_fd == AT_FDCWD) { 91 | PATHAUDITOR_ASSIGN_OR_RETURN(dir_fd, proc_info.CwdFileDescriptor(kDirOpenFlags)); 92 | } else { 93 | PATHAUDITOR_ASSIGN_OR_RETURN( 94 | dir_fd, proc_info.DupDirFileDescriptor(at_fd.value(), kDirOpenFlags)); 95 | } 96 | 97 | return dir_fd; 98 | } 99 | 100 | absl::StatusOr FileIsUserWritable( 101 | const ProcessInformation &proc_info, absl::string_view file, 102 | absl::optional at_fd = absl::optional()) { 103 | PATHAUDITOR_ASSIGN_OR_RETURN(int dir_fd, ResolveDirFd(proc_info, file, at_fd)); 104 | auto close_dir_fd = MakeCleanup([&dir_fd]() { close(dir_fd); }); 105 | 106 | struct stat sb; 107 | if (fstatat(dir_fd, std::string(file).c_str(), &sb, 0) == -1) { 108 | if (errno != ENOENT) { 109 | return absl::FailedPreconditionError( 110 | absl::StrCat("Couldn't fstatat ", file)); 111 | } 112 | // The file doesn't exist so it's not writable 113 | return false; 114 | } 115 | 116 | // is not a regular file 117 | if (!S_ISREG(sb.st_mode)) { 118 | return false; 119 | } 120 | // is not owned by root or the current user 121 | if (sb.st_uid != 0 && sb.st_uid != GetEuid()) { 122 | return true; 123 | } 124 | // is not owned by the root group and is group writable or is writable by 125 | // others 126 | if ((sb.st_gid != 0 && sb.st_mode & S_IWGRP) || sb.st_mode & S_IWOTH) { 127 | return true; 128 | } 129 | 130 | return false; 131 | } 132 | 133 | absl::StatusOr FileIsUserControlled(int dir_fd, absl::string_view file) { 134 | // Filter out special files 135 | if (file == "." || file == "..") { 136 | return false; 137 | } 138 | 139 | // if either the dir or the file are immutable the access is safe 140 | PATHAUDITOR_ASSIGN_OR_RETURN(bool dir_is_immutable, FdIsImmutable(dir_fd)); 141 | if (dir_is_immutable) { 142 | return false; 143 | } 144 | 145 | int file_fd = openat(dir_fd, std::string(file).c_str(), O_RDONLY); 146 | if (file_fd == -1) { 147 | if (errno != ENOENT) { 148 | return absl::FailedPreconditionError( 149 | absl::StrCat("Couldn't open file for immutable check ", file)); 150 | } 151 | } else { 152 | PATHAUDITOR_ASSIGN_OR_RETURN(bool file_is_immutable, FdIsImmutable(file_fd)); 153 | close(file_fd); 154 | if (file_is_immutable) { 155 | return false; 156 | } 157 | } 158 | 159 | struct statfs fs_buf; 160 | if (fstatfs(dir_fd, &fs_buf) == -1) { 161 | return absl::FailedPreconditionError("fstatfs(dir_fd) failed"); 162 | } 163 | 164 | // ignore proc and cgroup filesystems 165 | if (fs_buf.f_type == PROC_SUPER_MAGIC || 166 | fs_buf.f_type == CGROUP_SUPER_MAGIC || 167 | fs_buf.f_type == CGROUP2_SUPER_MAGIC) { 168 | return false; 169 | } 170 | 171 | struct stat sb; 172 | if (fstat(dir_fd, &sb) == -1) { 173 | return absl::FailedPreconditionError("fstat(dir_fd) failed"); 174 | } 175 | 176 | // non-root owner or owner != user 177 | if (sb.st_uid != 0 && sb.st_uid != GetEuid()) { 178 | return true; 179 | } 180 | 181 | // root owned dir that is writable by a user 182 | if ((sb.st_gid != 0 && sb.st_mode & S_IWGRP) || sb.st_mode & S_IWOTH) { 183 | // if not sticky the file is controlled 184 | if (!(sb.st_mode & S_ISVTX)) { 185 | return true; 186 | } 187 | 188 | // For sticky dirs you can only replace a file if you're the directory owner 189 | // or the owner of the file. 190 | // We already checked above if the directory is user owned. 191 | // This leaves the cases where the file is user owned or non-existent. 192 | 193 | // check if the file is owned by non-root 194 | struct stat next_sb; 195 | if (fstatat(dir_fd, std::string(file).c_str(), &next_sb, AT_SYMLINK_NOFOLLOW) == 196 | -1) { 197 | if (errno != ENOENT) { 198 | return absl::FailedPreconditionError( 199 | absl::StrCat("Couldn't fstatat ", file)); 200 | } 201 | // The file doesn't exist but it could be created by a user 202 | return true; 203 | } 204 | if (next_sb.st_uid != 0 && next_sb.st_uid != GetEuid()) { 205 | return true; 206 | } 207 | } 208 | 209 | return false; 210 | } 211 | 212 | } // namespace 213 | 214 | // The algorithm is roughly: 215 | // * keep a fd open to the current directory we're in 216 | // * absolute path => open(/) 217 | // * AT_FDCWD => open(.) 218 | // * iterate over the path segments 219 | // * dir => check perms and enter 220 | // * relative link => prepend to remaining path 221 | // * absolute link => prepend to remaining path and start at / 222 | absl::StatusOr PathIsUserControlled(const ProcessInformation &proc_info, 223 | absl::string_view path, 224 | absl::optional at_fd, 225 | unsigned int max_iteration_count) { 226 | PATHAUDITOR_ASSIGN_OR_RETURN(int dir_fd, ResolveDirFd(proc_info, path, at_fd)); 227 | auto close_dir_fd = MakeCleanup([&dir_fd]() { close(dir_fd); }); 228 | 229 | std::deque path_queue = absl::StrSplit(path, '/', absl::SkipEmpty()); 230 | 231 | for (size_t i = 0; i < max_iteration_count; i++) { 232 | if (path_queue.empty()) { 233 | return false; 234 | } 235 | 236 | std::string elem = path_queue.front(); 237 | path_queue.pop_front(); 238 | 239 | if (elem == ".") { 240 | continue; 241 | } 242 | 243 | // Check if the next path element is user controlled 244 | PATHAUDITOR_ASSIGN_OR_RETURN(bool access_is_unsafe, FileIsUserControlled(dir_fd, elem)); 245 | if (access_is_unsafe) { 246 | return true; 247 | } 248 | 249 | // Check if the element actually exists. We need to check after 250 | // FileIsUserControlled since a non-existent file could still be created 251 | // by a user if the directory is writable. 252 | struct stat sb; 253 | if (fstatat(dir_fd, elem.c_str(), &sb, AT_SYMLINK_NOFOLLOW) == -1) { 254 | if (errno != ENOENT) { 255 | return absl::FailedPreconditionError( 256 | absl::StrCat("Could not stat path element ", elem)); 257 | } else { 258 | return false; 259 | } 260 | } 261 | 262 | // Symlinks in /proc are magic. We can just follow them in the stat call. 263 | // If the file is a symlink and the current directory is in proc, just 264 | // follow the symlink instead. 265 | if ((sb.st_mode & S_IFMT) == S_IFLNK) { 266 | struct statfs fs_buf; 267 | if (fstatfs(dir_fd, &fs_buf) == -1) { 268 | return absl::FailedPreconditionError("fstatfs(dir_fd) failed"); 269 | } 270 | 271 | if (fs_buf.f_type == PROC_SUPER_MAGIC) { 272 | if (fstatat(dir_fd, elem.c_str(), &sb, 0) == -1) { 273 | return absl::FailedPreconditionError(absl::StrCat( 274 | "Could not stat path element without nofollow", elem)); 275 | } 276 | } 277 | } 278 | 279 | switch (sb.st_mode & S_IFMT) { 280 | case S_IFDIR: { 281 | // Change into the directory 282 | int new_fd = openat(dir_fd, elem.c_str(), kDirOpenFlags); 283 | if (new_fd == -1) { 284 | return absl::FailedPreconditionError( 285 | absl::StrCat("Couldn't openat next elem ", elem)); 286 | } 287 | close(dir_fd); 288 | dir_fd = new_fd; 289 | break; 290 | } 291 | case S_IFLNK: { 292 | // Read the link and prepend the result to our path queue 293 | absl::FixedArray link_buf(PATH_MAX); 294 | ssize_t link_len = readlinkat(dir_fd, elem.c_str(), link_buf.data(), 295 | link_buf.memsize()); 296 | if (link_len == -1) { 297 | return absl::FailedPreconditionError( 298 | absl::StrCat("Could not read link for path element ", elem)); 299 | } 300 | if ((size_t)link_len >= link_buf.memsize()) { 301 | return absl::FailedPreconditionError( 302 | absl::StrCat("Link is larger than PATH_MAX ", 303 | std::string(link_buf.data(), link_buf.memsize()))); 304 | } 305 | link_buf[link_len] = 0; 306 | // If the path is absolute, change to / 307 | if (IsAbsolutePath(link_buf.data())) { 308 | PATHAUDITOR_ASSIGN_OR_RETURN(int new_fd, 309 | proc_info.RootFileDescriptor(kDirOpenFlags)); 310 | close(dir_fd); 311 | dir_fd = new_fd; 312 | } 313 | // prepend the link elements to our path queue 314 | std::deque link_queue = 315 | absl::StrSplit(link_buf.data(), '/', absl::SkipEmpty()); 316 | path_queue.insert(path_queue.begin(), link_queue.begin(), 317 | link_queue.end()); 318 | break; 319 | } 320 | default: 321 | if (!path_queue.empty()) { 322 | return absl::FailedPreconditionError( 323 | "Non-directory in middle of path."); 324 | } 325 | return false; 326 | } 327 | } 328 | 329 | return absl::ResourceExhaustedError( 330 | absl::StrCat("Ran into max iteration count ", max_iteration_count)); 331 | } 332 | 333 | absl::StatusOr FileEventIsUserControlled( 334 | const ProcessInformation &proc_info, const FileEvent &event) { 335 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string path, event.PathArg(0)); 336 | 337 | absl::optional fd_arg; 338 | bool skip_last_element = false; 339 | 340 | switch (event.syscall_nr) { 341 | case SYS_chmod: 342 | case SYS_chown: 343 | case SYS_chdir: 344 | case SYS_rmdir: 345 | case SYS_uselib: 346 | case SYS_swapon: 347 | case SYS_chroot: 348 | case SYS_creat: // creat == open(O_CREAT|O_WRONLY|O_TRUNC) 349 | case SYS_truncate: 350 | break; 351 | case SYS_unlink: 352 | case SYS_mknod: 353 | case SYS_mkdir: 354 | case SYS_lchown: 355 | // These syscalls don't follow symlinks 356 | skip_last_element = true; 357 | break; 358 | case SYS_unlinkat: 359 | case SYS_mknodat: 360 | case SYS_mkdirat: { 361 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 362 | skip_last_element = true; 363 | break; 364 | } 365 | case SYS_open: { 366 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(1)); 367 | if (flags & O_NOFOLLOW || flags & O_EXCL) { 368 | skip_last_element = true; 369 | } 370 | break; 371 | } 372 | case SYS_openat: { 373 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 374 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(2)); 375 | if (flags & O_NOFOLLOW || flags & O_EXCL) { 376 | skip_last_element = true; 377 | } 378 | break; 379 | } 380 | case SYS_fchmodat: { 381 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 382 | // fchmodat has a no follow flag, but it's not used 383 | break; 384 | } 385 | case SYS_fchownat: { 386 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 387 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(4)); 388 | if (flags & AT_EMPTY_PATH && path.empty()) { 389 | return false; 390 | } 391 | if (flags & AT_SYMLINK_NOFOLLOW) { 392 | skip_last_element = true; 393 | } 394 | break; 395 | } 396 | #ifdef SYS_execveat 397 | case SYS_execveat: { 398 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 399 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(4)); 400 | if (flags & AT_EMPTY_PATH && path.empty()) { 401 | return false; 402 | } 403 | absl::StatusOr result = FileIsUserWritable(proc_info, path, fd_arg); 404 | if (result.ok() && *result) { 405 | return true; 406 | } 407 | if (flags & AT_SYMLINK_NOFOLLOW) { 408 | skip_last_element = true; 409 | } 410 | break; 411 | } 412 | #endif 413 | case SYS_execve: { 414 | absl::StatusOr result = FileIsUserWritable(proc_info, path); 415 | if (result.ok() && *result) { 416 | return true; 417 | } 418 | break; 419 | } 420 | case SYS_umount2: { 421 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(1)); 422 | if (flags & UMOUNT_NOFOLLOW) { 423 | skip_last_element = true; 424 | } 425 | break; 426 | } 427 | case SYS_name_to_handle_at: { 428 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(4)); 429 | if (flags & AT_EMPTY_PATH && path.empty()) { 430 | return false; 431 | } 432 | if (!(flags & AT_SYMLINK_FOLLOW)) { 433 | skip_last_element = true; 434 | } 435 | break; 436 | } 437 | case SYS_rename: { 438 | skip_last_element = true; 439 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string other_path, event.PathArg(1)); 440 | absl::StatusOr result = 441 | PathIsUserControlled(proc_info, Dirname(other_path)); 442 | if (result.ok() && *result) { 443 | return true; 444 | } 445 | break; 446 | } 447 | case SYS_renameat: 448 | case SYS_renameat2: { 449 | skip_last_element = true; 450 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 451 | PATHAUDITOR_ASSIGN_OR_RETURN(int new_fd, event.Arg(2)); 452 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string new_path, event.PathArg(1)); 453 | absl::StatusOr result = 454 | PathIsUserControlled(proc_info, Dirname(new_path), new_fd); 455 | if (result.ok() && *result) { 456 | return true; 457 | } 458 | 459 | break; 460 | } 461 | case SYS_link: { 462 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string newpath, event.PathArg(1)); 463 | absl::StatusOr result = 464 | PathIsUserControlled(proc_info, Dirname(newpath)); 465 | if (result.ok() && *result) { 466 | return true; 467 | } 468 | break; 469 | } 470 | case SYS_symlink: { 471 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string newpath, event.PathArg(1)); 472 | absl::StatusOr result = 473 | PathIsUserControlled(proc_info, Dirname(newpath)); 474 | if (result.ok() && *result) { 475 | return true; 476 | } 477 | // no checks on link target 478 | return false; 479 | } 480 | case SYS_linkat: { 481 | PATHAUDITOR_ASSIGN_OR_RETURN(fd_arg, event.Arg(0)); 482 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string newpath, event.PathArg(1)); 483 | PATHAUDITOR_ASSIGN_OR_RETURN(int newdirfd, event.Arg(2)); 484 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(4)); 485 | 486 | absl::StatusOr result = 487 | PathIsUserControlled(proc_info, Dirname(newpath), newdirfd); 488 | if (result.ok() && *result) { 489 | return true; 490 | } 491 | 492 | if (flags & AT_EMPTY_PATH && path.empty()) { 493 | return false; 494 | } 495 | 496 | if (!(flags & AT_SYMLINK_FOLLOW)) { 497 | skip_last_element = true; 498 | } 499 | 500 | break; 501 | } 502 | case SYS_symlinkat: { 503 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string newpath, event.PathArg(1)); 504 | PATHAUDITOR_ASSIGN_OR_RETURN(int newdirfd, event.Arg(1)); 505 | absl::StatusOr result = 506 | PathIsUserControlled(proc_info, Dirname(newpath), newdirfd); 507 | if (result.ok() && *result) { 508 | return true; 509 | } 510 | // no checks on link target 511 | return false; 512 | } 513 | case SYS_mount: { 514 | std::string source = path; 515 | PATHAUDITOR_ASSIGN_OR_RETURN(std::string target, event.PathArg(1)); 516 | PATHAUDITOR_ASSIGN_OR_RETURN(int flags, event.Arg(3)); 517 | 518 | absl::StatusOr result = PathIsUserControlled(proc_info, target); 519 | if (result.ok() && *result) { 520 | return true; 521 | } 522 | 523 | if (!(flags & (MS_BIND | MS_MOVE))) { 524 | // only check the source if MS_BIND or MS_MOVE is set 525 | return false; 526 | } 527 | 528 | break; 529 | } 530 | default: 531 | LOG(ERROR) << "Unexpected syscall nr: " << event.syscall_nr; 532 | return absl::UnimplementedError( 533 | absl::StrCat("No support for syscall ", event.syscall_nr)); 534 | } 535 | 536 | if (skip_last_element) { 537 | path = std::string(Dirname(path)); 538 | } 539 | 540 | return PathIsUserControlled(proc_info, path, fd_arg); 541 | } 542 | 543 | } // namespace pathauditor 544 | -------------------------------------------------------------------------------- /pathauditor/pathauditor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_PATHAUDITOR_H_ 16 | #define PATHAUDITOR_PATHAUDITOR_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/statusor.h" 21 | #include "absl/strings/string_view.h" 22 | #include "absl/types/optional.h" 23 | #include "pathauditor/file_event.h" 24 | #include "pathauditor/process_information.h" 25 | 26 | namespace pathauditor { 27 | 28 | // Checks if any element in the path could have been replaced with a symlink by 29 | // an unprivileged user. 30 | // If the path is relative, at_fd needs to be a valid file descriptor. 31 | // The max_iteration_count is to guard against symlink loops. 32 | absl::StatusOr PathIsUserControlled( 33 | const ProcessInformation &proc_info, absl::string_view path, 34 | absl::optional at_fd = absl::optional(), 35 | unsigned int max_iteration_count = 40); 36 | 37 | // Perform checks on the paths in the FileEvent based on the syscall and its 38 | // arguments. 39 | // For example, if open is called with the O_NOFOLLOW flag, we can skip the last 40 | // element in the path. 41 | absl::StatusOr FileEventIsUserControlled( 42 | const ProcessInformation &proc_info, const FileEvent &event); 43 | 44 | } // namespace pathauditor 45 | 46 | #endif // PATHAUDITOR_PATHAUDITOR_H_ 47 | -------------------------------------------------------------------------------- /pathauditor/process_information.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/process_information.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "pathauditor/util/path.h" 22 | #include "absl/status/statusor.h" 23 | #include "absl/strings/str_cat.h" 24 | #include "pathauditor/util/status_macros.h" 25 | 26 | namespace pathauditor { 27 | 28 | namespace { 29 | 30 | absl::StatusOr OpenFile(absl::string_view path, int open_flags) { 31 | int fd = open(std::string(path).c_str(), open_flags); 32 | if (fd == -1) { 33 | return absl::FailedPreconditionError( 34 | absl::StrCat("Could not open \"", path, "\"")); 35 | } 36 | return fd; 37 | } 38 | 39 | } // namespace 40 | 41 | RemoteProcessInformation::RemoteProcessInformation( 42 | pid_t pid, absl::string_view cwd, absl::optional cmdline, 43 | bool fallback) 44 | : pid_(pid), cwd_(cwd), cmdline_(std::move(cmdline)), fallback_(fallback) {} 45 | 46 | absl::StatusOr RemoteProcessInformation::OpenFileInProc( 47 | absl::string_view path, int open_flags) const { 48 | return OpenFile(JoinPath("/proc", absl::StrCat(pid_), path), 49 | open_flags); 50 | } 51 | 52 | absl::StatusOr RemoteProcessInformation::DupDirFileDescriptor( 53 | int fd, int open_flags) const { 54 | return OpenFileInProc(JoinPath("fd", absl::StrCat(fd)), open_flags); 55 | } 56 | 57 | absl::StatusOr RemoteProcessInformation::CwdFileDescriptor( 58 | int open_flags) const { 59 | // The root of the target process might not be the same as ours. Try to 60 | // resolve it relative to /proc//root 61 | absl::StatusOr maybe_fd = 62 | OpenFileInProc(JoinPath("root", cwd_), open_flags); 63 | if (maybe_fd.ok() || !fallback_) { 64 | return maybe_fd; 65 | } 66 | // Fallback if the process doesn't exist anymore. 67 | return OpenFile(cwd_, open_flags); 68 | } 69 | 70 | absl::StatusOr RemoteProcessInformation::RootFileDescriptor( 71 | int open_flags) const { 72 | absl::StatusOr maybe_fd = OpenFileInProc("root", open_flags); 73 | if (maybe_fd.ok() || !fallback_) { 74 | return maybe_fd; 75 | } 76 | // Fallback if the process doesn't exist anymore. 77 | return OpenFile("/", open_flags); 78 | } 79 | 80 | pid_t RemoteProcessInformation::Pid() const { return pid_; } 81 | 82 | std::string RemoteProcessInformation::Cwd() const { return cwd_; } 83 | 84 | std::string RemoteProcessInformation::Cmdline() const { 85 | return cmdline_.value_or(""); 86 | } 87 | 88 | absl::StatusOr SameProcessInformation::DupDirFileDescriptor( 89 | int fd, int open_flags) const { 90 | // use openat instead of dup so that we control the flags 91 | int new_fd = openat(fd, ".", open_flags); 92 | if (new_fd == -1) { 93 | return absl::FailedPreconditionError("openat on dir fd failed"); 94 | } 95 | return new_fd; 96 | } 97 | 98 | absl::StatusOr SameProcessInformation::CwdFileDescriptor( 99 | int open_flags) const { 100 | return OpenFile(".", open_flags); 101 | } 102 | 103 | absl::StatusOr SameProcessInformation::RootFileDescriptor( 104 | int open_flags) const { 105 | return OpenFile("/", open_flags); 106 | } 107 | 108 | } // namespace pathauditor 109 | -------------------------------------------------------------------------------- /pathauditor/process_information.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_PROCESS_INFORMATION_H_ 16 | #define PATHAUDITOR_PROCESS_INFORMATION_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/statusor.h" 21 | #include "absl/strings/string_view.h" 22 | #include "absl/types/optional.h" 23 | 24 | namespace pathauditor { 25 | 26 | // This interface is used by PathAuditor when checking fs-related 27 | // syscalls. It will need to lookup file descriptors in the context of the 28 | // process that made the syscall. 29 | class ProcessInformation { 30 | public: 31 | virtual ~ProcessInformation() {} 32 | virtual absl::StatusOr DupDirFileDescriptor(int fd, 33 | int open_flags) const = 0; 34 | virtual absl::StatusOr CwdFileDescriptor(int open_flags) const = 0; 35 | virtual absl::StatusOr RootFileDescriptor(int open_flags) const = 0; 36 | }; 37 | 38 | // Represents the current process. CwdFileDescriptor will simply open(".") etc. 39 | class SameProcessInformation : public ProcessInformation { 40 | public: 41 | absl::StatusOr DupDirFileDescriptor(int fd, 42 | int open_flags) const override; 43 | absl::StatusOr CwdFileDescriptor(int open_flags) const override; 44 | absl::StatusOr RootFileDescriptor(int open_flags) const override; 45 | }; 46 | 47 | // Represents a remote process. File descriptors are looked up using the proc 48 | // file system. 49 | class RemoteProcessInformation : public ProcessInformation { 50 | public: 51 | // The pid and cwd will be used to lookup file descriptors. 52 | // cmdline is optional as it's only used for logging. 53 | // fallback controls what to do if the process doesn't exist anymore. If it's 54 | // set to true, it will fall back to the root of the current mount namespace 55 | // for file lookups. 56 | RemoteProcessInformation( 57 | pid_t pid, absl::string_view cwd, 58 | absl::optional cmdline = absl::optional(), 59 | bool fallback = false); 60 | 61 | absl::StatusOr DupDirFileDescriptor(int fd, 62 | int open_flags) const override; 63 | absl::StatusOr CwdFileDescriptor(int open_flags) const override; 64 | absl::StatusOr RootFileDescriptor(int open_flags) const override; 65 | 66 | pid_t Pid() const; 67 | std::string Cwd() const; 68 | std::string Cmdline() const; 69 | 70 | private: 71 | absl::StatusOr OpenFileInProc(absl::string_view path, 72 | int open_flags) const; 73 | pid_t pid_; 74 | std::string cwd_; 75 | absl::optional cmdline_; 76 | bool fallback_; 77 | }; 78 | 79 | } // namespace pathauditor 80 | 81 | #endif // PATHAUDITOR_PROCESS_INFORMATION_H_ 82 | -------------------------------------------------------------------------------- /pathauditor/util/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | package(default_visibility = ["//pathauditor:__subpackages__"]) 16 | 17 | licenses(["notice"]) 18 | 19 | cc_library( 20 | name = "status_macros", 21 | hdrs = ["status_macros.h"], 22 | visibility = ["//visibility:public"], 23 | deps = [ 24 | "@com_google_absl//absl/base:core_headers", 25 | "@com_google_absl//absl/status", 26 | ], 27 | ) 28 | 29 | # gMock matchers for util::Status and util::StatusOr and a gUnit printer 30 | # extension for util::StatusOr. 31 | cc_library( 32 | name = "status_matchers", 33 | testonly = 1, 34 | hdrs = ["status_matchers.h"], 35 | visibility = ["//visibility:public"], 36 | deps = [ 37 | ":status_macros", 38 | "@com_google_googletest//:gtest", 39 | "@com_google_absl//absl/status", 40 | "@com_google_absl//absl/status:statusor", 41 | "@com_google_absl//absl/types:optional", 42 | ], 43 | ) 44 | 45 | # Tests for the Status macros. 46 | cc_test( 47 | name = "status_macros_test", 48 | srcs = ["status_macros_test.cc"], 49 | deps = [ 50 | ":status_macros", 51 | ":status_matchers", 52 | "@com_google_googletest//:gtest_main", 53 | "@com_google_absl//absl/memory", 54 | "@com_google_absl//absl/status", 55 | "@com_google_absl//absl/status:statusor", 56 | "@com_google_absl//absl/strings", 57 | ], 58 | ) 59 | 60 | # Compatibility layer for Abseil's flags vs. gFlags 61 | cc_library( 62 | name = "flags", 63 | hdrs = ["flag.h"], 64 | ) 65 | 66 | # A library for cleanup objects. 67 | cc_library( 68 | name = "cleanup", 69 | hdrs = ["cleanup.h"], 70 | ) 71 | 72 | cc_library( 73 | name = "path", 74 | srcs = ["path.cc"], 75 | hdrs = ["path.h"], 76 | deps = ["@com_google_absl//absl/strings"], 77 | ) 78 | 79 | cc_library( 80 | name = "strerror", 81 | srcs = ["strerror.cc"], 82 | hdrs = ["strerror.h"], 83 | deps = [ 84 | "@com_google_absl//absl/base:core_headers", 85 | "@com_google_absl//absl/strings", 86 | ], 87 | ) 88 | -------------------------------------------------------------------------------- /pathauditor/util/cleanup.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_UTIL_CLEANUP_H_ 16 | #define PATHAUDITOR_UTIL_CLEANUP_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace pathauditor { 23 | 24 | // A move-only RAII object that calls a stored cleanup functor when 25 | // destroyed. Cleanup is the return type of MakeCleanup(F). 26 | template 27 | class Cleanup { 28 | public: 29 | Cleanup() : released_(true), f_() {} 30 | 31 | template 32 | explicit Cleanup(G&& f) // NOLINT 33 | : f_(std::forward(f)) {} // NOLINT(build/c++11) 34 | 35 | Cleanup(Cleanup&& src) // NOLINT 36 | : released_(src.is_released()), f_(src.release()) {} 37 | 38 | // Implicitly move-constructible from any compatible Cleanup. 39 | // The source will be released as if src.release() were called. 40 | // A moved-from Cleanup can be safely destroyed or reassigned. 41 | template 42 | Cleanup(Cleanup&& src) // NOLINT 43 | : released_(src.is_released()), f_(src.release()) {} 44 | 45 | // Assignment to a Cleanup object behaves like destroying it 46 | // and making a new one in its place, analogous to unique_ptr 47 | // semantics. 48 | Cleanup& operator=(Cleanup&& src) { // NOLINT 49 | if (!released_) f_(); 50 | released_ = src.released_; 51 | f_ = src.release(); 52 | return *this; 53 | } 54 | 55 | ~Cleanup() { 56 | if (!released_) f_(); 57 | } 58 | 59 | // Releases the cleanup function instead of running it. 60 | // Hint: use c.release()() to run early. 61 | F release() { 62 | released_ = true; 63 | return std::move(f_); 64 | } 65 | 66 | bool is_released() const { return released_; } 67 | 68 | private: 69 | static_assert(!std::is_reference::value, "F must not be a reference"); 70 | 71 | bool released_ = false; 72 | F f_; 73 | }; 74 | 75 | template ::type> 77 | Cleanup MakeCleanup(F&& f) { 78 | return Cleanup(std::forward(f)); 79 | } 80 | 81 | } // namespace pathauditor 82 | 83 | #endif // PATHAUDITOR_UTIL_CLEANUP_H_ 84 | -------------------------------------------------------------------------------- /pathauditor/util/flag.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_UTIL_FLAG_H_ 16 | #define PATHAUDITOR_UTIL_FLAG_H_ 17 | 18 | #include 19 | 20 | #define ABSL_FLAG(type, name, default_value, help) \ 21 | DEFINE_##type(name, default_value, help) 22 | #define ABSL_RETIRED_FLAG ABSL_FLAG 23 | #define ABSL_DECLARE_FLAG(type, name) DECLARE_##type(name) 24 | 25 | // Internal defines for compatility with gflags and standard integer types. 26 | #define DECLARE_int32_t DECLARE_int32 27 | #define DECLARE_int64_t DECLARE_int64 28 | #define DECLARE_uint32_t DECLARE_uint32 29 | #define DECLARE_uint64_t DECLARE_uint64 30 | #define DEFINE_int32_t DEFINE_int32 31 | #define DEFINE_int64_t DEFINE_int64 32 | #define DEFINE_uint32_t DEFINE_uint32 33 | #define DEFINE_uint64_t DEFINE_uint64 34 | 35 | namespace absl { 36 | 37 | template 38 | const T& GetFlag(const T& flag) { 39 | return flag; 40 | } 41 | 42 | template 43 | void SetFlag(T* flag, const T& value) { 44 | *flag = value; 45 | } 46 | 47 | } // namespace absl 48 | 49 | #endif // PATHAUDITOR_UTIL_FLAG_H_ 50 | -------------------------------------------------------------------------------- /pathauditor/util/path.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/util/path.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "absl/strings/str_split.h" 21 | 22 | namespace pathauditor { 23 | 24 | // 40% of the time in JoinPath() is from calls with 2 arguments, so we 25 | // specialize that case. 26 | std::string JoinPath(absl::string_view path1, absl::string_view path2) { 27 | if (path1.empty()) return std::string(path2); 28 | if (path2.empty()) return std::string(path1); 29 | if (path1.back() == '/') { 30 | if (path2.front() == '/') 31 | return absl::StrCat(path1, absl::ClippedSubstr(path2, 1)); 32 | } else { 33 | if (path2.front() != '/') return absl::StrCat(path1, "/", path2); 34 | } 35 | return absl::StrCat(path1, path2); 36 | } 37 | 38 | namespace internal { 39 | 40 | // Given a collection of file paths, append them all together, 41 | // ensuring that the proper path separators are inserted between them. 42 | std::string JoinPathImpl(bool honor_abs, 43 | std::initializer_list paths) { 44 | std::string result; 45 | 46 | if (paths.size() != 0) { 47 | // This size calculation is worst-case: it assumes one extra "/" for every 48 | // path other than the first. 49 | size_t total_size = paths.size() - 1; 50 | for (const absl::string_view path : paths) total_size += path.size(); 51 | result.resize(total_size); 52 | 53 | auto begin = result.begin(); 54 | auto out = begin; 55 | bool trailing_slash = false; 56 | for (absl::string_view path : paths) { 57 | if (path.empty()) continue; 58 | if (path.front() == '/') { 59 | if (honor_abs) { 60 | out = begin; // wipe out whatever we've built up so far. 61 | } else if (trailing_slash) { 62 | path.remove_prefix(1); 63 | } 64 | } else { 65 | if (!trailing_slash && out != begin) *out++ = '/'; 66 | } 67 | const size_t this_size = path.size(); 68 | memcpy(&*out, path.data(), this_size); 69 | out += this_size; 70 | trailing_slash = out[-1] == '/'; 71 | } 72 | result.erase(out - begin); 73 | } 74 | return result; 75 | } 76 | 77 | } // namespace internal 78 | 79 | bool IsAbsolutePath(absl::string_view path) { 80 | return !path.empty() && path[0] == '/'; 81 | } 82 | 83 | std::string AddSlash(absl::string_view path) { 84 | size_t length = path.size(); 85 | if (length && path[length - 1] != '/') { 86 | return absl::StrCat(path, "/"); 87 | } else { 88 | return std::string(path); 89 | } 90 | } 91 | 92 | absl::string_view Dirname(absl::string_view path) { 93 | return SplitPath(path).first; 94 | } 95 | 96 | absl::string_view Basename(absl::string_view path) { 97 | return SplitPath(path).second; 98 | } 99 | 100 | std::pair SplitPath( 101 | absl::string_view path) { 102 | absl::string_view::size_type pos = path.find_last_of('/'); 103 | 104 | // Handle the case with no '/' in 'path'. 105 | if (pos == absl::string_view::npos) 106 | return std::make_pair(path.substr(0, 0), path); 107 | 108 | // Handle the case with a single leading '/' in 'path'. 109 | if (pos == 0) 110 | return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1)); 111 | 112 | return std::make_pair(path.substr(0, pos), 113 | absl::ClippedSubstr(path, pos + 1)); 114 | } 115 | 116 | } // namespace pathauditor 117 | -------------------------------------------------------------------------------- /pathauditor/util/path.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_UTIL_PATH_H_ 16 | #define PATHAUDITOR_UTIL_PATH_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/strings/string_view.h" 23 | 24 | // A set of file pathname manipulation routines. 25 | // Calls to each of the following functions assume their input is 26 | // well-formed (for some currently nebulous definition of the word). 27 | // 28 | // This collection is largely modeled on Python's os.path module. 29 | // Filenames are restricted to ASCII characters. 30 | 31 | namespace pathauditor { 32 | 33 | namespace internal { 34 | // Not part of the public API. 35 | std::string JoinPathImpl(bool honor_abs, 36 | std::initializer_list paths); 37 | } // namespace internal 38 | 39 | // Join multiple paths together. 40 | // JoinPath and JoinPathRespectAbsolute have slightly different semantics. 41 | // JoinPath unconditionally joins all paths together, whereas 42 | // JoinPathRespectAbsolute ignores any segments prior to the last absolute 43 | // path. For example: 44 | // 45 | // Arguments | JoinPath | JoinPathRespectAbsolute 46 | // ---------------------------+---------------------+----------------------- 47 | // '/foo', 'bar' | /foo/bar | /foo/bar 48 | // '/foo/', 'bar' | /foo/bar | /foo/bar 49 | // '/foo', '/bar' | /foo/bar | /bar 50 | // '/foo', '/bar', '/baz' | /foo/bar/baz | /baz 51 | // 52 | // All paths will be treated as relative paths, regardless of whether or not 53 | // they start with a leading '/'. That is, all paths will be concatenated 54 | // together, with the appropriate path separator inserted in between. 55 | // Arguments must be convertible to absl::string_view. 56 | // 57 | // Usage: 58 | // std::string path = pathauditor::JoinPath("/cns", dirname, filename); 59 | // std::string path = pathauditor::JoinPath(getenv("TEST_SRCDIR"), filename); 60 | // 61 | // 0, 1, 2-path specializations exist to optimize common cases. 62 | inline std::string JoinPath() { return std::string(); } 63 | inline std::string JoinPath(absl::string_view path) { 64 | return std::string(path.data(), path.size()); 65 | } 66 | std::string JoinPath(absl::string_view path1, absl::string_view path2); 67 | template 68 | inline std::string JoinPath(absl::string_view path1, absl::string_view path2, 69 | absl::string_view path3, const T&... args) { 70 | return internal::JoinPathImpl(false, {path1, path2, path3, args...}); 71 | } 72 | 73 | // Join multiple paths together, respecting intermediate absolute paths. 74 | // All paths will be joined together, but if any of the paths is absolute 75 | // (as defined by IsAbsolutePath()), all prior path segments will be ignored. 76 | // This is the behavior of the old pathauditor::JoinPath(). 77 | // Arguments must be convertible to absl::string_view. 78 | // 79 | // Usage: 80 | // std::string path = pathauditor::JoinPathRespectAbsolute("/f", dirname, filename); 81 | template 82 | inline std::string JoinPathRespectAbsolute(const T&... args) { 83 | return internal::JoinPathImpl(true, {args...}); 84 | } 85 | 86 | // Return true if path is absolute. 87 | bool IsAbsolutePath(absl::string_view path); 88 | 89 | // If path is non-empty and doesn't already end with a slash, append one 90 | // to the end. 91 | std::string AddSlash(absl::string_view path); 92 | 93 | // Returns the part of the path before the final "/", EXCEPT: 94 | // * If there is a single leading "/" in the path, the result will be the 95 | // leading "/". 96 | // * If there is no "/" in the path, the result is the empty prefix of the 97 | // input string. 98 | absl::string_view Dirname(absl::string_view path); 99 | 100 | // Return the parts of the path, split on the final "/". If there is no 101 | // "/" in the path, the first part of the output is empty and the second 102 | // is the input. If the only "/" in the path is the first character, it is 103 | // the first part of the output. 104 | std::pair SplitPath( 105 | absl::string_view path); 106 | 107 | // Returns the part of the path after the final "/". If there is no 108 | // "/" in the path, the result is the same as the input. 109 | // Note that this function's behavior differs from the Unix basename 110 | // command if path ends with "/". For such paths, this function returns the 111 | // empty string. 112 | absl::string_view Basename(absl::string_view path); 113 | 114 | } // namespace pathauditor 115 | 116 | #endif // PATHAUDITOR_UTIL_PATH_H_ 117 | -------------------------------------------------------------------------------- /pathauditor/util/status_macros.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This file is a custom fork of util/task/status_macros.h. This will become 16 | // obsolete and will be replaced once Abseil releases absl::Status. 17 | 18 | #ifndef PATHAUDITOR_UTIL_STATUS_MACROS_H_ 19 | #define PATHAUDITOR_UTIL_STATUS_MACROS_H_ 20 | 21 | #include "absl/base/optimization.h" 22 | #include "absl/status/status.h" 23 | 24 | // Internal helper for concatenating macro values. 25 | #define PATHAUDITOR_MACROS_IMPL_CONCAT_INNER_(x, y) x##y 26 | #define PATHAUDITOR_MACROS_IMPL_CONCAT(x, y) \ 27 | PATHAUDITOR_MACROS_IMPL_CONCAT_INNER_(x, y) 28 | 29 | #define PATHAUDITOR_RETURN_IF_ERROR(expr) \ 30 | do { \ 31 | const auto status = (expr); \ 32 | if (ABSL_PREDICT_FALSE(!status.ok())) { \ 33 | return status; \ 34 | } \ 35 | } while (0); 36 | 37 | #define PATHAUDITOR_ASSIGN_OR_RETURN(lhs, rexpr) \ 38 | PATHAUDITOR_ASSIGN_OR_RETURN_IMPL( \ 39 | PATHAUDITOR_MACROS_IMPL_CONCAT(_pathauditor_statusor, __LINE__), lhs, \ 40 | rexpr) 41 | 42 | #define PATHAUDITOR_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ 43 | auto statusor = (rexpr); \ 44 | if (ABSL_PREDICT_FALSE(!statusor.ok())) { \ 45 | return statusor.status(); \ 46 | } \ 47 | lhs = std::move(statusor).value(); 48 | 49 | #endif // PATHAUDITOR_UTIL_STATUS_MACROS_H_ 50 | -------------------------------------------------------------------------------- /pathauditor/util/status_macros_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/util/status_macros.h" 16 | 17 | #include 18 | 19 | #include "gmock/gmock.h" 20 | #include "gtest/gtest.h" 21 | #include "absl/memory/memory.h" 22 | #include "absl/strings/str_cat.h" 23 | #include "absl/status/status.h" 24 | #include "absl/status/statusor.h" 25 | #include "pathauditor/util/status_matchers.h" 26 | 27 | namespace pathauditor { 28 | namespace { 29 | 30 | TEST(ReturnIfError, ReturnsOnErrorStatus) { 31 | auto func = []() -> absl::Status { 32 | PATHAUDITOR_RETURN_IF_ERROR(absl::OkStatus()); 33 | PATHAUDITOR_RETURN_IF_ERROR(absl::OkStatus()); 34 | PATHAUDITOR_RETURN_IF_ERROR( 35 | absl::Status(absl::StatusCode::kUnknown, "EXPECTED")); 36 | return absl::Status(absl::StatusCode::kUnknown, "ERROR"); 37 | }; 38 | 39 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 40 | } 41 | 42 | TEST(ReturnIfError, ReturnsOnErrorFromLambda) { 43 | auto func = []() -> absl::Status { 44 | PATHAUDITOR_RETURN_IF_ERROR([] { return absl::OkStatus(); }()); 45 | PATHAUDITOR_RETURN_IF_ERROR( 46 | [] { return absl::Status(absl::StatusCode::kUnknown, "EXPECTED"); }()); 47 | return absl::Status(absl::StatusCode::kUnknown, "ERROR"); 48 | }; 49 | 50 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 51 | } 52 | 53 | TEST(AssignOrReturn, AssignsMultipleVariablesInSequence) { 54 | auto func = []() -> absl::Status { 55 | int value1; 56 | PATHAUDITOR_ASSIGN_OR_RETURN(value1, absl::StatusOr(1)); 57 | EXPECT_EQ(1, value1); 58 | int value2; 59 | PATHAUDITOR_ASSIGN_OR_RETURN(value2, absl::StatusOr(2)); 60 | EXPECT_EQ(2, value2); 61 | int value3; 62 | PATHAUDITOR_ASSIGN_OR_RETURN(value3, absl::StatusOr(3)); 63 | EXPECT_EQ(3, value3); 64 | int value4; 65 | PATHAUDITOR_ASSIGN_OR_RETURN( 66 | value4, 67 | absl::StatusOr(absl::Status(absl::StatusCode::kUnknown, "EXPECTED"))); 68 | return absl::Status(absl::StatusCode::kUnknown, 69 | absl::StrCat("ERROR: assigned value ", value4)); 70 | }; 71 | 72 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 73 | } 74 | 75 | TEST(AssignOrReturn, AssignsRepeatedlyToSingleVariable) { 76 | auto func = []() -> absl::Status{ 77 | int value = 1; 78 | PATHAUDITOR_ASSIGN_OR_RETURN(value, absl::StatusOr(2)); 79 | EXPECT_EQ(2, value); 80 | PATHAUDITOR_ASSIGN_OR_RETURN(value, absl::StatusOr(3)); 81 | EXPECT_EQ(3, value); 82 | PATHAUDITOR_ASSIGN_OR_RETURN( 83 | value, 84 | absl::StatusOr(absl::Status(absl::StatusCode::kUnknown, "EXPECTED"))); 85 | return absl::Status(absl::StatusCode::kUnknown, "ERROR"); 86 | }; 87 | 88 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 89 | } 90 | 91 | TEST(AssignOrReturn, MovesUniquePtr) { 92 | auto func = []() -> absl::Status{ 93 | std::unique_ptr ptr; 94 | PATHAUDITOR_ASSIGN_OR_RETURN( 95 | ptr, absl::StatusOr>(absl::make_unique(1))); 96 | EXPECT_EQ(*ptr, 1); 97 | return absl::Status(absl::StatusCode::kUnknown, "EXPECTED"); 98 | }; 99 | 100 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 101 | } 102 | 103 | TEST(AssignOrReturn, DoesNotAssignUniquePtrOnErrorStatus) { 104 | auto func = []() -> absl::Status{ 105 | std::unique_ptr ptr; 106 | PATHAUDITOR_ASSIGN_OR_RETURN( 107 | ptr, absl::StatusOr>( 108 | absl::Status(absl::StatusCode::kUnknown, "EXPECTED"))); 109 | EXPECT_EQ(ptr, nullptr); 110 | return absl::OkStatus(); 111 | }; 112 | 113 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 114 | } 115 | 116 | TEST(AssignOrReturn, MovesUniquePtrRepeatedlyToSingleVariable) { 117 | auto func = []() -> absl::Status{ 118 | std::unique_ptr ptr; 119 | PATHAUDITOR_ASSIGN_OR_RETURN( 120 | ptr, absl::StatusOr>(absl::make_unique(1))); 121 | EXPECT_EQ(*ptr, 1); 122 | PATHAUDITOR_ASSIGN_OR_RETURN( 123 | ptr, absl::StatusOr>(absl::make_unique(2))); 124 | EXPECT_EQ(*ptr, 2); 125 | return absl::Status(absl::StatusCode::kUnknown, "EXPECTED"); 126 | }; 127 | 128 | EXPECT_THAT(func(), StatusIs(absl::StatusCode::kUnknown, "EXPECTED")); 129 | } 130 | 131 | } // namespace 132 | } // namespace pathauditor 133 | -------------------------------------------------------------------------------- /pathauditor/util/status_matchers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_UTIL_STATUS_MATCHERS_H_ 16 | #define PATHAUDITOR_UTIL_STATUS_MATCHERS_H_ 17 | 18 | #include 19 | 20 | #include "gmock/gmock.h" 21 | #include "absl/types/optional.h" 22 | #include "absl/status/status.h" 23 | #include "absl/status/statusor.h" 24 | #include "pathauditor/util/status_macros.h" 25 | 26 | #define PATHAUDITOR_PATHAUDITOR_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ 27 | PATHAUDITOR_ASSERT_OK_AND_ASSIGN_IMPL( \ 28 | PATHAUDITOR_MACROS_IMPL_CONCAT(_pathauditor_statusor, __LINE__), lhs, \ 29 | rexpr) 30 | 31 | #define PATHAUDITOR_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \ 32 | auto statusor = (rexpr); \ 33 | ASSERT_THAT(statusor.status(), pathauditor::IsOk()); \ 34 | lhs = std::move(statusor).value(); 35 | 36 | namespace pathauditor { 37 | namespace internal { 38 | 39 | // Implements a gMock matcher that checks that an absl::StatusOr has 40 | // an OK status and that the contained T value matches another matcher. 41 | template 42 | class IsOkAndHoldsMatcher 43 | : public ::testing::MatcherInterface&> { 44 | public: 45 | template 46 | IsOkAndHoldsMatcher(MatcherT&& value_matcher) 47 | : value_matcher_(::testing::SafeMatcherCast(value_matcher)) {} 48 | 49 | // From testing::MatcherInterface. 50 | void DescribeTo(std::ostream* os) const override { 51 | *os << "is OK and contains a value that "; 52 | value_matcher_.DescribeTo(os); 53 | } 54 | 55 | // From testing::MatcherInterface. 56 | void DescribeNegationTo(std::ostream* os) const override { 57 | *os << "is not OK or contains a value that "; 58 | value_matcher_.DescribeNegationTo(os); 59 | } 60 | 61 | // From testing::MatcherInterface. 62 | bool MatchAndExplain( 63 | const absl::StatusOr& status_or, 64 | ::testing::MatchResultListener* listener) const override { 65 | if (!status_or.ok()) { 66 | *listener << "which is not OK"; 67 | return false; 68 | } 69 | 70 | ::testing::StringMatchResultListener value_listener; 71 | bool is_a_match = 72 | value_matcher_.MatchAndExplain(status_or.value(), &value_listener); 73 | std::string value_explanation = value_listener.str(); 74 | if (!value_explanation.empty()) { 75 | *listener << absl::StrCat("which contains a value ", value_explanation); 76 | } 77 | 78 | return is_a_match; 79 | } 80 | 81 | private: 82 | const ::testing::Matcher value_matcher_; 83 | }; 84 | 85 | template 86 | class IsOkAndHoldsGenerator { 87 | public: 88 | explicit IsOkAndHoldsGenerator(ValueMatcherT value_matcher) 89 | : value_matcher_(std::move(value_matcher)) {} 90 | 91 | template 92 | operator ::testing::Matcher&>() const { 93 | return ::testing::MakeMatcher(new IsOkAndHoldsMatcher(value_matcher_)); 94 | } 95 | 96 | private: 97 | const ValueMatcherT value_matcher_; 98 | }; 99 | 100 | class IsOkMatcher { 101 | public: 102 | template 103 | bool MatchAndExplain(const StatusT& status_container, 104 | ::testing::MatchResultListener* listener) const { 105 | if (!status_container.ok()) { 106 | *listener << "which is not OK"; 107 | return false; 108 | } 109 | return true; 110 | } 111 | 112 | void DescribeTo(std::ostream* os) const { *os << "is OK"; } 113 | 114 | void DescribeNegationTo(std::ostream* os) const { *os << "is not OK"; } 115 | }; 116 | 117 | template 118 | class StatusIsMatcher { 119 | public: 120 | StatusIsMatcher(const StatusIsMatcher&) = default; 121 | StatusIsMatcher& operator=(const StatusIsMatcher&) = default; 122 | 123 | StatusIsMatcher(Enum code, absl::optional message) 124 | : code_{code}, message_{message} {} 125 | 126 | template 127 | bool MatchAndExplain(const T& value, 128 | ::testing::MatchResultListener* listener) const { 129 | auto status = GetStatus(value); 130 | if (code_ != status.code()) { 131 | *listener << "whose error code is generic::" 132 | << absl::StatusCodeToString(status.code()); 133 | return false; 134 | } 135 | if (message_.has_value() && status.message() != message_.value()) { 136 | *listener << "whose error message is '" << message_.value() << "'"; 137 | return false; 138 | } 139 | return true; 140 | } 141 | 142 | void DescribeTo(std::ostream* os) const { 143 | *os << "has a status code that is generic::" 144 | << absl::StatusCodeToString(code_); 145 | if (message_.has_value()) { 146 | *os << ", and has an error message that is '" << message_.value() << "'"; 147 | } 148 | } 149 | 150 | void DescribeNegationTo(std::ostream* os) const { 151 | *os << "has a status code that is not generic::" 152 | << absl::StatusCodeToString(code_); 153 | if (message_.has_value()) { 154 | *os << ", and has an error message that is not '" << message_.value() 155 | << "'"; 156 | } 157 | } 158 | 159 | private: 160 | template ().code())>::value, 163 | int>::type = 0> 164 | static const StatusT& GetStatus(const StatusT& status) { 165 | return status; 166 | } 167 | 168 | template ().status())> 170 | static StatusT GetStatus(const StatusOrT& status_or) { 171 | return status_or.status(); 172 | } 173 | 174 | const Enum code_; 175 | const absl::optional message_; 176 | }; 177 | 178 | } // namespace internal 179 | 180 | inline ::testing::PolymorphicMatcher IsOk() { 181 | return ::testing::MakePolymorphicMatcher(internal::IsOkMatcher{}); 182 | } 183 | 184 | template 185 | internal::IsOkAndHoldsGenerator IsOkAndHolds( 186 | ValueMatcherT value_matcher) { 187 | return internal::IsOkAndHoldsGenerator(value_matcher); 188 | } 189 | 190 | template 191 | ::testing::PolymorphicMatcher> StatusIs( 192 | Enum code, absl::optional message = absl::nullopt) { 193 | return ::testing::MakePolymorphicMatcher( 194 | internal::StatusIsMatcher{code, message}); 195 | } 196 | 197 | } // namespace pathauditor 198 | 199 | #endif // PATHAUDITOR_UTIL_STATUS_MATCHERS_H_ 200 | -------------------------------------------------------------------------------- /pathauditor/util/strerror.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "pathauditor/util/strerror.h" 16 | 17 | #include // For strerror_r 18 | 19 | #include 20 | #include 21 | 22 | #include "absl/base/attributes.h" 23 | #include "absl/strings/str_cat.h" 24 | 25 | namespace pathauditor { 26 | namespace { 27 | 28 | // Only one of these overloads will be used in any given build, as determined by 29 | // the return type of strerror_r(): char* (for GNU), or int (for XSI). See 'man 30 | // strerror_r' for more details. 31 | ABSL_ATTRIBUTE_UNUSED const char* StrErrorR(char* (*strerror_r)(int, char*, 32 | size_t), 33 | int errnum, char* buf, 34 | size_t buflen) { 35 | return strerror_r(errnum, buf, buflen); 36 | } 37 | 38 | // The XSI version (most portable). 39 | ABSL_ATTRIBUTE_UNUSED const char* StrErrorR(int (*strerror_r)(int, char*, 40 | size_t), 41 | int errnum, char* buf, 42 | size_t buflen) { 43 | if (strerror_r(errnum, buf, buflen)) { 44 | *buf = '\0'; 45 | } 46 | return buf; 47 | } 48 | 49 | } // namespace 50 | 51 | std::string StrError(int errnum) { 52 | const int saved_errno = errno; 53 | char buf[100]; 54 | const char* str = StrErrorR(strerror_r, errnum, buf, sizeof(buf)); 55 | if (*str == '\0') { 56 | return absl::StrCat("Unknown error ", errnum); 57 | } 58 | errno = saved_errno; 59 | return str; 60 | } 61 | 62 | } // namespace pathauditor 63 | -------------------------------------------------------------------------------- /pathauditor/util/strerror.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef PATHAUDITOR_UTIL_STRERROR_H_ 16 | #define PATHAUDITOR_UTIL_STRERROR_H_ 17 | 18 | #include 19 | 20 | namespace pathauditor { 21 | 22 | // Returns a human-readable string describing the given POSIX error code. This 23 | // is a portable and thread-safe alternative to strerror(). If the error code is 24 | // not translatable, the string will be "Unknown error nnn". errno will not be 25 | // modified by this call. This function is thread-safe. 26 | std::string StrError(int errnum); 27 | 28 | } // namespace pathauditor 29 | 30 | #endif // PATHAUDITOR_STRERROR_H_ 31 | --------------------------------------------------------------------------------