├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── Modules │ ├── FindKeyutils.cmake │ ├── FindPAM.cmake │ └── FindUUID.cmake └── pam_e4crypt.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore compilation products 2 | *.o 3 | *.so 4 | 5 | # Ignore stuff CMake produces 6 | CMakeCache.txt 7 | CMakeFiles 8 | Makefile 9 | cmake_install.cmake 10 | 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(pam_e4crypt C) 3 | 4 | # 5 | # Dependencies 6 | # 7 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 8 | 9 | find_package(PAM REQUIRED) 10 | 11 | find_package(Keyutils REQUIRED) 12 | find_package(OpenSSL REQUIRED) 13 | find_package(UUID REQUIRED) 14 | 15 | # 16 | # Build 17 | # 18 | 19 | # modules on't start with "lib" 20 | set(CMAKE_SHARED_MODULE_PREFIX "") 21 | 22 | add_library(pam_e4crypt MODULE pam_e4crypt.c) 23 | target_link_libraries(pam_e4crypt pam keyutils OpenSSL::Crypto uuid) 24 | 25 | # install the module in the same place where all the other modules should be 26 | # NOTE: we actually disrespect the install prefix here 27 | if(NOT DEFINED CMAKE_INSTALL_LIBDIR) 28 | get_filename_component(CMAKE_INSTALL_LIBDIR ${PAM_LIBRARY} DIRECTORY) 29 | endif() 30 | install(TARGETS pam_e4crypt 31 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/security/" 32 | ) 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pam_e4crypt 2 | 3 | This is a PAM module for unlocking transparently encrypted directories on ext4. 4 | 5 | Since version 4.1, the Linux kernel supports transparent encryption in ext4. The 6 | mechanism relies on the keyrings facility of the kernel. 7 | 8 | This module will create keys for (de)ciphering files and file-names in encrypted 9 | directories during the authentication phase. During the session setup phase, 10 | those keys are added to the session keyring, giving the user (instant) access 11 | to directories for which she previously set a "policy" matching her passphrase 12 | (e.g. using e4crypt from the e2fsprogs). 13 | 14 | Note that the only encryption mode currently supported is aes256-xts. 15 | 16 | Google's [fscrypt](https://github.com/google/fscrypt) has the same purpose but 17 | is not restricted to ext4's transparent encryption feature and far better 18 | maintained. Thus, fscrypt is probably better suited for production machines. 19 | 20 | 21 | ## Pitfalls 22 | 23 | Users should be aware of the following when using (or thinking about using) this 24 | module: 25 | 26 | * Files and directories are usually cached by the kernel for performance 27 | reasons. When using ext4 transparent encryption, decrypted content ends up 28 | in those caches and may thus (still) be visible to users with sufficient 29 | access rights (e.g. `root`) during or even after a session (until the caches 30 | are cleared). 31 | * Obviously, this module will only unlock files and directories if it will be 32 | invoked, e.g. if PAM is used for login. Thus, background services can not 33 | access encrypted files directories if the files are not unlocked (that's the 34 | whole point of this module -- duh). TL;DR: don't complain about cronjobs 35 | failing while trying to access encrypted files. 36 | * Changing the password used for login does (currently) not change the 37 | encryption key of affected directories. Hence, those directories will not be 38 | unlocked after a password-change. 39 | 40 | 41 | ## Using this module 42 | 43 | This module should be invoked late during the authentication phase as well as 44 | early during the session phase, after an invocation of the `pam_keyinit` module. 45 | The module should be marked as `required` for the authentication phase and 46 | either `required` or `optional` for the session phase, e.g.: 47 | 48 | ``` 49 | auth ... 50 | auth required pam_e4crypt.so 51 | 52 | account ... 53 | password ... 54 | 55 | session required pam_keyinit.so 56 | session required pam_e4crypt.so 57 | session ... 58 | ``` 59 | 60 | During the authentication phase, keys are generated from the user password. 61 | Hence, the module should be invoked late, at a point where the password is 62 | available. 63 | 64 | During the session phase, the generated keys are added to the session keyring. 65 | Obviously, the keyring has to be initialized. Hence, `pam_keyinit` or an 66 | equivalent module has to invoked prior to `pam_e4crypt`. Since other session 67 | modules may access encrypted directories, it is highly recommended to have 68 | `pam_e4crypt` invoked early. 69 | 70 | As this module is considered experimental, users also may want to specify 71 | `onerr=succeed`. Furthermore, it has been reported that having the module 72 | `required` for the session phase may break some setups. Hence, new users are 73 | encouraged making the module `optional` for that phase, at least initially. 74 | 75 | 76 | ### Salt 77 | 78 | Users _must_ provide a salt (up to 16 bytes) for generation of a key. If no salt 79 | is supplied, no key will be generated. By default, the (raw) salt is read from 80 | `$HOME/.ext4_encryption_salt`. You can initialize this file with the following 81 | commands: 82 | 83 | ```head -c 16 /dev/urandom > ~/.ext4_encryption_salt``` 84 | 85 | Alternatively, the module can be configured to read the salt from files in a 86 | dedicated directory using the `saltpath` parameter: 87 | 88 | ``` 89 | auth required pam_e4crypt.so saltpath=/home/.e4crypt 90 | ``` 91 | 92 | The module will then read the salt from `/home/.e4crypt/$USER` 93 | 94 | Note that the `e4crypt` will read salt from various places by default. When 95 | using the `e4crypt add_key` command, specify the intended user's salt via the 96 | `-S` option for compatibility with this module. 97 | 98 | 99 | ### Keyring 100 | 101 | By default, keys are added to the session keyring. Using the `keyring` argument 102 | in the PAM config, it is possible to specify an alternative keyring to which the 103 | keys should be added. Use like: 104 | 105 | ``` 106 | session required pam_e4crypt.so keyring= 107 | ``` 108 | 109 | As ``, one may specify either a keyring's description or one of the 110 | "special values" understood by `keyctl` (1.5), e.g. `@u` for the user specific 111 | keyring. 112 | 113 | 114 | ## Dependencies 115 | 116 | At runtime, the module requires: 117 | * Linux-Kernel>=4.1 118 | * PAM 119 | * keyutils 120 | * OpenSSL (or some compatible replacement like libressl) 121 | * libuuid (util-linux) 122 | 123 | At build time, the developer-packages for all the runtime-dependencies are 124 | required in addition to: 125 | * CMake>=3.5 126 | * A C-compiler (e.g. gcc or clang) 127 | 128 | 129 | ## Installation 130 | 131 | The module is built using CMake. Run 132 | 133 | ``` 134 | cmake 135 | make 136 | make install 137 | ``` 138 | in the directory in which you indent to build the module. In-tree builds are 139 | supported (supply `.` as the source directory). 140 | 141 | Additional cflags and ldflags can be supplied via the `CMAKE_C_FLAGS` and 142 | `CMAKE_MODULE_LINKER_FLAGS` when generating the build system, e.g.: 143 | ``` 144 | cmake -DCMAKE_C_FLAGS="-O2 -fstack-protector-strong" 145 | ``` 146 | 147 | The module is automatically installed in a directory `security` which resides in 148 | the same folder as the PAM library. If those directories are symlinks on the 149 | target platform, the actual installation path may differ from the installation 150 | path originally used by PAM due to the default search order used by CMake. 151 | 152 | Albeit the module should still be found by PAM, users may choose to specify the 153 | library base path used for installation manually by setting the 154 | `CMAKE_INSTALL_LIBDIR` variable. 155 | 156 | For example, run 157 | ``` 158 | cmake -DCMAKE_INSTALL_LIBDIR= 159 | ``` 160 | to prepare the module for installation to `/security/`. 161 | 162 | 163 | ## About passwords, mounts and policies 164 | 165 | "policies" are actually keys generated from a passphrase and a salt. This PAM 166 | module will generate keys from the user's passphrase and add them as policies, 167 | just like `e4crypt add_key`. If you view you session keyring, e.g. using 168 | `keyctl show @s`, those keys will be visible as "logon" keys with a descriptor 169 | prefixed with "ext4:". The hexadecimal string following the prefix is the actual 170 | policy descriptor which you can pass to `e4crypt set_policy`. 171 | 172 | The correct process for encrypting a folder once logged in with pam_e4crypt is: 173 | 174 | ``` 175 | $ keyctl show 176 | Session Keyring 177 | 111111111 --alswrv 0 0 keyring: _ses 178 | 222222222 ----s-rv 0 0 \_ user: invocation_id 179 | 333333333 --als-rv 1000 1000 \_ logon: ext4:abcdef012345678 180 | $ mkdir crypted 181 | $ e4crypt set_policy abcdef012345678 crypted 182 | ``` 183 | 184 | ## Licensing 185 | 186 | The module is currently licensed under the GPLv2. Have a look at the LICENSE 187 | file for more information. Also, you might want to consult a lawyer if you 188 | intend to ship this module together which a BSD-licensed version of PAM. 189 | 190 | The license was chosen because some code was cargo-culted from the e2fsprogs, 191 | which was authored by Michael Halcrow and Ildar Muslukhov, copyrighted by Google 192 | and licensed under the GPLv2. Maybe, one day, if all the affected functions are 193 | rewritten to a degree where they are legally re-implementations, none of the 194 | eventual co-authors object and none of the dependencies switched to GPL-only, 195 | I'll re-release the thing under the LGPL or a BSD-license. 196 | 197 | -------------------------------------------------------------------------------- /cmake/Modules/FindKeyutils.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the keyutils library 2 | # Once done this will define 3 | # KEYUTILS_FOUND - System has libkeyutils 4 | # KEYUTILS_INCLUDE_DIRS - The libkeyutils include directories 5 | # KEYUTILS_LIBRARIES - The libraries needed to use libkeyutils 6 | 7 | find_path(KEYUTILS_INCLUDE_DIR keyutils.h) 8 | 9 | find_library(KEYUTILS_LIBRARY NAMES keyutils) 10 | 11 | set(KEYUTILS_LIBRARIES ${KEYUTILS_LIBRARY}) 12 | set(KEYUTILS_INCLUDE_DIRS ${KEYUTILS_INCLUDE_DIR}) 13 | 14 | include(FindPackageHandleStandardArgs) 15 | 16 | # handle the QUIETLY and REQUIRED arguments and set KEYUTILS_FOUND to TRUE 17 | # if all listed variables are TRUE 18 | find_package_handle_standard_args(KEYUTILS DEFAULT_MSG KEYUTILS_LIBRARY KEYUTILS_INCLUDE_DIR) 19 | 20 | mark_as_advanced(KEYUTILS_INCLUDE_DIR KEYUTILS_LIBRARY) 21 | 22 | add_library(keyutils SHARED IMPORTED GLOBAL) 23 | set_property(TARGET keyutils PROPERTY IMPORTED_LOCATION ${KEYUTILS_LIBRARY}) 24 | set_property(TARGET keyutils PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${KEYUTILS_INCLUDE_DIRS}) 25 | 26 | -------------------------------------------------------------------------------- /cmake/Modules/FindPAM.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the pam library 2 | # Once done this will define 3 | # PAM_FOUND - System has libpam 4 | # PAM_INCLUDE_DIRS - The libpam include directories 5 | # PAM_LIBRARIES - The libraries needed to use libpam 6 | 7 | find_path(PAM_INCLUDE_DIR security/pam_modules.h PATH_SUFFIXES ..) 8 | 9 | set(PAM_DEFINITIONS "-fPIC") 10 | 11 | find_library(PAM_LIBRARY NAMES pam) 12 | 13 | set(PAM_LIBRARIES ${PAM_LIBRARY}) 14 | set(PAM_INCLUDE_DIRS ${PAM_INCLUDE_DIR}) 15 | 16 | include(FindPackageHandleStandardArgs) 17 | 18 | # handle the QUIETLY and REQUIRED arguments and set PAM_FOUND to TRUE 19 | # if all listed variables are TRUE 20 | find_package_handle_standard_args(PAM DEFAULT_MSG PAM_LIBRARY PAM_INCLUDE_DIR) 21 | 22 | mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY) 23 | 24 | add_library(pam SHARED IMPORTED GLOBAL) 25 | set_property(TARGET pam PROPERTY IMPORTED_LOCATION ${PAM_LIBRARY}) 26 | set_property(TARGET pam PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PAM_INCLUDE_DIRS}) 27 | 28 | -------------------------------------------------------------------------------- /cmake/Modules/FindUUID.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the "uuid" library 2 | # Once done this will define 3 | # UUID_FOUND - System has libuuid 4 | # UUID_INCLUDE_DIRS - The libuuid include directories 5 | # UUID_LIBRARIES - The libraries needed to use libuuid 6 | # UUID_DEFINITIONS - Compiler switches required for using libuuid 7 | # the uuid IMPORTED target 8 | 9 | find_package(PkgConfig) 10 | pkg_check_modules(PC_UUID QUIET uuid) 11 | 12 | set(UUID_DEFINITIONS ${PC_UUID_CFLAGS_OTHER}) 13 | 14 | find_path(UUID_INCLUDE_DIR uuid/uuid.h 15 | HINTS ${PC_UUID_INCLUDEDIR} ${PC_UUID_INCLUDE_DIRS} 16 | PATH_SUFFIXES ..) 17 | 18 | find_library(UUID_LIBRARY NAMES uuid 19 | HINTS ${PC_UUID_LIBDIR} ${PC_UUID_LIBRARY_DIRS}) 20 | 21 | set(UUID_LIBRARIES ${UUID_LIBRARY}) 22 | set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR}) 23 | 24 | include(FindPackageHandleStandardArgs) 25 | 26 | # handle the QUIETLY and REQUIRED arguments and set UUID_FOUND to TRUE 27 | # if all listed variables are TRUE 28 | find_package_handle_standard_args(UUID DEFAULT_MSG UUID_LIBRARY UUID_INCLUDE_DIR) 29 | 30 | mark_as_advanced(UUID_INCLUDE_DIR UUID_LIBRARY) 31 | 32 | add_library(uuid SHARED IMPORTED GLOBAL) 33 | set_property(TARGET uuid PROPERTY IMPORTED_LOCATION ${UUID_LIBRARY}) 34 | set_property(TARGET uuid PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${UUID_INCLUDE_DIRS}) 35 | 36 | -------------------------------------------------------------------------------- /pam_e4crypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pam_e4crypt 3 | * Copyright Julian Ganz 4 | * 5 | * This file is part of pam_e4crypt; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | */ 10 | 11 | 12 | // this is a PAM module implementing authentication (kinda) and session setup 13 | #define PAM_SM_AUTH 14 | #define PAM_SM_SESSION 15 | 16 | // std and system includes 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | // ext4 specific includes 29 | #include 30 | #include 31 | 32 | // library includes 33 | #include 34 | #include 35 | 36 | // PAM includes 37 | #include 38 | #include 39 | 40 | 41 | // misc definitions -- originally ripped from e4crypt 42 | #define EXT4_KEY_REF_STR_BUF_SIZE ((EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1) 43 | #define EXT2FS_KEY_TYPE_LOGON "logon" 44 | #define EXT2FS_KEY_DESC_PREFIX "ext4:" 45 | #define EXT2FS_KEY_DESC_PREFIX_SIZE 5 46 | #define SHA512_LENGTH 64 47 | #define PAM_E4CRYPT_KEY_DATA "pam_e4crypt_key_data" 48 | 49 | 50 | /** 51 | * Log honoring the silent flag 52 | * 53 | * Use like syslog, but only if the variable `flags` is around. 54 | */ 55 | #define pam_log(level, ...) do { if (~flags & PAM_SILENT) \ 56 | syslog(level, "pam_e4crypt: " __VA_ARGS__); } while (0) 57 | 58 | // utility functions 59 | 60 | /** 61 | * Retrieve the value of an argument 62 | * 63 | * @returns the value of the option or NULL if the argument doesn't match the 64 | * name supplied 65 | */ 66 | static 67 | char const* 68 | get_modarg_value( 69 | char const* modarg_name, ///< name of the argument 70 | char const* modarg ///< the argument 71 | ) { 72 | // match the name 73 | const size_t name_length = strlen(modarg_name); 74 | if (strncmp(modarg, modarg_name, name_length) != 0) 75 | return NULL; 76 | 77 | // an option either has a value concanated to the name via `=` or it is 78 | // empty (e.g. the argument only contains the name) 79 | if (modarg[name_length] != '=') { 80 | if (modarg[name_length] == '\0') 81 | return ""; 82 | } 83 | 84 | // whatever comes after the `=` is a value 85 | return modarg + name_length + 1; 86 | } 87 | 88 | 89 | /** 90 | * Encryption key list 91 | * 92 | * This struct implements a simple stack containing encryptoin keys. 93 | */ 94 | struct key_list { 95 | struct ext4_encryption_key* data; ///< pointer to the array of keys held 96 | size_t count; ///< number of keys in the list 97 | }; 98 | 99 | 100 | /** 101 | * Initialize a key list 102 | */ 103 | static 104 | void 105 | key_list_init( 106 | struct key_list* list ///< key list to initialize 107 | ) { 108 | list->data = NULL; 109 | list->count = 0; 110 | } 111 | 112 | 113 | /** 114 | * Free all resources associated with a key list 115 | * 116 | * The key list is reset (e.g. re-initialized) 117 | */ 118 | static 119 | void 120 | key_list_destroy( 121 | struct key_list* list ///< key list to destroy 122 | ) { 123 | memset(list->data, 0, sizeof(*(list->data)) * list->count); 124 | free(list->data); 125 | key_list_init(list); 126 | } 127 | 128 | 129 | /** 130 | * Allocate room for one more key 131 | * 132 | * @returns a pointer to the key allocated or `NULL`, if the allocation failed. 133 | */ 134 | static 135 | struct ext4_encryption_key* 136 | key_list_alloc_key( 137 | struct key_list* list ///< key list in which to allocate a key 138 | ) { 139 | size_t current_pos = list->count; 140 | ++(list->count); 141 | 142 | // we resize whenever we hit a power of 2 143 | int need_resize = 1; 144 | for (size_t size = list->count; size > 1; size >>= 1) 145 | if (size & 1) { 146 | need_resize = 0; 147 | break; 148 | } 149 | 150 | if (need_resize) { 151 | // reallocate manually so we can clear the memory 152 | size_t old_size = sizeof(struct ext4_encryption_key) * list->count; 153 | struct ext4_encryption_key* tmp = malloc(old_size * 2); 154 | if (!tmp) 155 | return NULL; 156 | if (list->data == NULL) { 157 | list->data = tmp; 158 | return list->data + current_pos; 159 | } 160 | memcpy(tmp, list->data, old_size); 161 | memset(list->data, 0, old_size); 162 | free(list->data); 163 | list->data = tmp; 164 | } 165 | 166 | return list->data + current_pos; 167 | } 168 | 169 | 170 | /** 171 | * Pop a key from the stack 172 | */ 173 | static 174 | void 175 | key_list_pop( 176 | struct key_list* list ///< key list from which to pop a key 177 | ) { 178 | if (list->count > 0) 179 | --list->count; 180 | } 181 | 182 | 183 | /** 184 | * Cleanup function for keylists given to PAM 185 | * 186 | * This function destroys and frees a key-list. 187 | * Only use if the key list itself was allocated with `malloc()` 188 | */ 189 | static 190 | void 191 | key_list_pam_cleanup( 192 | pam_handle_t* pamh, 193 | void* data, 194 | int error_status 195 | ) { 196 | if (!data) 197 | return; 198 | key_list_destroy((struct key_list*) data); 199 | free(data); 200 | } 201 | 202 | 203 | /** 204 | * Supposed pbkdf2_sha512 implementation 205 | * 206 | * Originally ripped from e4crypt 207 | */ 208 | static 209 | void 210 | pbkdf2_sha512( 211 | const char *passphrase, ///< passphrase to encode 212 | char *salt, ///< salt to use for encoding 213 | unsigned int saltsize, ///< salt to use for encoding 214 | unsigned int count, ///< count of cycles to perform 215 | unsigned char derived_key[EXT4_MAX_KEY_SIZE] ///< output 216 | ) { 217 | size_t passphrase_size = strlen(passphrase); 218 | if (passphrase_size > EXT4_MAX_PASSPHRASE_SIZE) 219 | passphrase_size = EXT4_MAX_PASSPHRASE_SIZE; 220 | 221 | unsigned char buf[SHA512_LENGTH + EXT4_MAX_PASSPHRASE_SIZE] = {0}; 222 | unsigned char tempbuf[SHA512_LENGTH] = {0}; 223 | char final[SHA512_LENGTH] = {0}; 224 | unsigned char saltbuf[EXT4_MAX_SALT_SIZE + EXT4_MAX_PASSPHRASE_SIZE] = {0}; 225 | int actual_buf_len = SHA512_LENGTH + passphrase_size; 226 | int actual_saltbuf_len = EXT4_MAX_SALT_SIZE + passphrase_size; 227 | unsigned int x, y; 228 | __u32 *final_u32 = (__u32 *)final; 229 | __u32 *temp_u32 = (__u32 *)tempbuf; 230 | 231 | memcpy(saltbuf, salt, saltsize); 232 | memcpy(&saltbuf[EXT4_MAX_SALT_SIZE], passphrase, passphrase_size); 233 | 234 | memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size); 235 | 236 | for (x = 0; x < count; ++x) { 237 | if (x == 0) { 238 | SHA512(saltbuf, actual_saltbuf_len, tempbuf); 239 | } else { 240 | /* 241 | * buf: [previous hash || passphrase] 242 | */ 243 | memcpy(buf, tempbuf, SHA512_LENGTH); 244 | SHA512(buf, actual_buf_len, tempbuf); 245 | } 246 | for (y = 0; y < (sizeof(final) / sizeof(*final_u32)); ++y) 247 | final_u32[y] = final_u32[y] ^ temp_u32[y]; 248 | } 249 | 250 | memcpy(derived_key, final, EXT4_MAX_KEY_SIZE); 251 | 252 | memset(buf, 0, sizeof(buf)); 253 | memset(tempbuf, 0, sizeof(tempbuf)); 254 | memset(final, 0, sizeof(final)); 255 | memset(saltbuf, 0, sizeof(saltbuf)); 256 | } 257 | 258 | 259 | /** 260 | * Generate a ref string from a key 261 | * 262 | * Originally ripped from e4crypt 263 | */ 264 | static 265 | void 266 | generate_key_ref_str( 267 | char* key_ref_str, //!< output pointer 268 | struct ext4_encryption_key* key //!< key for which to generate the ref 269 | ) { 270 | unsigned char key_ref1[SHA512_LENGTH]; 271 | unsigned char key_ref2[SHA512_LENGTH]; 272 | unsigned char key_desc[EXT4_KEY_DESCRIPTOR_SIZE]; 273 | int x; 274 | 275 | SHA512(key->raw, EXT4_MAX_KEY_SIZE, key_ref1); 276 | SHA512(key_ref1, SHA512_LENGTH, key_ref2); 277 | memcpy(key_desc, key_ref2, EXT4_KEY_DESCRIPTOR_SIZE); 278 | for (x = 0; x < EXT4_KEY_DESCRIPTOR_SIZE; ++x) { 279 | sprintf(&key_ref_str[x * 2], "%02x", key_desc[x]); 280 | } 281 | key_ref_str[EXT4_KEY_REF_STR_BUF_SIZE - 1] = '\0'; 282 | 283 | memset(key_ref1, 0, sizeof(key_ref1)); 284 | memset(key_ref2, 0, sizeof(key_ref2)); 285 | memset(key_desc, 0, sizeof(key_desc)); 286 | } 287 | 288 | 289 | /** 290 | * Generate a key and store it into the list 291 | */ 292 | static 293 | void 294 | generate_key( 295 | int flags, 296 | char* salt_path, 297 | char* auth_token, 298 | struct key_list* keys 299 | ) { 300 | int fd = open(salt_path, O_RDONLY); 301 | if (fd == -1) { 302 | pam_log(LOG_WARNING, "Could not open salt file '%s': %s", salt_path, strerror(errno)); 303 | return; 304 | } 305 | 306 | char salt[EXT4_MAX_SALT_SIZE]; 307 | int saltsize = read(fd, salt, sizeof(salt)); 308 | close(fd); 309 | 310 | if (saltsize <= 0) { 311 | pam_log(LOG_WARNING, "Failed to read salt from '%s'!", salt_path); 312 | return; 313 | } 314 | 315 | struct ext4_encryption_key* key = key_list_alloc_key(keys); 316 | if (!key) { 317 | pam_log(LOG_WARNING, "Could not allocate space for key!"); 318 | goto clean_salt; 319 | } 320 | key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; 321 | key->size = EXT4_MAX_KEY_SIZE; 322 | 323 | pam_log(LOG_NOTICE, "Generating key with salt length %d from file '%s'", saltsize, salt_path); 324 | pbkdf2_sha512(auth_token, salt, saltsize, EXT4_PBKDF2_ITERATIONS, key->raw); 325 | 326 | // avoid duplicates in the key list 327 | { 328 | struct ext4_encryption_key* current_key = key; 329 | while (current_key-- > keys->data) 330 | if (memcmp(current_key, key, sizeof(*key)) == 0) { 331 | key_list_pop(keys); 332 | pam_log(LOG_NOTICE, "Found duplicate key"); 333 | goto clean_salt; 334 | } 335 | } 336 | 337 | clean_salt: 338 | memset(salt, 0, sizeof(salt)); 339 | } 340 | 341 | 342 | 343 | // keyring retrieval 344 | 345 | 346 | /** 347 | * Named key 348 | * 349 | * Represenation of a named key of some sort 350 | */ 351 | struct named_key { 352 | char const* name; 353 | key_serial_t id; 354 | }; 355 | 356 | 357 | /** 358 | * Special keys 359 | * 360 | * List of special named keys as understood by keyctl-1.5.10. 361 | */ 362 | static 363 | struct named_key const special_keys[] = { 364 | {"@t" , KEY_SPEC_THREAD_KEYRING }, 365 | {"@p" , KEY_SPEC_PROCESS_KEYRING }, 366 | {"@s" , KEY_SPEC_SESSION_KEYRING }, 367 | {"@u" , KEY_SPEC_USER_KEYRING }, 368 | {"@us", KEY_SPEC_USER_SESSION_KEYRING }, 369 | {"@g" , KEY_SPEC_GROUP_KEYRING }, 370 | {"@a" , KEY_SPEC_REQKEY_AUTH_KEY } 371 | }; 372 | 373 | 374 | /** 375 | * Find a keyring matching a spec 376 | * 377 | * @returns the non-zero keyring id found or `0`, if no keyring was found 378 | * 379 | * This function returns the key specified by the string supplied. First, it is 380 | * looked up in a list of special keys. If the key is not one of the special 381 | * keys, a keyring is searched using `request_key()`. If no keyring could be 382 | * found, `0` is returned and `errno` set. 383 | */ 384 | static 385 | key_serial_t 386 | parse_keyring( 387 | char const* keyring ///< specification of the keyring 388 | ) { 389 | // first, look up in the list of special keys 390 | struct named_key const* curr; 391 | curr = special_keys + sizeof(special_keys)/sizeof(special_keys[0]); 392 | while (curr-- > special_keys) 393 | if (strcmp(curr->name, keyring) == 0) 394 | return curr->id; 395 | 396 | // look up using the syscall 397 | key_serial_t retval = request_key("keyring", keyring, NULL, 0); 398 | 399 | // Make sure we return `0` instead of `-1` if no key was found. If we used 400 | // negative values for errors, we would potentially mask special key ids. 401 | if (retval > 0) 402 | return retval; 403 | 404 | // errno should already be set by `request_key` at this point. 405 | return 0; 406 | } 407 | 408 | 409 | 410 | 411 | // PAM authentication module implementations 412 | 413 | 414 | /** 415 | * Implementation `pam_sm_authenticate` for this module 416 | * 417 | * This function actually doesn't do any authentication but instead generates 418 | * keys from the password. 419 | * Those keys are stored PAM-internally, so they can later be applied during 420 | * the session setup. 421 | */ 422 | PAM_EXTERN 423 | int 424 | pam_sm_authenticate( 425 | pam_handle_t* pamh, ///< pam handle 426 | int flags, ///< flags 427 | int argc, ///< number of arguments passed to the module 428 | const char** argv ///< arguments passed to the module 429 | ) { 430 | int retval; 431 | 432 | char* auth_token = NULL; 433 | retval = pam_get_item(pamh, PAM_AUTHTOK, (const void**) &auth_token); 434 | if ((retval != PAM_SUCCESS) || !auth_token) { 435 | pam_log(LOG_ERR, "Failed to get auth token!"); 436 | return PAM_AUTH_ERR; 437 | } 438 | 439 | // we will use a key list for carrying the keys from the authentication 440 | // phase to the session setup phase 441 | struct key_list* keys = malloc(sizeof(*keys)); 442 | if (!keys) { 443 | pam_log(LOG_ERR, "Failed to allocate memory for the key list!"); 444 | return PAM_AUTH_ERR; 445 | } 446 | key_list_init(keys); 447 | pam_set_data(pamh, PAM_E4CRYPT_KEY_DATA, keys, key_list_pam_cleanup); 448 | 449 | // First read a salt define in a fixed place in the HOME directory 450 | const char *username; 451 | retval = pam_get_item(pamh, PAM_USER, (void*) &username); 452 | if (retval != PAM_SUCCESS) 453 | return retval; 454 | struct passwd const* pw = pam_modutil_getpwnam(pamh, username); 455 | if (!pw) { 456 | pam_log(LOG_ERR, "error looking up user"); 457 | return PAM_USER_UNKNOWN; 458 | } 459 | char path[PATH_MAX]; 460 | snprintf(path, PATH_MAX, "%s/%s", pw->pw_dir, ".ext4_encryption_salt"); 461 | 462 | for (int i = 0; i < argc; ++i) { 463 | char const* option; 464 | 465 | if (option = get_modarg_value("saltpath", argv[i])) { 466 | // If a custom saltpath has been passed, use it instead 467 | int spchars = snprintf(path, PATH_MAX, "%s/%s", option, pw->pw_name); 468 | continue; 469 | } 470 | 471 | pam_log(LOG_WARNING, "Unknown option for authenticate: %s", argv[i]); 472 | } 473 | 474 | generate_key(flags, path, auth_token, keys); 475 | 476 | return PAM_SUCCESS; 477 | } 478 | 479 | 480 | /** 481 | * Implementation `pam_sm_setcred` for this module 482 | * 483 | * Dummy to make PAM happy. 484 | */ 485 | PAM_EXTERN 486 | int 487 | pam_sm_setcred( 488 | pam_handle_t* pamh, ///< pam handle 489 | int flags, ///< flags 490 | int argc, ///< number of arguments passed to the module 491 | const char** argv ///< arguments passed to the module 492 | ) { 493 | return PAM_SUCCESS; 494 | } 495 | 496 | 497 | 498 | 499 | // PAM session module implementations 500 | 501 | 502 | /** 503 | * Implementation `pam_sm_open_session` for this module 504 | * 505 | * Retrieves the keys previously generated in the authentication stage and adds 506 | * them to the session keyring. 507 | */ 508 | PAM_EXTERN 509 | int 510 | pam_sm_open_session( 511 | pam_handle_t* pamh, ///< pam handle 512 | int flags, ///< flags 513 | int argc, ///< number of arguments passed to the module 514 | const char** argv ///< arguments passed to the module 515 | ) { 516 | int retval; 517 | key_serial_t keyring = KEY_SPEC_SESSION_KEYRING; 518 | 519 | // parse arguments passed to the module on the session line 520 | for (int i = 0; i < argc; ++i) { 521 | char const* option; 522 | 523 | if (option = get_modarg_value("keyring", argv[i])) { 524 | // A keyring option may have been passed. If so, we try to retrieve 525 | // the key. 526 | keyring = parse_keyring(option); 527 | if (keyring != 0) 528 | continue; 529 | 530 | pam_log(LOG_ERR, 531 | "Could not retrieve keyring '%s': %s", 532 | option, 533 | strerror(errno)); 534 | return PAM_SESSION_ERR; 535 | } 536 | 537 | pam_log(LOG_WARNING, "Unknown option for open_session: %s", argv[i]); 538 | } 539 | 540 | // get the keys we are about to insert 541 | struct key_list* keys = NULL; 542 | retval = pam_get_data(pamh, PAM_E4CRYPT_KEY_DATA, (const void**) &keys); 543 | if ((retval != PAM_SUCCESS) || !keys) { 544 | pam_log(LOG_ERR, "Failed to retrieve key list!"); 545 | return PAM_SESSION_ERR; 546 | } 547 | 548 | const char *username; 549 | retval = pam_get_item(pamh, PAM_USER, (void*) &username); 550 | if (retval != PAM_SUCCESS) 551 | return retval; 552 | 553 | struct passwd const* pw = pam_modutil_getpwnam(pamh, username); 554 | if (!pw) { 555 | pam_log(LOG_ERR, "error looking up user"); 556 | return PAM_USER_UNKNOWN; 557 | } 558 | 559 | // We need to switch the real UID and GID to find the user session keyring. 560 | // We also need to switch the FS UID and GID so the keys end up with the 561 | // correct permission. 562 | uid_t old_uid = getuid(); 563 | uid_t old_gid = getgid(); 564 | 565 | if ((old_gid != pw->pw_gid) && (retval = setregid(pw->pw_gid, -1)) < 0) { 566 | pam_log(LOG_ERR, "Could not set GID: %s", strerror(errno)); 567 | return PAM_SESSION_ERR; 568 | } 569 | 570 | if ((old_uid != pw->pw_uid) && (retval = setreuid(pw->pw_uid, -1) < 0)) { 571 | pam_log(LOG_ERR, "Could not set UID: %s", strerror(errno)); 572 | goto reset_gid; 573 | } 574 | 575 | if ((old_gid != pw->pw_gid) && (retval = setfsgid(pw->pw_gid)) < 0) { 576 | pam_log(LOG_ERR, "Could not set FS GID: %s", strerror(errno)); 577 | goto reset_uid; 578 | } 579 | 580 | if ((old_uid != pw->pw_uid) && (retval = setfsuid(pw->pw_uid) < 0)) { 581 | pam_log(LOG_ERR, "Could not set FS UID: %s", strerror(errno)); 582 | goto reset_fsgid; 583 | } 584 | 585 | struct ext4_encryption_key* ext4_key = keys->data + keys->count; 586 | while (ext4_key-- > keys->data) { 587 | char key_ref_str[EXT2FS_KEY_DESC_PREFIX_SIZE + EXT4_KEY_REF_STR_BUF_SIZE]; 588 | strcpy(key_ref_str, EXT2FS_KEY_DESC_PREFIX); 589 | generate_key_ref_str(key_ref_str + EXT2FS_KEY_DESC_PREFIX_SIZE, 590 | ext4_key); 591 | pam_log(LOG_NOTICE, "Inserting key with reference %s as %d:%d", 592 | key_ref_str, pw->pw_uid, pw->pw_gid); 593 | 594 | key_serial_t key = add_key(EXT2FS_KEY_TYPE_LOGON, key_ref_str, 595 | ext4_key, sizeof(*ext4_key), keyring); 596 | if (key < 0) { 597 | pam_log(LOG_ERR, "Could not add key: %s", strerror(errno)); 598 | continue; 599 | } 600 | } 601 | 602 | if ((old_uid != pw->pw_uid) && (retval = setfsuid(old_uid) < 0)) 603 | pam_log(LOG_ERR, "Could not set GID: %s", strerror(errno)); 604 | 605 | reset_fsgid: 606 | if ((old_gid != pw->pw_gid) && (retval = setfsgid(old_gid)) < 0) 607 | pam_log(LOG_ERR, "Could not set UID: %s", strerror(errno)); 608 | 609 | reset_uid: 610 | if ((old_uid != pw->pw_uid) && (retval = setreuid(old_uid, -1) < 0)) 611 | pam_log(LOG_ERR, "Could not set GID: %s", strerror(errno)); 612 | 613 | reset_gid: 614 | if ((old_gid != pw->pw_gid) && (retval = setregid(old_gid, -1)) < 0) 615 | pam_log(LOG_ERR, "Could not set UID: %s", strerror(errno)); 616 | 617 | return retval; 618 | } 619 | 620 | 621 | /** 622 | * Implementation `pam_sm_close_session` for this module 623 | * 624 | * Dummy to make PAM happy. 625 | */ 626 | PAM_EXTERN 627 | int 628 | pam_sm_close_session( 629 | pam_handle_t* pamh, ///< pam handle 630 | int flags, ///< flags 631 | int argc, ///< number of arguments passed to the module 632 | const char** argv ///< arguments passed to the module 633 | ) { 634 | return PAM_SUCCESS; 635 | } 636 | 637 | 638 | --------------------------------------------------------------------------------