├── .gitignore ├── AUTHORS ├── COPYING ├── COPYING.openssl ├── INSTALL ├── Makefile.am ├── README.md ├── ca.cnf ├── configure.ac ├── deo.1 ├── m4 ├── ax_check_gnu_make.m4 └── ax_define_dir.m4 ├── src ├── Makefile.am ├── asn1.c ├── asn1.h ├── cleanup.c ├── cleanup.h ├── d2i.c ├── d2i.h ├── main.c ├── main.h ├── misc.c ├── misc.h └── plugins │ ├── Makefile.am │ ├── askpassd │ ├── askp.c │ ├── askp.h │ ├── deo-askpassd.path.in │ ├── deo-askpassd.service.in │ ├── iface.c │ ├── iface.h │ ├── list.c │ ├── list.h │ ├── main.c │ ├── module-setup.sh.in │ ├── question.c │ └── question.h │ ├── cryptsetup.c │ ├── decrypt.c │ ├── decryptd │ ├── ctx.c │ ├── ctx.h │ ├── decrypt.c │ ├── decrypt.h │ ├── deo-decryptd.socket.in │ ├── deo-decryptd@.service.in │ └── main.c │ ├── encrypt.c │ ├── query.c │ └── targets.c └── tests ├── ca.py ├── server.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.a 3 | *.o 4 | *.la 5 | *.lo 6 | *.so 7 | *.swp 8 | *.swo 9 | .autotools 10 | .cproject 11 | .deps 12 | .dirstamp 13 | .project 14 | .settings 15 | aclocal.m4 16 | autom4te.cache 17 | build 18 | compile 19 | config.guess 20 | config.log 21 | config.status 22 | config.sub 23 | configure 24 | configure-stamp 25 | depcomp 26 | install-sh 27 | Makefile.in 28 | Makefile 29 | missing 30 | test-driver 31 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Nathaniel McCallum 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /COPYING.openssl: -------------------------------------------------------------------------------- 1 | In addition, as a special exception, the copyright holders give 2 | permission to link the code of portions of this program with the 3 | OpenSSL library under certain conditions as described in each 4 | individual source file, and distribute linked combinations 5 | including the two. 6 | 7 | You must obey the GNU General Public License in all respects 8 | for all of the code used other than OpenSSL. If you modify 9 | file(s) with this exception, you may extend this exception to your 10 | version of the file(s), but you are not obligated to do so. If you 11 | do not wish to do so, delete this exception statement from your 12 | version. If you delete this exception statement from all source 13 | files in the program, then also delete it here. -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, 5 | Inc. 6 | 7 | Copying and distribution of this file, with or without modification, 8 | are permitted in any medium without royalty provided the copyright 9 | notice and this notice are preserved. This file is offered as-is, 10 | without warranty of any kind. 11 | 12 | Basic Installation 13 | ================== 14 | 15 | Briefly, the shell command `./configure && make && make install' 16 | should configure, build, and install this package. The following 17 | more-detailed instructions are generic; see the `README' file for 18 | instructions specific to this package. Some packages provide this 19 | `INSTALL' file but do not implement all of the features documented 20 | below. The lack of an optional feature in a given package is not 21 | necessarily a bug. More recommendations for GNU packages can be found 22 | in *note Makefile Conventions: (standards)Makefile Conventions. 23 | 24 | The `configure' shell script attempts to guess correct values for 25 | various system-dependent variables used during compilation. It uses 26 | those values to create a `Makefile' in each directory of the package. 27 | It may also create one or more `.h' files containing system-dependent 28 | definitions. Finally, it creates a shell script `config.status' that 29 | you can run in the future to recreate the current configuration, and a 30 | file `config.log' containing compiler output (useful mainly for 31 | debugging `configure'). 32 | 33 | It can also use an optional file (typically called `config.cache' 34 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 35 | the results of its tests to speed up reconfiguring. Caching is 36 | disabled by default to prevent problems with accidental use of stale 37 | cache files. 38 | 39 | If you need to do unusual things to compile the package, please try 40 | to figure out how `configure' could check whether to do them, and mail 41 | diffs or instructions to the address given in the `README' so they can 42 | be considered for the next release. If you are using the cache, and at 43 | some point `config.cache' contains results you don't want to keep, you 44 | may remove or edit it. 45 | 46 | The file `configure.ac' (or `configure.in') is used to create 47 | `configure' by a program called `autoconf'. You need `configure.ac' if 48 | you want to change it or regenerate `configure' using a newer version 49 | of `autoconf'. 50 | 51 | The simplest way to compile this package is: 52 | 53 | 1. `cd' to the directory containing the package's source code and type 54 | `./configure' to configure the package for your system. 55 | 56 | Running `configure' might take a while. While running, it prints 57 | some messages telling which features it is checking for. 58 | 59 | 2. Type `make' to compile the package. 60 | 61 | 3. Optionally, type `make check' to run any self-tests that come with 62 | the package, generally using the just-built uninstalled binaries. 63 | 64 | 4. Type `make install' to install the programs and any data files and 65 | documentation. When installing into a prefix owned by root, it is 66 | recommended that the package be configured and built as a regular 67 | user, and only the `make install' phase executed with root 68 | privileges. 69 | 70 | 5. Optionally, type `make installcheck' to repeat any self-tests, but 71 | this time using the binaries in their final installed location. 72 | This target does not install anything. Running this target as a 73 | regular user, particularly if the prior `make install' required 74 | root privileges, verifies that the installation completed 75 | correctly. 76 | 77 | 6. You can remove the program binaries and object files from the 78 | source code directory by typing `make clean'. To also remove the 79 | files that `configure' created (so you can compile the package for 80 | a different kind of computer), type `make distclean'. There is 81 | also a `make maintainer-clean' target, but that is intended mainly 82 | for the package's developers. If you use it, you may have to get 83 | all sorts of other programs in order to regenerate files that came 84 | with the distribution. 85 | 86 | 7. Often, you can also type `make uninstall' to remove the installed 87 | files again. In practice, not all packages have tested that 88 | uninstallation works correctly, even though it is required by the 89 | GNU Coding Standards. 90 | 91 | 8. Some packages, particularly those that use Automake, provide `make 92 | distcheck', which can by used by developers to test that all other 93 | targets like `make install' and `make uninstall' work correctly. 94 | This target is generally not run by end users. 95 | 96 | Compilers and Options 97 | ===================== 98 | 99 | Some systems require unusual options for compilation or linking that 100 | the `configure' script does not know about. Run `./configure --help' 101 | for details on some of the pertinent environment variables. 102 | 103 | You can give `configure' initial values for configuration parameters 104 | by setting variables in the command line or in the environment. Here 105 | is an example: 106 | 107 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 108 | 109 | *Note Defining Variables::, for more details. 110 | 111 | Compiling For Multiple Architectures 112 | ==================================== 113 | 114 | You can compile the package for more than one kind of computer at the 115 | same time, by placing the object files for each architecture in their 116 | own directory. To do this, you can use GNU `make'. `cd' to the 117 | directory where you want the object files and executables to go and run 118 | the `configure' script. `configure' automatically checks for the 119 | source code in the directory that `configure' is in and in `..'. This 120 | is known as a "VPATH" build. 121 | 122 | With a non-GNU `make', it is safer to compile the package for one 123 | architecture at a time in the source code directory. After you have 124 | installed the package for one architecture, use `make distclean' before 125 | reconfiguring for another architecture. 126 | 127 | On MacOS X 10.5 and later systems, you can create libraries and 128 | executables that work on multiple system types--known as "fat" or 129 | "universal" binaries--by specifying multiple `-arch' options to the 130 | compiler but only a single `-arch' option to the preprocessor. Like 131 | this: 132 | 133 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 134 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 135 | CPP="gcc -E" CXXCPP="g++ -E" 136 | 137 | This is not guaranteed to produce working output in all cases, you 138 | may have to build one architecture at a time and combine the results 139 | using the `lipo' tool if you have problems. 140 | 141 | Installation Names 142 | ================== 143 | 144 | By default, `make install' installs the package's commands under 145 | `/usr/local/bin', include files under `/usr/local/include', etc. You 146 | can specify an installation prefix other than `/usr/local' by giving 147 | `configure' the option `--prefix=PREFIX', where PREFIX must be an 148 | absolute file name. 149 | 150 | You can specify separate installation prefixes for 151 | architecture-specific files and architecture-independent files. If you 152 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 153 | PREFIX as the prefix for installing programs and libraries. 154 | Documentation and other data files still use the regular prefix. 155 | 156 | In addition, if you use an unusual directory layout you can give 157 | options like `--bindir=DIR' to specify different values for particular 158 | kinds of files. Run `configure --help' for a list of the directories 159 | you can set and what kinds of files go in them. In general, the 160 | default for these options is expressed in terms of `${prefix}', so that 161 | specifying just `--prefix' will affect all of the other directory 162 | specifications that were not explicitly provided. 163 | 164 | The most portable way to affect installation locations is to pass the 165 | correct locations to `configure'; however, many packages provide one or 166 | both of the following shortcuts of passing variable assignments to the 167 | `make install' command line to change installation locations without 168 | having to reconfigure or recompile. 169 | 170 | The first method involves providing an override variable for each 171 | affected directory. For example, `make install 172 | prefix=/alternate/directory' will choose an alternate location for all 173 | directory configuration variables that were expressed in terms of 174 | `${prefix}'. Any directories that were specified during `configure', 175 | but not in terms of `${prefix}', must each be overridden at install 176 | time for the entire installation to be relocated. The approach of 177 | makefile variable overrides for each directory variable is required by 178 | the GNU Coding Standards, and ideally causes no recompilation. 179 | However, some platforms have known limitations with the semantics of 180 | shared libraries that end up requiring recompilation when using this 181 | method, particularly noticeable in packages that use GNU Libtool. 182 | 183 | The second method involves providing the `DESTDIR' variable. For 184 | example, `make install DESTDIR=/alternate/directory' will prepend 185 | `/alternate/directory' before all installation names. The approach of 186 | `DESTDIR' overrides is not required by the GNU Coding Standards, and 187 | does not work on platforms that have drive letters. On the other hand, 188 | it does better at avoiding recompilation issues, and works well even 189 | when some directory options were not specified in terms of `${prefix}' 190 | at `configure' time. 191 | 192 | Optional Features 193 | ================= 194 | 195 | If the package supports it, you can cause programs to be installed 196 | with an extra prefix or suffix on their names by giving `configure' the 197 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 198 | 199 | Some packages pay attention to `--enable-FEATURE' options to 200 | `configure', where FEATURE indicates an optional part of the package. 201 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 202 | is something like `gnu-as' or `x' (for the X Window System). The 203 | `README' should mention any `--enable-' and `--with-' options that the 204 | package recognizes. 205 | 206 | For packages that use the X Window System, `configure' can usually 207 | find the X include and library files automatically, but if it doesn't, 208 | you can use the `configure' options `--x-includes=DIR' and 209 | `--x-libraries=DIR' to specify their locations. 210 | 211 | Some packages offer the ability to configure how verbose the 212 | execution of `make' will be. For these packages, running `./configure 213 | --enable-silent-rules' sets the default to minimal output, which can be 214 | overridden with `make V=1'; while running `./configure 215 | --disable-silent-rules' sets the default to verbose, which can be 216 | overridden with `make V=0'. 217 | 218 | Particular systems 219 | ================== 220 | 221 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 222 | CC is not installed, it is recommended to use the following options in 223 | order to use an ANSI C compiler: 224 | 225 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 226 | 227 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 228 | 229 | HP-UX `make' updates targets which have the same time stamps as 230 | their prerequisites, which makes it generally unusable when shipped 231 | generated files such as `configure' are involved. Use GNU `make' 232 | instead. 233 | 234 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 235 | parse its `' header file. The option `-nodtk' can be used as 236 | a workaround. If GNU CC is not installed, it is therefore recommended 237 | to try 238 | 239 | ./configure CC="cc" 240 | 241 | and if that doesn't work, try 242 | 243 | ./configure CC="cc -nodtk" 244 | 245 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 246 | directory contains several dysfunctional programs; working variants of 247 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 248 | in your `PATH', put it _after_ `/usr/bin'. 249 | 250 | On Haiku, software installed for all users goes in `/boot/common', 251 | not `/usr/local'. It is recommended to use the following options: 252 | 253 | ./configure --prefix=/boot/common 254 | 255 | Specifying the System Type 256 | ========================== 257 | 258 | There may be some features `configure' cannot figure out 259 | automatically, but needs to determine by the type of machine the package 260 | will run on. Usually, assuming the package is built to be run on the 261 | _same_ architectures, `configure' can figure that out, but if it prints 262 | a message saying it cannot guess the machine type, give it the 263 | `--build=TYPE' option. TYPE can either be a short name for the system 264 | type, such as `sun4', or a canonical name which has the form: 265 | 266 | CPU-COMPANY-SYSTEM 267 | 268 | where SYSTEM can have one of these forms: 269 | 270 | OS 271 | KERNEL-OS 272 | 273 | See the file `config.sub' for the possible values of each field. If 274 | `config.sub' isn't included in this package, then this package doesn't 275 | need to know the machine type. 276 | 277 | If you are _building_ compiler tools for cross-compiling, you should 278 | use the option `--target=TYPE' to select the type of system they will 279 | produce code for. 280 | 281 | If you want to _use_ a cross compiler, that generates code for a 282 | platform different from the build platform, you should specify the 283 | "host" platform (i.e., that on which the generated programs will 284 | eventually be run) with `--host=TYPE'. 285 | 286 | Sharing Defaults 287 | ================ 288 | 289 | If you want to set default values for `configure' scripts to share, 290 | you can create a site shell script called `config.site' that gives 291 | default values for variables like `CC', `cache_file', and `prefix'. 292 | `configure' looks for `PREFIX/share/config.site' if it exists, then 293 | `PREFIX/etc/config.site' if it exists. Or, you can set the 294 | `CONFIG_SITE' environment variable to the location of the site script. 295 | A warning: not all `configure' scripts look for a site script. 296 | 297 | Defining Variables 298 | ================== 299 | 300 | Variables not defined in a site shell script can be set in the 301 | environment passed to `configure'. However, some packages may run 302 | configure again during the build, and the customized values of these 303 | variables may be lost. In order to avoid this problem, you should set 304 | them in the `configure' command line, using `VAR=value'. For example: 305 | 306 | ./configure CC=/usr/local2/bin/gcc 307 | 308 | causes the specified `gcc' to be used as the C compiler (unless it is 309 | overridden in the site shell script). 310 | 311 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 312 | an Autoconf limitation. Until the limitation is lifted, you can use 313 | this workaround: 314 | 315 | CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 316 | 317 | `configure' Invocation 318 | ====================== 319 | 320 | `configure' recognizes the following options to control how it 321 | operates. 322 | 323 | `--help' 324 | `-h' 325 | Print a summary of all of the options to `configure', and exit. 326 | 327 | `--help=short' 328 | `--help=recursive' 329 | Print a summary of the options unique to this package's 330 | `configure', and exit. The `short' variant lists options used 331 | only in the top level, while the `recursive' variant lists options 332 | also present in any nested packages. 333 | 334 | `--version' 335 | `-V' 336 | Print the version of Autoconf used to generate the `configure' 337 | script, and exit. 338 | 339 | `--cache-file=FILE' 340 | Enable the cache: use and save the results of the tests in FILE, 341 | traditionally `config.cache'. FILE defaults to `/dev/null' to 342 | disable caching. 343 | 344 | `--config-cache' 345 | `-C' 346 | Alias for `--cache-file=config.cache'. 347 | 348 | `--quiet' 349 | `--silent' 350 | `-q' 351 | Do not print messages saying which checks are being made. To 352 | suppress all normal output, redirect it to `/dev/null' (any error 353 | messages will still be shown). 354 | 355 | `--srcdir=DIR' 356 | Look for the package's source code in directory DIR. Usually 357 | `configure' can determine that directory automatically. 358 | 359 | `--prefix=DIR' 360 | Use DIR as the installation prefix. *note Installation Names:: 361 | for more details, including other options available for fine-tuning 362 | the installation locations. 363 | 364 | `--no-create' 365 | `-n' 366 | Run the configure checks, but stop before creating any output 367 | files. 368 | 369 | `configure' also accepts some other, not widely useful, options. Run 370 | `configure --help' for more details. 371 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | 3 | ACLOCAL_AMFLAGS = -I m4 4 | AM_MAKEFLAGS = --no-print-directory 5 | 6 | CLEANALL = $(systemdsystemunit_DATA) $(dracut_DATA) $(dracut_SCRIPTS) 7 | 8 | clean-local: 9 | rm -rf $(CLEANALL) 10 | 11 | EXTRA_DIST = README.md tests/test.py tests/ca.py tests/server.py 12 | 13 | AM_TESTS_ENVIRONMENT = export DEO_PLUGINS=src/plugins/.libs DEO_BIN=src/deo; 14 | TESTS = tests/test.py 15 | 16 | dist_man_MANS = deo.1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deo 2 | 3 | #### Welcome to Deo! 4 | Deo is a system service and client for binding data to your local network. 5 | The concept of this application is simple, but let's look at an example. 6 | 7 | ##### Use Case: Network-bound Automatic Disk Decryption 8 | Suppose you have a disk with sensitive data. If it breaks, you can't just 9 | send it back for repair. This could expose the data. Nor can you just throw 10 | it away. 11 | 12 | You could encrypt the disk. This would let you repair or discard the disk. But 13 | now you have to remember a password and manually enter it on every boot. This 14 | doesn't scale. 15 | 16 | What you need is a way to encrypt the disk using a high entropy key and then 17 | making it so that this key can be used automatically when you are on the 18 | network, but not otherwise. 19 | 20 | The solution to this problem is encrypting the key in such a way that it can 21 | only be decrypted when you are on the network. Hence, you bind the key to 22 | the network. 23 | 24 | This is precisely what Deo does. Let's look at how it works. 25 | 26 | ##### Online Operation 27 | First, you encrypt some data to one or more particular servers. As mentioned 28 | in the example above, this data is usually a key to decrypt some secondary 29 | data. 30 | 31 | $ deo encrypt -a anchor.pem one.me.com ... < key.txt > key.enc 32 | 33 | Second, this key can be decrypted so long as your have access to the server it 34 | was encrypted to. 35 | 36 | $ deo decrypt < key.enc > key.txt 37 | 38 | That's it! 39 | 40 | You can encrypt the data to as many servers as you want in a single pass. The 41 | encryption will not succeed unless all servers can be reached. Each server 42 | should have a unique certificate; no sharing is required (and is strongly 43 | discouraged). Server certificates can be rotated using standard CA tools like 44 | certmonger. 45 | 46 | If a payload is encrypted to multiple servers, only one of those servers is 47 | required to be online for decryption. This lets you have load balancing and 48 | redundancy for the decryption service. 49 | 50 | ##### Offline Operation 51 | 52 | Deo encryption (but not, of course, decryption) can also be performed in 53 | offline mode. Start by downloading the encryption certificate chain: 54 | 55 | $ deo query -a anchor.pem one.me.com > one.pem 56 | 57 | Now you can perform encryption offline: 58 | 59 | $ deo encrypt -a anchor.pem one.pem < key.txt > key.enc 60 | 61 | You can even mix online and offline mode: 62 | 63 | $ deo encrypt -a anchor.pem one.pem two.me.com < key.txt > key.enc 64 | 65 | ##### Security Considerations 66 | 67 | Deo tries very hard to always do the right thing. 68 | 69 | In particular, you may notice that the deo client takes anchor.pem 70 | as an option. This is because we always validate whatever certificates we 71 | encounter. This includes pem files stored locally. We force you to use this 72 | argument to think carefully about which certificates you trust. 73 | 74 | ##### Deo Server 75 | 76 | The Deo server is a simple systemd activated service. It listens on port 77 | 5700 by default. The most complex thing is getting the certificate setup 78 | right. Here again, we test to make sure you don't expose sensitive 79 | certificates. 80 | 81 | Unlike most services, Deo uses two certificate/key pairs. All certificate 82 | material is stored in PEM format. 83 | 84 | The first certificate/key pair is for encrypting all communications. This is 85 | just a standard TLS certificate setup. Simply place the certificate, its 86 | private key and any required intermediate certificates into 87 | /etc/deo/decryptd.pem. 88 | 89 | The second certificate/key pair is used for encryption and decryption of the 90 | client secrets and is setup in a slightly more complicated two-step process. 91 | 92 | First, all three components (the certificate, the private key and any required 93 | intermediate certificates) should be placed in a .pem file of your choosing 94 | in the /etc/deo/decrypt.d/ directory. When the server receives a decryption 95 | request, it will attempt to decrypt the data using the certificates stored 96 | here. Hence, installing this .pem file into this directory permits the server 97 | to decrypt incoming decryption requests using this certificate. 98 | 99 | Second, the certificate itself and any intermediate certificates (but not the 100 | private key), should be placed into /etc/deo/encrypt.pem. This data is 101 | returned directly to clients who query for the encryption certificate chain. 102 | On important note is necessary. This file MUST contain a certificate with a 103 | subject with a commonName that resolves to the decryption server (including 104 | the port if a non-standard port is used). This is the hostname that the client 105 | will use during decryption. This hostname may be an IP address. 106 | 107 | Why place the same certificate information in two different places? The Deo 108 | decryption service advertises only one encryption certificate chain but can 109 | handle decryption requests for multiple certificates. This allows for easy 110 | transitional certificate rotation. You can swap in a new certificate (for 111 | new encryption and decryption requests) but still retain the old certificate 112 | to handle decryption requests for already encrypted data. 113 | 114 | Once certificates are installed, just run the following as root: 115 | 116 | # systemctl enable deo-decryptd.socket 117 | # systemctl start deo-decryptd.socket 118 | 119 | ##### Using Deo with Disks 120 | ###### Prerequisites 121 | First, you must have a Deo server running somewhere on your network. For 122 | more information on this step, see the above section. 123 | 124 | Second, you must have a client with LUKS disk encryption already enabled. 125 | 126 | ###### Setup 127 | Both of the following commands are run as root. 128 | 129 | First, we will configure the initramfs for networking. If you are using IPv4 130 | DHCP, no configuration is needed. For other setups, please consult the dracut 131 | documentation. 132 | 133 | Second, we will add a new random key to the pre-existing LUKS encrypted disk 134 | and then encrypt it using Deo in a known location. This command works 135 | exactly like the encrypt command with the exception that a LUKS encrypted disk 136 | is specified rather than passing data using stdin/stdout: 137 | 138 | # deo cryptsetup -d /dev/sda1 -a anchor.pem one.me.com 139 | 140 | Finally, we need to rebuild the system's initramfs: 141 | 142 | # dracut -f 143 | 144 | That's it! Once you reboot, the disk should unlock automatically so long as 145 | one of the specified encryption targets is available. 146 | 147 | ##### Future Improvements 148 | 149 | Here is a list of things we could improve: 150 | 151 | 1. Check certificate expiration. Does OpenSSL do this for us? 152 | 2. Support CRL/OCSP checking and maybe OCSP stapling. 153 | 154 | ##### Concluding Thoughts 155 | 156 | Deo is still a new project and may have some rough edges. We'd 157 | love to hear your feedback! 158 | 159 | ##### Installation 160 | 161 | ./configure 162 | make 163 | sudo make install 164 | -------------------------------------------------------------------------------- /ca.cnf: -------------------------------------------------------------------------------- 1 | # 2 | # OpenSSL example configuration file. 3 | # This is mostly being used for generation of certificate requests. 4 | # 5 | 6 | # This definition stops the following lines choking if HOME isn't 7 | # defined. 8 | HOME = . 9 | RANDFILE = $ENV::HOME/.rnd 10 | 11 | # Extra OBJECT IDENTIFIER info: 12 | #oid_file = $ENV::HOME/.oid 13 | oid_section = new_oids 14 | 15 | # To use this configuration file with the "-extfile" option of the 16 | # "openssl x509" utility, name here the section containing the 17 | # X.509v3 extensions to use: 18 | # extensions = 19 | # (Alternatively, use a configuration file that has only 20 | # X.509v3 extensions in its main [= default] section.) 21 | 22 | [ new_oids ] 23 | 24 | # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. 25 | # Add a simple OID like this: 26 | # testoid1=1.2.3.4 27 | # Or use config file substitution like this: 28 | # testoid2=${testoid1}.5.6 29 | 30 | # Policies used by the TSA examples. 31 | tsa_policy1 = 1.2.3.4.1 32 | tsa_policy2 = 1.2.3.4.5.6 33 | tsa_policy3 = 1.2.3.4.5.7 34 | 35 | #################################################################### 36 | [ ca ] 37 | default_ca = CA_default # The default ca section 38 | 39 | #################################################################### 40 | [ CA_default ] 41 | 42 | dir = ./@CA_DIR@ # Where everything is kept 43 | certs = $dir/certs # Where the issued certs are kept 44 | crl_dir = $dir/crl # Where the issued crl are kept 45 | database = $dir/index.txt # database index file. 46 | #unique_subject = no # Set to 'no' to allow creation of 47 | # several ctificates with same subject. 48 | new_certs_dir = $dir/newcerts # default place for new certs. 49 | 50 | certificate = $dir/cacert.pem # The CA certificate 51 | serial = $dir/serial # The current serial number 52 | crlnumber = $dir/crlnumber # the current crl number 53 | # must be commented out to leave a V1 CRL 54 | crl = $dir/crl.pem # The current CRL 55 | private_key = $dir/private/cakey.pem# The private key 56 | RANDFILE = $dir/private/.rand # private random number file 57 | 58 | x509_extensions = usr_cert # The extentions to add to the cert 59 | 60 | # Comment out the following two lines for the "traditional" 61 | # (and highly broken) format. 62 | name_opt = ca_default # Subject Name options 63 | cert_opt = ca_default # Certificate field options 64 | 65 | # Extension copying option: use with caution. 66 | # copy_extensions = copy 67 | 68 | # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs 69 | # so this is commented out by default to leave a V1 CRL. 70 | # crlnumber must also be commented out to leave a V1 CRL. 71 | # crl_extensions = crl_ext 72 | 73 | default_days = 365 # how long to certify for 74 | default_crl_days= 30 # how long before next CRL 75 | default_md = sha256 # use SHA-256 by default 76 | preserve = no # keep passed DN ordering 77 | 78 | # A few difference way of specifying how similar the request should look 79 | # For type CA, the listed attributes must be the same, and the optional 80 | # and supplied fields are just that :-) 81 | policy = policy_anything 82 | 83 | # For the CA policy 84 | [ policy_match ] 85 | countryName = match 86 | stateOrProvinceName = match 87 | organizationName = match 88 | organizationalUnitName = optional 89 | commonName = supplied 90 | emailAddress = optional 91 | 92 | # For the 'anything' policy 93 | # At this point in time, you must list all acceptable 'object' 94 | # types. 95 | [ policy_anything ] 96 | countryName = optional 97 | stateOrProvinceName = optional 98 | localityName = optional 99 | organizationName = optional 100 | organizationalUnitName = optional 101 | commonName = supplied 102 | emailAddress = optional 103 | 104 | #################################################################### 105 | [ req ] 106 | default_bits = 2048 107 | default_md = sha256 108 | default_keyfile = privkey.pem 109 | distinguished_name = req_distinguished_name 110 | attributes = req_attributes 111 | x509_extensions = v3_ca # The extentions to add to the self signed cert 112 | 113 | # Passwords for private keys if not present they will be prompted for 114 | # input_password = secret 115 | # output_password = secret 116 | 117 | # This sets a mask for permitted string types. There are several options. 118 | # default: PrintableString, T61String, BMPString. 119 | # pkix : PrintableString, BMPString (PKIX recommendation before 2004) 120 | # utf8only: only UTF8Strings (PKIX recommendation after 2004). 121 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). 122 | # MASK:XXXX a literal mask value. 123 | # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. 124 | string_mask = utf8only 125 | 126 | # req_extensions = v3_req # The extensions to add to a certificate request 127 | 128 | [ req_distinguished_name ] 129 | countryName = Country Name (2 letter code) 130 | countryName_default = XX 131 | countryName_min = 2 132 | countryName_max = 2 133 | 134 | stateOrProvinceName = State or Province Name (full name) 135 | #stateOrProvinceName_default = Default Province 136 | 137 | localityName = Locality Name (eg, city) 138 | localityName_default = Default City 139 | 140 | 0.organizationName = Organization Name (eg, company) 141 | 0.organizationName_default = Default Company Ltd 142 | 143 | # we can do this but it is not needed normally :-) 144 | #1.organizationName = Second Organization Name (eg, company) 145 | #1.organizationName_default = World Wide Web Pty Ltd 146 | 147 | organizationalUnitName = Organizational Unit Name (eg, section) 148 | #organizationalUnitName_default = 149 | 150 | commonName = Common Name (eg, your name or your server\'s hostname) 151 | commonName_max = 64 152 | 153 | emailAddress = Email Address 154 | emailAddress_max = 64 155 | 156 | # SET-ex3 = SET extension number 3 157 | 158 | [ req_attributes ] 159 | challengePassword = A challenge password 160 | challengePassword_min = 4 161 | challengePassword_max = 20 162 | 163 | unstructuredName = An optional company name 164 | 165 | [ usr_cert ] 166 | 167 | # These extensions are added when 'ca' signs a request. 168 | 169 | # This goes against PKIX guidelines but some CAs do it and some software 170 | # requires this to avoid interpreting an end user certificate as a CA. 171 | 172 | basicConstraints=CA:FALSE 173 | 174 | # Here are some examples of the usage of nsCertType. If it is omitted 175 | # the certificate can be used for anything *except* object signing. 176 | 177 | # This is OK for an SSL server. 178 | # nsCertType = server 179 | 180 | # For an object signing certificate this would be used. 181 | # nsCertType = objsign 182 | 183 | # For normal client use this is typical 184 | # nsCertType = client, email 185 | 186 | # and for everything including object signing: 187 | # nsCertType = client, email, objsign 188 | 189 | # This is typical in keyUsage for a client certificate. 190 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 191 | 192 | # This will be displayed in Netscape's comment listbox. 193 | nsComment = "OpenSSL Generated Certificate" 194 | 195 | # PKIX recommendations harmless if included in all certificates. 196 | subjectKeyIdentifier=hash 197 | authorityKeyIdentifier=keyid,issuer 198 | 199 | # This stuff is for subjectAltName and issuerAltname. 200 | # Import the email address. 201 | # subjectAltName=email:copy 202 | # An alternative to produce certificates that aren't 203 | # deprecated according to PKIX. 204 | # subjectAltName=email:move 205 | 206 | # Copy subject details 207 | # issuerAltName=issuer:copy 208 | 209 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem 210 | #nsBaseUrl 211 | #nsRevocationUrl 212 | #nsRenewalUrl 213 | #nsCaPolicyUrl 214 | #nsSslServerName 215 | 216 | # This is required for TSA certificates. 217 | # extendedKeyUsage = critical,timeStamping 218 | 219 | [ v3_req ] 220 | 221 | # Extensions to add to a certificate request 222 | 223 | basicConstraints = CA:FALSE 224 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 225 | 226 | [ v3_ca ] 227 | 228 | 229 | # Extensions for a typical CA 230 | 231 | 232 | # PKIX recommendation. 233 | 234 | subjectKeyIdentifier=hash 235 | 236 | authorityKeyIdentifier=keyid:always,issuer 237 | 238 | # This is what PKIX recommends but some broken software chokes on critical 239 | # extensions. 240 | #basicConstraints = critical,CA:true 241 | # So we do this instead. 242 | basicConstraints = CA:true 243 | 244 | # Key usage: this is typical for a CA certificate. However since it will 245 | # prevent it being used as an test self-signed certificate it is best 246 | # left out by default. 247 | # keyUsage = cRLSign, keyCertSign 248 | 249 | # Some might want this also 250 | # nsCertType = sslCA, emailCA 251 | 252 | # Include email address in subject alt name: another PKIX recommendation 253 | # subjectAltName=email:copy 254 | # Copy issuer details 255 | # issuerAltName=issuer:copy 256 | 257 | # DER hex encoding of an extension: beware experts only! 258 | # obj=DER:02:03 259 | # Where 'obj' is a standard or added object 260 | # You can even override a supported extension: 261 | # basicConstraints= critical, DER:30:03:01:01:FF 262 | 263 | [ crl_ext ] 264 | 265 | # CRL extensions. 266 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. 267 | 268 | # issuerAltName=issuer:copy 269 | authorityKeyIdentifier=keyid:always 270 | 271 | [ proxy_cert_ext ] 272 | # These extensions should be added when creating a proxy certificate 273 | 274 | # This goes against PKIX guidelines but some CAs do it and some software 275 | # requires this to avoid interpreting an end user certificate as a CA. 276 | 277 | basicConstraints=CA:FALSE 278 | 279 | # Here are some examples of the usage of nsCertType. If it is omitted 280 | # the certificate can be used for anything *except* object signing. 281 | 282 | # This is OK for an SSL server. 283 | # nsCertType = server 284 | 285 | # For an object signing certificate this would be used. 286 | # nsCertType = objsign 287 | 288 | # For normal client use this is typical 289 | # nsCertType = client, email 290 | 291 | # and for everything including object signing: 292 | # nsCertType = client, email, objsign 293 | 294 | # This is typical in keyUsage for a client certificate. 295 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 296 | 297 | # This will be displayed in Netscape's comment listbox. 298 | nsComment = "OpenSSL Generated Certificate" 299 | 300 | # PKIX recommendations harmless if included in all certificates. 301 | subjectKeyIdentifier=hash 302 | authorityKeyIdentifier=keyid,issuer 303 | 304 | # This stuff is for subjectAltName and issuerAltname. 305 | # Import the email address. 306 | # subjectAltName=email:copy 307 | # An alternative to produce certificates that aren't 308 | # deprecated according to PKIX. 309 | # subjectAltName=email:move 310 | 311 | # Copy subject details 312 | # issuerAltName=issuer:copy 313 | 314 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem 315 | #nsBaseUrl 316 | #nsRevocationUrl 317 | #nsRenewalUrl 318 | #nsCaPolicyUrl 319 | #nsSslServerName 320 | 321 | # This really needs to be in place for it to be a proxy certificate. 322 | proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo 323 | 324 | #################################################################### 325 | [ tsa ] 326 | 327 | default_tsa = tsa_config1 # the default TSA section 328 | 329 | [ tsa_config1 ] 330 | 331 | # These are used by the TSA reply generation only. 332 | dir = ./demoCA # TSA root directory 333 | serial = $dir/tsaserial # The current serial number (mandatory) 334 | crypto_device = builtin # OpenSSL engine to use for signing 335 | signer_cert = $dir/tsacert.pem # The TSA signing certificate 336 | # (optional) 337 | certs = $dir/cacert.pem # Certificate chain to include in reply 338 | # (optional) 339 | signer_key = $dir/private/tsakey.pem # The TSA private key (optional) 340 | 341 | default_policy = tsa_policy1 # Policy if request did not specify it 342 | # (optional) 343 | other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) 344 | digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) 345 | accuracy = secs:1, millisecs:500, microsecs:100 # (optional) 346 | clock_precision_digits = 0 # number of digits after dot. (optional) 347 | ordering = yes # Is ordering defined for timestamps? 348 | # (optional, default: no) 349 | tsa_name = yes # Must the TSA name be included in the reply? 350 | # (optional, default: no) 351 | ess_cert_id_chain = no # Must the ESS cert id chain be included? 352 | # (optional, default: no) 353 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.59) 2 | AC_INIT(deo, 0.5.1) 3 | AC_CANONICAL_SYSTEM 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | 6 | AC_PROG_CC_C99 7 | AC_PROG_MKDIR_P 8 | AC_PROG_SED 9 | 10 | AM_INIT_AUTOMAKE([subdir-objects foreign std-options]) 11 | AM_SILENT_RULES([yes]) 12 | AM_PROG_CC_C_O 13 | AM_PATH_PYTHON 14 | 15 | LT_INIT([disable-static]) 16 | LT_SYS_MODULE_EXT 17 | 18 | AC_CHECK_FILE([/usr/lib/dracut/modules.d], 19 | [AC_SUBST([dracutdir], [/usr/lib/dracut/modules.d/60deo])], 20 | [AC_MSG_ERROR(not found)]) 21 | 22 | PKG_PROG_PKG_CONFIG([0.25]) 23 | 24 | PKG_CHECK_MODULES([OPENSSL], [openssl]) 25 | PKG_CHECK_MODULES([CRYPTSETUP], [libcryptsetup]) 26 | PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd], [], [PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd-daemon], [], AC_MSG_ERROR([libsystemd/systemd-devel not found]))]) 27 | 28 | PKG_CHECK_EXISTS([systemd], [], [AC_MSG_ERROR([systemd not found])]) 29 | AC_ARG_WITH([systemdsystemunitdir], 30 | AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), 31 | [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) 32 | AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) 33 | 34 | 35 | AC_SUBST([DEO_SOCKET], [5700]) 36 | AC_SUBST([DEO_CONF], [${sysconfdir}/${PACKAGE_NAME}]) 37 | AC_SUBST([plugindir], [${libdir}/${PACKAGE_NAME}/plugins]) 38 | 39 | AX_DEFINE_DIR(DEO_SOCKET, DEO_SOCKET, [Default deo socket]) 40 | AX_DEFINE_DIR(DEO_CONF, DEO_CONF, [Location of deo configuration]) 41 | AX_DEFINE_DIR(DEO_PLUGINS, plugindir, [Location of deo plugins]) 42 | 43 | AC_CONFIG_FILES(Makefile src/Makefile src/plugins/Makefile) 44 | AC_OUTPUT 45 | 46 | -------------------------------------------------------------------------------- /deo.1: -------------------------------------------------------------------------------- 1 | .TH "deo" "1" "August 2015" "" "" 2 | .SH NAME 3 | deo \- network bound encryption 4 | .SH SYNOPSIS 5 | 6 | deo query -a ANCHORS HOST[:PORT] 7 | .br 8 | deo encrypt -a ANCHORS HOST[:PORT]|FILE ... 9 | .br 10 | deo decrypt [-a ANCHORS] [HOST[:PORT] ...] 11 | .br 12 | deo targets 13 | 14 | deo cryptsetup [-k KEYDIR] -d DEVICE -a ANCHORS HOST[:PORT]|FILE ... 15 | 16 | .SH DESCRIPTION 17 | Deo encrypts data to one or more network decryption services (called targets). 18 | 19 | A target need not be online at encryption time (an offline copy of the 20 | target's encryption certificate is sufficient). However, once data is encrypted 21 | to the targets, it cannot be decrypted without contacting the one of them. 22 | 23 | Note that none of the encrypted data ever passes over the wire. Instead, only a 24 | randomly generated key is transferred. This means that a compromise of the 25 | target service or network transport does not compromise your data. 26 | 27 | .SH TRUST ANCHORS 28 | Most commands take a "-a ANCHORS" argument. This argument specifies a 29 | PEM-encoded file containing the root certificates to trust for all operations. 30 | In the most common use, this will contain your institution's CA signing 31 | certificate or one of its sub-CA certificates. 32 | 33 | .B SECURITY NOTE: 34 | It is important that you not use a third-party CA for this 35 | trust. If you do, it will severely compromise your security. 36 | 37 | .SH USAGE 38 | .B deo query -a ANCHORS HOST[:PORT] 39 | 40 | .in +4 41 | Downloads a target's encryption certificate chain for offline use. 42 | 43 | If the certificate chain passes trust validation, it is printed to standard 44 | output in PEM format. 45 | .in 46 | 47 | .B deo encrypt -a ANCHORS HOST[:PORT]|FILE ... 48 | 49 | .in +4 50 | Encrypts data to all specified targets. 51 | 52 | Plaintext data is passed on standard input and the encrypted ciphertext is 53 | returned on standard output. The specified target(s) may be either: 54 | 55 | * a hostname with optional port 56 | * a PEM-encoded file ( 57 | .B deo query 58 | ) 59 | .in 60 | 61 | .B deo decrypt [-a ANCHORS] [HOST[:PORT] ...] 62 | 63 | .in +4 64 | Decrypts data using the first available target. 65 | 66 | The ciphertext output data from 67 | .B deo encrypt 68 | is passed to decrypt on standard 69 | input. If a trusted target can be contacted and the operation succeeds, the 70 | plaintext data is returned on standard output. 71 | 72 | No arguments are required because 73 | .B deo encrypt 74 | embeds the anchors 75 | and targets used into its output. However, if other anchors or targets are 76 | required for some reason, they can be specified here. 77 | .in 78 | 79 | .B deo targets 80 | 81 | .in +4 82 | Prints the targets embedded into the encrypted ciphertext. 83 | 84 | The ciphertext output data from 85 | .B deo encrypt 86 | is passed to 87 | .B deo targets 88 | on standard input. The embedded targets are printed to standard output in 89 | priority order. 90 | .in 91 | 92 | .B deo cryptsetup [-k KEYDIR] -d DEVICE -a ANCHORS HOST[:PORT]|FILE ... 93 | 94 | .in +4 95 | Binds a LUKS-encrypted DEVICE to all specified targets. 96 | 97 | This command works essentially like 98 | .B deo encrypt 99 | except that it binds LUKS-encrypted disks instead of encrypting plaintext data. 100 | A new cryptographically-secure random key is installed into the LUKS header of 101 | the specified DEVICE. This key is then encrypted using 102 | .B deo encrypt 103 | and stored in the KEYDIR using the UUID of the specified DEVICE as the filename. 104 | When the system reboots, the disk will automatically unlock if and only if it 105 | can reach one of the trusted targets. 106 | 107 | .B NOTE: 108 | If you are encrypting the root filesystem, you 109 | .B MUST 110 | run 111 | .B dracut 112 | to rebuild your initramfs. For more information, see the 113 | .B dracut(8) 114 | documentation. 115 | .in -------------------------------------------------------------------------------- /m4/ax_check_gnu_make.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_GNU_MAKE() 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro searches for a GNU version of make. If a match is found, the 12 | # makefile variable `ifGNUmake' is set to the empty string, otherwise it 13 | # is set to "#". This is useful for including a special features in a 14 | # Makefile, which cannot be handled by other versions of make. The 15 | # variable _cv_gnu_make_command is set to the command to invoke GNU make 16 | # if it exists, the empty string otherwise. 17 | # 18 | # Here is an example of its use: 19 | # 20 | # Makefile.in might contain: 21 | # 22 | # # A failsafe way of putting a dependency rule into a makefile 23 | # $(DEPEND): 24 | # $(CC) -MM $(srcdir)/*.c > $(DEPEND) 25 | # 26 | # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) 27 | # @ifGNUmake@ include $(DEPEND) 28 | # @ifGNUmake@ endif 29 | # 30 | # Then configure.in would normally contain: 31 | # 32 | # AX_CHECK_GNU_MAKE() 33 | # AC_OUTPUT(Makefile) 34 | # 35 | # Then perhaps to cause gnu make to override any other make, we could do 36 | # something like this (note that GNU make always looks for GNUmakefile 37 | # first): 38 | # 39 | # if ! test x$_cv_gnu_make_command = x ; then 40 | # mv Makefile GNUmakefile 41 | # echo .DEFAULT: > Makefile ; 42 | # echo \ $_cv_gnu_make_command \$@ >> Makefile; 43 | # fi 44 | # 45 | # Then, if any (well almost any) other make is called, and GNU make also 46 | # exists, then the other make wraps the GNU make. 47 | # 48 | # LICENSE 49 | # 50 | # Copyright (c) 2008 John Darrington 51 | # 52 | # Copying and distribution of this file, with or without modification, are 53 | # permitted in any medium without royalty provided the copyright notice 54 | # and this notice are preserved. This file is offered as-is, without any 55 | # warranty. 56 | 57 | #serial 7 58 | 59 | AC_DEFUN([AX_CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command, 60 | _cv_gnu_make_command='' ; 61 | dnl Search all the common names for GNU make 62 | for a in "$MAKE" make gmake gnumake ; do 63 | if test -z "$a" ; then continue ; fi ; 64 | if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then 65 | _cv_gnu_make_command=$a ; 66 | break; 67 | fi 68 | done ; 69 | ) ; 70 | dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise 71 | if test "x$_cv_gnu_make_command" != "x" ; then 72 | ifGNUmake='' ; 73 | else 74 | ifGNUmake='#' ; 75 | AC_MSG_RESULT("Not found"); 76 | fi 77 | AC_SUBST(ifGNUmake) 78 | ] ) 79 | -------------------------------------------------------------------------------- /m4/ax_define_dir.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_define_dir.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro sets VARNAME to the expansion of the DIR variable, taking 12 | # care of fixing up ${prefix} and such. 13 | # 14 | # VARNAME is then offered as both an output variable and a C preprocessor 15 | # symbol. 16 | # 17 | # Example: 18 | # 19 | # AX_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.]) 20 | # 21 | # LICENSE 22 | # 23 | # Copyright (c) 2008 Stepan Kasal 24 | # Copyright (c) 2008 Andreas Schwab 25 | # Copyright (c) 2008 Guido U. Draheim 26 | # Copyright (c) 2008 Alexandre Oliva 27 | # 28 | # Copying and distribution of this file, with or without modification, are 29 | # permitted in any medium without royalty provided the copyright notice 30 | # and this notice are preserved. This file is offered as-is, without any 31 | # warranty. 32 | 33 | #serial 6 34 | 35 | AU_ALIAS([AC_DEFINE_DIR], [AX_DEFINE_DIR]) 36 | AC_DEFUN([AX_DEFINE_DIR], [ 37 | prefix_NONE= 38 | exec_prefix_NONE= 39 | test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix 40 | test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix 41 | dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn 42 | dnl refers to ${prefix}. Thus we have to use `eval' twice. 43 | eval ax_define_dir="\"[$]$2\"" 44 | eval ax_define_dir="\"$ax_define_dir\"" 45 | AC_SUBST($1, "$ax_define_dir") 46 | AC_DEFINE_UNQUOTED($1, "$ax_define_dir", [$3]) 47 | test "$prefix_NONE" && prefix=NONE 48 | test "$exec_prefix_NONE" && exec_prefix=NONE 49 | ]) 50 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = plugins 2 | 3 | AM_CFLAGS = \ 4 | -Wall \ 5 | -Wextra \ 6 | -Werror \ 7 | -Wno-missing-field-initializers \ 8 | -Wno-unused-parameter \ 9 | -Wno-sign-compare \ 10 | -Wstrict-aliasing 11 | 12 | bin_PROGRAMS = deo 13 | deo_CFLAGS = $(AM_CFLAGS) @OPENSSL_CFLAGS@ -ldl 14 | deo_LDADD = @OPENSSL_LIBS@ 15 | deo_LDFLAGS = -rdynamic 16 | deo_SOURCES = \ 17 | asn1.c \ 18 | asn1.h \ 19 | cleanup.c \ 20 | cleanup.h \ 21 | d2i.c \ 22 | d2i.h \ 23 | misc.c \ 24 | misc.h \ 25 | main.c \ 26 | main.h 27 | -------------------------------------------------------------------------------- /src/asn1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "asn1.h" 20 | 21 | ASN1_SEQUENCE(DEO_KEY) = { 22 | ASN1_SIMPLE(DEO_KEY, hash, ASN1_OCTET_STRING), 23 | ASN1_SIMPLE(DEO_KEY, key, ASN1_OCTET_STRING), 24 | } ASN1_SEQUENCE_END(DEO_KEY) 25 | 26 | ASN1_SEQUENCE(DEO_MSG_DEC_REQ) = { 27 | ASN1_SIMPLE(DEO_MSG_DEC_REQ, cipher, ASN1_OBJECT), 28 | ASN1_SIMPLE(DEO_MSG_DEC_REQ, digest, ASN1_OBJECT), 29 | 30 | ASN1_SEQUENCE_OF(DEO_MSG_DEC_REQ, keys, DEO_KEY), 31 | ASN1_SIMPLE(DEO_MSG_DEC_REQ, data, ASN1_OCTET_STRING), 32 | ASN1_SIMPLE(DEO_MSG_DEC_REQ, tag, ASN1_OCTET_STRING), 33 | ASN1_SIMPLE(DEO_MSG_DEC_REQ, iv, ASN1_OCTET_STRING), 34 | } ASN1_SEQUENCE_END(DEO_MSG_DEC_REQ) 35 | 36 | ASN1_CHOICE(DEO_MSG) = { 37 | ASN1_EXP(DEO_MSG, value.err, ASN1_ENUMERATED, DEO_MSG_TYPE_ERR), 38 | ASN1_EXP(DEO_MSG, value.crt_req, ASN1_NULL, DEO_MSG_TYPE_CRT_REQ), 39 | ASN1_EXP_SEQUENCE_OF(DEO_MSG, value.crt_rep, X509, DEO_MSG_TYPE_CRT_REP), 40 | ASN1_EXP(DEO_MSG, value.dec_req, DEO_MSG_DEC_REQ, DEO_MSG_TYPE_DEC_REQ), 41 | ASN1_EXP(DEO_MSG, value.dec_rep, ASN1_OCTET_STRING, DEO_MSG_TYPE_DEC_REP), 42 | } ASN1_CHOICE_END(DEO_MSG) 43 | 44 | ASN1_SEQUENCE(DEO_HEADER) = { 45 | ASN1_SEQUENCE_OF(DEO_HEADER, targets, ASN1_UTF8STRING), 46 | ASN1_SEQUENCE_OF(DEO_HEADER, anchors, X509), 47 | ASN1_SIMPLE(DEO_HEADER, req, DEO_MSG_DEC_REQ), 48 | ASN1_SIMPLE(DEO_HEADER, iv, ASN1_OCTET_STRING), 49 | } ASN1_SEQUENCE_END(DEO_HEADER) 50 | 51 | IMPLEMENT_ASN1_FUNCTIONS(DEO_KEY) 52 | IMPLEMENT_ASN1_FUNCTIONS(DEO_MSG_DEC_REQ) 53 | IMPLEMENT_ASN1_FUNCTIONS(DEO_MSG) 54 | IMPLEMENT_ASN1_FUNCTIONS(DEO_HEADER) 55 | 56 | DEFINE_CLEANUP(DEO_KEY) 57 | DEFINE_CLEANUP(DEO_MSG_DEC_REQ) 58 | DEFINE_CLEANUP(DEO_MSG) 59 | DEFINE_CLEANUP(DEO_HEADER) 60 | 61 | const char * 62 | deo_err_string(DEO_ERR err) 63 | { 64 | switch (err) { 65 | case DEO_ERR_NONE: 66 | return ""; 67 | 68 | case DEO_ERR_INTERNAL: 69 | return "internal error"; 70 | 71 | case DEO_ERR_NOSUPPORT_CIPHER: 72 | return "cipher unsupported"; 73 | 74 | case DEO_ERR_NOSUPPORT_DIGEST: 75 | return "digest unsupported"; 76 | 77 | case DEO_ERR_NOTFOUND_KEY: 78 | return "key not found"; 79 | } 80 | 81 | return ""; 82 | } 83 | -------------------------------------------------------------------------------- /src/asn1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "cleanup.h" 22 | 23 | #include 24 | #include 25 | 26 | typedef struct { 27 | ASN1_OCTET_STRING *hash; 28 | ASN1_OCTET_STRING *key; 29 | } DEO_KEY; 30 | 31 | typedef struct { 32 | ASN1_OBJECT *cipher; 33 | ASN1_OBJECT *digest; 34 | 35 | STACK_OF(DEO_KEY) *keys; 36 | ASN1_OCTET_STRING *data; 37 | ASN1_OCTET_STRING *tag; 38 | ASN1_OCTET_STRING *iv; 39 | } DEO_MSG_DEC_REQ; 40 | 41 | typedef enum { 42 | DEO_ERR_NONE = 0, 43 | DEO_ERR_INTERNAL, 44 | DEO_ERR_NOSUPPORT_DIGEST, 45 | DEO_ERR_NOSUPPORT_CIPHER, 46 | DEO_ERR_NOTFOUND_KEY, 47 | } DEO_ERR; 48 | 49 | typedef enum { 50 | DEO_MSG_TYPE_ERR = 0, 51 | DEO_MSG_TYPE_CRT_REQ, 52 | DEO_MSG_TYPE_CRT_REP, 53 | DEO_MSG_TYPE_DEC_REQ, 54 | DEO_MSG_TYPE_DEC_REP, 55 | } DEO_MSG_TYPE; 56 | 57 | typedef struct { 58 | DEO_MSG_TYPE type; 59 | union { 60 | ASN1_ENUMERATED *err; 61 | ASN1_NULL *crt_req; 62 | STACK_OF(X509) *crt_rep; 63 | DEO_MSG_DEC_REQ *dec_req; 64 | ASN1_OCTET_STRING *dec_rep; 65 | } value; 66 | } DEO_MSG; 67 | 68 | typedef struct { 69 | STACK_OF(ASN1_UTF8STRING) *targets; 70 | STACK_OF(X509) *anchors; 71 | DEO_MSG_DEC_REQ *req; 72 | ASN1_OCTET_STRING *iv; 73 | } DEO_HEADER; 74 | 75 | DECLARE_ASN1_FUNCTIONS(DEO_KEY) 76 | DECLARE_ASN1_FUNCTIONS(DEO_MSG_DEC_REQ) 77 | DECLARE_ASN1_FUNCTIONS(DEO_MSG) 78 | DECLARE_ASN1_FUNCTIONS(DEO_HEADER) 79 | 80 | DECLARE_CLEANUP(DEO_KEY); 81 | DECLARE_CLEANUP(DEO_MSG_DEC_REQ); 82 | DECLARE_CLEANUP(DEO_MSG); 83 | DECLARE_CLEANUP(DEO_HEADER); 84 | 85 | #define SEND_ERR(bio, err) \ 86 | ASN1_item_i2d_bio(&DEO_MSG_it, bio, &(DEO_MSG) { \ 87 | .type = DEO_MSG_TYPE_ERR, \ 88 | .value.err = &(ASN1_ENUMERATED) { \ 89 | .data = &(uint8_t) { err }, \ 90 | .type = V_ASN1_ENUMERATED, \ 91 | .length = 1, \ 92 | } \ 93 | }) 94 | 95 | const char * 96 | deo_err_string(DEO_ERR err); 97 | -------------------------------------------------------------------------------- /src/cleanup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "cleanup.h" 20 | #include 21 | #include 22 | 23 | void 24 | cleanup_uint8_t(uint8_t **x) 25 | { 26 | if (x == NULL) return; 27 | free(*x); 28 | } 29 | 30 | void 31 | cleanup_char(char **x) 32 | { 33 | if (x == NULL) return; 34 | free(*x); 35 | } 36 | 37 | void 38 | cleanup_FILE(FILE **x) 39 | { 40 | if (x == NULL || *x == NULL) return; 41 | fclose(*x); 42 | } 43 | 44 | void 45 | cleanup_DIR(DIR **x) 46 | { 47 | if (x == NULL || *x == NULL) return; 48 | closedir(*x); 49 | } 50 | 51 | void 52 | cleanup_fd(int *fd) 53 | { 54 | if (fd == NULL || *fd < 0) 55 | return; 56 | 57 | close(*fd); 58 | } 59 | 60 | DEFINE_CLEANUP_STACK(X509_INFO) 61 | DEFINE_CLEANUP_STACK(X509) 62 | 63 | DEFINE_CLEANUP(ASN1_OCTET_STRING) 64 | DEFINE_CLEANUP(EVP_CIPHER_CTX) 65 | DEFINE_CLEANUP(EVP_PKEY) 66 | DEFINE_CLEANUP(X509_STORE_CTX) 67 | DEFINE_CLEANUP(X509_STORE) 68 | DEFINE_CLEANUP(X509) 69 | DEFINE_CLEANUP(SSL_CTX) 70 | DEFINE_CLEANUP(SSL) 71 | 72 | void 73 | cleanup_BIO(BIO **x) { 74 | if (x == NULL) return; 75 | BIO_free_all(*x); 76 | } 77 | 78 | void 79 | cleanup_BIGNUM(BIGNUM **x) { 80 | if (x == NULL) return; 81 | BN_free(*x); 82 | } 83 | -------------------------------------------------------------------------------- /src/cleanup.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #define DECLARE_CLEANUP(type) \ 29 | void cleanup_ ## type(type **x) 30 | 31 | #define DEFINE_CLEANUP(type) \ 32 | void cleanup_ ## type(type **x) { \ 33 | if (x == NULL || *x == NULL) return; \ 34 | type ## _free(*x); \ 35 | } 36 | 37 | 38 | #define DECLARE_CLEANUP_STACK(type) \ 39 | void cleanup_sk_ ## type(STACK_OF(type) **x) 40 | 41 | #define DEFINE_CLEANUP_STACK(type) \ 42 | void cleanup_sk_ ## type(STACK_OF(type) **x) { \ 43 | if (x == NULL || *x == NULL) return; \ 44 | sk_ ## type ## _pop_free(*x, type ## _free); \ 45 | } 46 | 47 | #define AUTO(type, name) \ 48 | __attribute__((cleanup(cleanup_ ## type))) type *name = NULL 49 | 50 | #define AUTO_FD(name) \ 51 | __attribute__((cleanup(cleanup_fd))) int name = -1 52 | 53 | #define AUTO_STACK(type, name) \ 54 | __attribute__((cleanup(cleanup_sk_ ## type))) STACK_OF(type) *name = NULL 55 | 56 | #define STEAL(name) \ 57 | ({ __typeof__(name) __tmp = name; name = NULL; __tmp; }) 58 | 59 | DECLARE_CLEANUP(uint8_t); 60 | DECLARE_CLEANUP(char); 61 | DECLARE_CLEANUP(FILE); 62 | DECLARE_CLEANUP(DIR); 63 | 64 | void 65 | cleanup_fd(int *fd); 66 | 67 | DECLARE_CLEANUP_STACK(X509_INFO); 68 | DECLARE_CLEANUP_STACK(X509); 69 | 70 | DECLARE_CLEANUP(ASN1_OCTET_STRING); 71 | DECLARE_CLEANUP(EVP_CIPHER_CTX); 72 | DECLARE_CLEANUP(EVP_PKEY); 73 | DECLARE_CLEANUP(X509_STORE_CTX); 74 | DECLARE_CLEANUP(X509_STORE); 75 | DECLARE_CLEANUP(X509); 76 | DECLARE_CLEANUP(SSL_CTX); 77 | DECLARE_CLEANUP(SSL); 78 | DECLARE_CLEANUP(BIO); 79 | DECLARE_CLEANUP(BIGNUM); 80 | -------------------------------------------------------------------------------- /src/d2i.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "d2i.h" 20 | #include "cleanup.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define BLOCKSIZE 128 27 | 28 | #define BIT8 0b10000000 29 | 30 | #define IS_ID_SHORT(b) \ 31 | (((b) & V_ASN1_PRIMITIVE_TAG) != V_ASN1_PRIMITIVE_TAG) 32 | 33 | #define IS_ID_LAST(b) \ 34 | (((b) & BIT8) == 0) 35 | 36 | #define IS_LEN_SHORT(b) \ 37 | (((b) & BIT8) == 0) 38 | 39 | static ssize_t 40 | get_size(const uint8_t *buf, size_t len, BIGNUM *out) 41 | { 42 | int sz = -1; 43 | 44 | if (len == 0) 45 | return 1; 46 | 47 | if (BN_set_word(out, 0) <= 0) 48 | return -ENOMEM; 49 | 50 | for (size_t i = 0; i < len; i++) { 51 | if (sz > 0) { 52 | if (BN_lshift(out, out, 8) <= 0) 53 | return -ENOMEM; 54 | 55 | if (BN_add_word(out, buf[i]) <= 0) 56 | return -ENOMEM; 57 | } else if (sz < 0) { 58 | if (i == 0) { 59 | if (IS_ID_SHORT(buf[i])) 60 | sz++; 61 | } else if (IS_ID_LAST(buf[i])) 62 | sz++; 63 | } else if (sz == 0) { 64 | sz = buf[i] & ~BIT8; 65 | 66 | if (IS_LEN_SHORT(buf[i])) 67 | return BN_set_word(out, sz) > 0 ? 0 : -ENOMEM; 68 | 69 | if (i + 1 == len) 70 | return sz; 71 | } 72 | } 73 | 74 | return sz <= 0 ? 1 : 0; 75 | } 76 | 77 | void * 78 | d2i_bio_max(const ASN1_ITEM *it, BIO *in, void *x, unsigned int max) 79 | { 80 | AUTO(uint8_t, buf); 81 | AUTO(BIGNUM, size); 82 | AUTO(BIGNUM, cmp); 83 | size_t blocks = 0; 84 | ssize_t need = 0; 85 | size_t have = 0; 86 | int rd; 87 | 88 | size = BN_new(); 89 | if (size == NULL) 90 | return NULL; 91 | 92 | while (true) { 93 | need = get_size(buf, have, size); 94 | if (need < 0) 95 | return NULL; 96 | else if (need == 0) 97 | break; 98 | 99 | if (have + need > max) 100 | return NULL; 101 | 102 | if (have + (size_t) need >= blocks * BLOCKSIZE) { 103 | uint8_t *tmp; 104 | 105 | blocks = (have + need + BLOCKSIZE - 1) / BLOCKSIZE; 106 | tmp = realloc(buf, blocks * BLOCKSIZE); 107 | if (tmp == NULL) 108 | return NULL; 109 | 110 | buf = tmp; 111 | } 112 | 113 | rd = BIO_read(in, buf + have, need); 114 | have += need; 115 | if (rd <= 0 || rd != need) 116 | return NULL; 117 | } 118 | 119 | cmp = BN_new(); 120 | if (cmp == NULL) 121 | return NULL; 122 | 123 | if (BN_set_word(cmp, max) <= 0) 124 | return NULL; 125 | 126 | if (BN_cmp(size, cmp) > 0) 127 | return NULL; 128 | 129 | if (BN_set_word(cmp, BLOCKSIZE) <= 0) 130 | return NULL; 131 | 132 | while (true) { 133 | need = BLOCKSIZE; 134 | if (BN_cmp(size, cmp) < 0) 135 | need = BN_get_word(size); 136 | if (need == 0) 137 | break; 138 | 139 | if (BN_sub_word(size, need) <= 0) 140 | return NULL; 141 | 142 | if (have + (size_t) need >= blocks * BLOCKSIZE) { 143 | uint8_t *tmp; 144 | 145 | blocks = (have + need + BLOCKSIZE - 1) / BLOCKSIZE; 146 | tmp = realloc(buf, blocks * BLOCKSIZE); 147 | if (tmp == NULL) 148 | return NULL; 149 | 150 | buf = tmp; 151 | } 152 | 153 | while (need > 0) { 154 | rd = BIO_read(in, buf + have, need); 155 | if (rd <= 0) 156 | return NULL; 157 | 158 | have += rd; 159 | need -= rd; 160 | } 161 | } 162 | 163 | return ASN1_item_d2i(x, &(const uint8_t *) { buf }, have, it); 164 | } 165 | 166 | void * 167 | d2i_fp_max(const ASN1_ITEM *it, FILE *fp, void *x, unsigned int max) 168 | { 169 | AUTO(BIO, bio); 170 | 171 | bio = BIO_new_fp(fp, BIO_NOCLOSE); 172 | if (bio == NULL) 173 | return NULL; 174 | 175 | return d2i_bio_max(it, bio, x, max); 176 | } 177 | -------------------------------------------------------------------------------- /src/d2i.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #define DEO_MAX_INPUT (1024 * 512) 25 | 26 | void * 27 | d2i_bio_max(const ASN1_ITEM *it, BIO *in, void *x, unsigned int max); 28 | 29 | void * 30 | d2i_fp_max(const ASN1_ITEM *it, FILE *fp, void *x, unsigned int max); 31 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "main.h" 20 | #include "cleanup.h" 21 | #include "d2i.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define MAX_PLUGINS 128 31 | 32 | struct record { 33 | void *dll; 34 | char *name; 35 | deo_plugin plug; 36 | }; 37 | 38 | static struct { 39 | size_t cnt; 40 | struct record rec[MAX_PLUGINS]; 41 | } inv; 42 | 43 | static int 44 | cmp(const void *a, const void *b) 45 | { 46 | const struct record *aa = a; 47 | const struct record *bb = b; 48 | return strcmp(aa->name, bb->name); 49 | } 50 | 51 | static void 52 | onexit(void) 53 | { 54 | for (size_t i = 0; i < inv.cnt; i++) { 55 | dlclose(inv.rec[i].dll); 56 | free(inv.rec[i].name); 57 | } 58 | 59 | EVP_cleanup(); 60 | } 61 | 62 | static bool 63 | endswith(const char *haystack, const char *needle) 64 | { 65 | size_t hlen; 66 | size_t nlen; 67 | 68 | if (haystack == NULL || needle == NULL) 69 | return false; 70 | 71 | hlen = strlen(haystack); 72 | nlen = strlen(needle); 73 | 74 | if (hlen < nlen) 75 | return false; 76 | 77 | return strcmp(haystack + hlen - nlen, needle) == 0; 78 | } 79 | 80 | #define append(dst, src) strncat(dst, src, sizeof(dst) - strlen(dst) - 1) 81 | 82 | static const char * 83 | make_plugin_name(const char *plugindir, const char *name) 84 | { 85 | static char path[PATH_MAX]; 86 | 87 | memset(path, 0, sizeof(path)); 88 | 89 | append(path, plugindir); 90 | append(path, "/"); 91 | append(path, name); 92 | if (!endswith(path, LT_MODULE_EXT)) 93 | append(path, LT_MODULE_EXT); 94 | 95 | return path; 96 | } 97 | 98 | #include 99 | 100 | static void 101 | load_plugin(const char *path) 102 | { 103 | const deo_plugin *tmp = NULL; 104 | const char *tmpx; 105 | 106 | if (inv.cnt >= MAX_PLUGINS) 107 | error(EXIT_FAILURE, 0, "Too many plugins"); 108 | 109 | assert(endswith(path, LT_MODULE_EXT)); 110 | tmpx = strrchr(path, '/'); 111 | if (tmpx == NULL) 112 | tmpx = path; 113 | else 114 | tmpx++; 115 | inv.rec[inv.cnt].name = strndup(tmpx, strlen(tmpx) - strlen(LT_MODULE_EXT)); 116 | 117 | inv.rec[inv.cnt].dll = dlopen(path, RTLD_NOW | RTLD_LOCAL); 118 | if (inv.rec[inv.cnt].dll == NULL) 119 | error(EXIT_FAILURE, 0, "Unable to open plugin: %s", dlerror()); 120 | 121 | tmp = dlsym(inv.rec[inv.cnt++].dll, "deo"); 122 | if (tmp == NULL) 123 | error(EXIT_FAILURE, 0, "Unable to read plugin: %s", dlerror()); 124 | 125 | inv.rec[inv.cnt - 1].plug = *tmp; 126 | } 127 | 128 | int 129 | main(int argc, char *argv[]) 130 | { 131 | const char *plugindir = getenv("DEO_PLUGINS"); 132 | int ret = EXIT_FAILURE; 133 | int max = 0; 134 | AUTO(DIR, dir); 135 | 136 | if (plugindir == NULL) 137 | plugindir = DEO_PLUGINS; 138 | 139 | SSL_load_error_strings(); 140 | SSL_library_init(); 141 | OpenSSL_add_all_algorithms(); 142 | atexit(onexit); 143 | 144 | if (argc > 1) { 145 | const char *plugin = make_plugin_name(plugindir, argv[1]); 146 | if (access(plugin, R_OK) == 0) { 147 | load_plugin(plugin); 148 | return inv.rec[inv.cnt - 1].plug.cmd(argc, argv); 149 | } else { 150 | fprintf(stderr, "Unknown command: %s\n\n", argv[1]); 151 | } 152 | } 153 | 154 | dir = opendir(plugindir); 155 | if (dir == NULL) 156 | error(EXIT_FAILURE, errno, "Unable to open plugindir (%s)", plugindir); 157 | 158 | for (struct dirent *de = readdir(dir); de != NULL; de = readdir(dir)) { 159 | const char *plugin = make_plugin_name(plugindir, de->d_name); 160 | 161 | if (!deo_isreg(plugindir, de)) 162 | continue; 163 | 164 | if (!endswith(de->d_name, LT_MODULE_EXT)) 165 | continue; 166 | 167 | load_plugin(plugin); 168 | } 169 | 170 | qsort(inv.rec, inv.cnt, sizeof(struct record), cmp); 171 | 172 | for (size_t i = 0; i < inv.cnt; i++) { 173 | int tmp = strlen(inv.rec[i].name); 174 | if (tmp > max) 175 | max = tmp; 176 | } 177 | 178 | fprintf(stderr, "Usage: %s ...\n\n", argv[0]); 179 | for (size_t i = 0; i < inv.cnt; i++) { 180 | /* Don't list plugins without summaries. */ 181 | if (inv.rec[i].plug.summary == NULL) 182 | continue; 183 | 184 | fprintf(stderr, "%*s %s\n", max + 4, 185 | inv.rec[i].name, 186 | inv.rec[i].plug.summary); 187 | } 188 | fprintf(stderr, "\n"); 189 | 190 | return ret; 191 | } 192 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asn1.h" 22 | #include "d2i.h" 23 | #include "misc.h" 24 | 25 | typedef struct { 26 | int (*cmd)(int argc, char *argv[]); 27 | const char *summary; 28 | } deo_plugin; 29 | -------------------------------------------------------------------------------- /src/misc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "misc.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | bool 30 | deo_validate(const STACK_OF(X509) *anchors, STACK_OF(X509) *chain) 31 | { 32 | AUTO(X509_STORE_CTX, sctx); 33 | AUTO(X509_STORE, store); 34 | 35 | if (chain == NULL || sk_X509_num(chain) == 0) 36 | return false; 37 | 38 | store = X509_STORE_new(); 39 | if (store == NULL) 40 | return false; 41 | 42 | if (anchors == NULL || sk_X509_num(anchors) == 0) { 43 | if (X509_STORE_set_default_paths(store) <= 0) 44 | return false; 45 | } else { 46 | for (int i = 0; i < sk_X509_num(anchors); i++) { 47 | X509 *cert = sk_X509_value(anchors, i); 48 | X509_STORE_add_cert(store, cert); 49 | } 50 | } 51 | 52 | sctx = X509_STORE_CTX_new(); 53 | if (sctx == NULL) 54 | return false; 55 | 56 | if (X509_STORE_CTX_init(sctx, store, sk_X509_value(chain, 0), chain) <= 0) 57 | return false; 58 | 59 | return X509_verify_cert(sctx) > 0; 60 | } 61 | 62 | bool 63 | deo_load(FILE *fp, STACK_OF(X509) *certs) 64 | { 65 | AUTO_STACK(X509_INFO, infos); 66 | 67 | infos = PEM_X509_INFO_read(fp, NULL, NULL, NULL); 68 | if (infos == NULL) 69 | return false; 70 | 71 | for (int i = 0; i < sk_X509_INFO_num(infos); i++) { 72 | X509_INFO *info = sk_X509_INFO_value(infos, i); 73 | X509 *cert; 74 | 75 | if (info->x509 == NULL) 76 | continue; 77 | 78 | cert = X509_dup(info->x509); 79 | if (cert == NULL) 80 | return false; 81 | 82 | if (sk_X509_push(certs, cert) <= 0) { 83 | X509_free(cert); 84 | return false; 85 | } 86 | } 87 | 88 | return true; 89 | } 90 | 91 | static bool 92 | equals(ASN1_IA5STRING *ia5str, const char *str) 93 | { 94 | const char *s = (const char *) ASN1_STRING_data(ia5str); 95 | int l = ASN1_STRING_length(ia5str); 96 | 97 | if (l != strlen(str)) 98 | return false; 99 | 100 | for (int i = 0; i < l; i++) { 101 | if (s[i] == '\0') 102 | return false; 103 | } 104 | 105 | return strncasecmp(s, str, l) == 0; 106 | } 107 | 108 | static bool 109 | verify_hostname(BIO *io, const char *hostname) 110 | { 111 | STACK_OF(GENERAL_NAME) *sans = NULL; 112 | X509_NAME_ENTRY *e = NULL; 113 | X509_NAME *name = NULL; 114 | X509 *cert = NULL; 115 | SSL *ssl = NULL; 116 | int idx = -1; 117 | 118 | BIO_get_ssl(io, &ssl); 119 | if (ssl == NULL) 120 | return false; 121 | 122 | cert = SSL_get_peer_certificate(ssl); 123 | if (cert == NULL) 124 | return false; 125 | 126 | sans = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); 127 | if (sans != NULL) { 128 | for (int i = 0; i < sk_GENERAL_NAME_num(sans); i++) { 129 | const GENERAL_NAME *san; 130 | 131 | san = sk_GENERAL_NAME_value(sans, i); 132 | if (san == NULL) 133 | continue; 134 | 135 | if (san->type != GEN_DNS) 136 | continue; 137 | 138 | if (equals(san->d.dNSName, hostname)) { 139 | sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free); 140 | return true; 141 | } 142 | } 143 | 144 | sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free); 145 | return false; 146 | } 147 | 148 | name = X509_get_subject_name(cert); 149 | if (name == NULL) 150 | return false; 151 | 152 | idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1); 153 | if (idx < 0) 154 | return false; 155 | 156 | e = X509_NAME_get_entry(name, idx); 157 | if (e == NULL) 158 | return false; 159 | 160 | return equals(X509_NAME_ENTRY_get_data(e), hostname); 161 | } 162 | 163 | DEO_MSG * 164 | deo_request(const STACK_OF(X509) *anchors, const ASN1_UTF8STRING *target, 165 | const DEO_MSG *req) 166 | { 167 | const int ops = SSL_OP_NO_COMPRESSION | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; 168 | char trgt[target->length + 1]; 169 | AUTO(SSL_CTX, ctx); 170 | AUTO(BIO, io); 171 | 172 | for (size_t i = 0; i < target->length; i++) { 173 | if (target->data[i] == '\0') 174 | return NULL; 175 | } 176 | 177 | memcpy(trgt, target->data, target->length); 178 | trgt[target->length] = '\0'; 179 | 180 | if (anchors == NULL || sk_X509_num(anchors) == 0) 181 | return NULL; 182 | 183 | ctx = SSL_CTX_new(SSLv23_client_method()); 184 | if (ctx == NULL) 185 | return NULL; 186 | 187 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 188 | if (SSL_CTX_set_options(ctx, ops) <= 0) 189 | return NULL; 190 | 191 | for (int i = 0; anchors != NULL && i < sk_X509_num(anchors); i++) { 192 | X509 *cert = sk_X509_value(anchors, i); 193 | X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert); 194 | } 195 | 196 | io = BIO_new_ssl_connect(ctx); 197 | if (io == NULL) 198 | return NULL; 199 | 200 | BIO_set_conn_port(io, DEO_SOCKET); 201 | BIO_set_ssl_mode(io, SSL_MODE_AUTO_RETRY); 202 | if (BIO_set_conn_hostname(io, trgt) <= 0) 203 | return NULL; 204 | 205 | if (BIO_do_connect(io) <= 0) 206 | return NULL; 207 | 208 | if (BIO_do_handshake(io) <= 0) 209 | return NULL; 210 | 211 | if (!verify_hostname(io, trgt)) 212 | return NULL; 213 | 214 | if (ASN1_item_i2d_bio(&DEO_MSG_it, io, (DEO_MSG *) req) <= 0) 215 | return NULL; 216 | 217 | return ASN1_item_d2i_bio(&DEO_MSG_it, io, NULL); 218 | } 219 | 220 | bool 221 | deo_anchors(char c, const char *arg, STACK_OF(X509) **misc) 222 | { 223 | AUTO_STACK(X509, tmp); 224 | AUTO(FILE, file); 225 | 226 | if (arg == NULL || misc == NULL) 227 | return false; 228 | 229 | file = fopen(arg, "r"); 230 | if (file == NULL) 231 | return false; 232 | 233 | tmp = *misc == NULL ? sk_X509_new_null() : *misc; 234 | if (tmp == NULL) 235 | return false; 236 | 237 | if (!deo_load(file, tmp)) 238 | return false; 239 | 240 | if (*misc == NULL) 241 | *misc = STEAL(tmp); 242 | 243 | return true; 244 | } 245 | 246 | bool 247 | deo_getopt(int argc, char **argv, const char *opt, const char *keep, ...) 248 | { 249 | char options[strlen(opt) + strlen(keep) + 1]; 250 | const int ind = ++optind; 251 | char *forget[argc]; 252 | char *retain[argc]; 253 | size_t fcnt = 0; 254 | size_t rcnt = 0; 255 | va_list ap; 256 | 257 | memset(forget, 0, sizeof(forget)); 258 | memset(retain, 0, sizeof(retain)); 259 | strcpy(options, opt); 260 | strcat(options, keep); 261 | 262 | for (int c; (c = getopt(argc, argv, options)) != -1; ) { 263 | const char *k = strchr(keep, c); 264 | const char *o = strchr(opt, c); 265 | bool found = false; 266 | 267 | if ((k != NULL) == (o != NULL)) { 268 | return false; 269 | } else if (k != NULL) { 270 | retain[rcnt++] = argv[optind - 2]; 271 | if (k[1] == ':') 272 | retain[rcnt++] = argv[optind - 1]; 273 | } else if (o != NULL) { 274 | forget[fcnt++] = argv[optind - 2]; 275 | if (o[1] == ':') 276 | forget[fcnt++] = argv[optind - 1]; 277 | } 278 | 279 | va_start(ap, keep); 280 | for (size_t i = 0; options[i] != '\0' && !found; i++) { 281 | deo_parse p; 282 | void *m; 283 | 284 | if (i == 0 && strchr("+-", options[i]) != NULL) 285 | continue; 286 | 287 | if (options[i] == ':') 288 | continue; 289 | 290 | p = va_arg(ap, deo_parse); 291 | m = va_arg(ap, void *); 292 | 293 | if (options[i] != c) 294 | continue; 295 | 296 | found = true; 297 | if (p == NULL) { 298 | va_end(ap); 299 | return false; 300 | } 301 | 302 | if (!p(c, options[1 + 1] == ':' ? optarg : NULL, m)) { 303 | va_end(ap); 304 | return false; 305 | } 306 | } 307 | va_end(ap); 308 | 309 | if (!found) 310 | return false; 311 | } 312 | 313 | for (size_t i = 0; i < fcnt; i++) 314 | argv[ind + i] = forget[i]; 315 | for (size_t i = 0; i < rcnt; i++) 316 | argv[ind + fcnt + i] = retain[i]; 317 | 318 | optind -= rcnt; 319 | return true; 320 | } 321 | 322 | int 323 | deo_run(char *argv[], int in, int out) 324 | { 325 | char path[PATH_MAX + 1] = {}; 326 | int status = 0; 327 | pid_t pid; 328 | 329 | strncpy(path, argv[0], PATH_MAX); 330 | if (strchr(argv[0], '/') != NULL) { 331 | if (realpath(argv[0], path) == NULL) 332 | return errno; 333 | } 334 | 335 | pid = fork(); 336 | if (pid < 0) 337 | return errno; 338 | 339 | if (pid == 0) { 340 | if (dup2(in, STDIN_FILENO) < 0) 341 | exit(errno); 342 | 343 | if (dup2(out, STDOUT_FILENO) < 0) 344 | exit(errno); 345 | 346 | execvp(path, argv); 347 | exit(errno); 348 | } 349 | 350 | if (waitpid(pid, &status, 0) != pid) 351 | return errno; 352 | 353 | return WEXITSTATUS(status); 354 | } 355 | 356 | int 357 | deo_pipe(int *rend, int *wend) 358 | { 359 | int in[2]; 360 | 361 | if (pipe(in) != 0) 362 | return errno; 363 | 364 | *rend = in[0]; 365 | *wend = in[1]; 366 | return 0; 367 | } 368 | 369 | bool 370 | deo_isreg(const char *dir, struct dirent *de) 371 | { 372 | char path[PATH_MAX]; 373 | struct stat st; 374 | int ret; 375 | 376 | switch (de->d_type) { 377 | case DT_REG: 378 | return true; 379 | 380 | case DT_UNKNOWN: 381 | ret = snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); 382 | if (ret < 0 || ret == sizeof(path)) 383 | break; 384 | 385 | if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) 386 | return true; 387 | 388 | default: 389 | break; 390 | } 391 | 392 | return false; 393 | } 394 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asn1.h" 22 | #include 23 | 24 | /* Callback signature for deo_getopt(). Return false on an error. */ 25 | typedef bool (*deo_parse)(char c, const char *arg, void *misc); 26 | 27 | /* Validates the certificate chain using the specified trust anchors. 28 | * If anchors is NULL or empty, the default system trust store is used. */ 29 | bool 30 | deo_validate(const STACK_OF(X509) *anchors, STACK_OF(X509) *chain); 31 | 32 | /* Loads all certificates from the file and adds them to certs. */ 33 | bool 34 | deo_load(FILE *fp, STACK_OF(X509) *certs); 35 | 36 | /* Sends a request to the server and receives the response. */ 37 | DEO_MSG * 38 | deo_request(const STACK_OF(X509) *anchors, const ASN1_UTF8STRING *target, 39 | const DEO_MSG *req); 40 | 41 | /* Callback function for deo_getopt() for parsing anchor arguments. */ 42 | bool 43 | deo_anchors(char c, const char *arg, STACK_OF(X509) **misc); 44 | 45 | /* Implement consistent getopt style parsing. 46 | * 47 | * This function works like getopt(), and even uses getopt() internally. 48 | * A few differences are worth noting. 49 | * 50 | * First, options specified in both opt and keep are processed. However, 51 | * the options listed in keep are moved to the end of the argv array after 52 | * optind. 53 | * 54 | * Second, option processing is done via callbacks. This is done similar to 55 | * printf. Each option specified (in either opt or keep) requires two 56 | * variable arguments. The first is a pointer to a callback function with the 57 | * signature of deo_parse. The second is a misc value to pass to the 58 | * callback. If the first variable argument for an option is NULL, no 59 | * callback will be called. In this case, if the option specified is provided 60 | * the function will return false; 61 | */ 62 | bool 63 | deo_getopt(int argc, char **argv, const char *opt, const char *keep, ...); 64 | 65 | /* Runs a command with the specified stdin and stdout. Returns errno. */ 66 | int 67 | deo_run(char *argv[], int in, int out); 68 | 69 | /* Exactly like pipe(), just a different signature. */ 70 | int 71 | deo_pipe(int *rend, int *wend); 72 | 73 | /* Returns true if the passed dirent is a regular file. */ 74 | bool 75 | deo_isreg(const char *dir, struct dirent *de); 76 | -------------------------------------------------------------------------------- /src/plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_LDFLAGS = -module -avoid-version -export-symbols-regex ^deo$ 2 | AM_CFLAGS = @CRYPTSETUP_CFLAGS@ @LIBSYSTEMD_CFLAGS@ \ 3 | -Wall \ 4 | -Wextra \ 5 | -Werror \ 6 | -Wno-missing-field-initializers \ 7 | -Wno-unused-parameter \ 8 | -Wno-sign-compare \ 9 | -Wstrict-aliasing 10 | 11 | plugin_LTLIBRARIES = \ 12 | askpassd.la \ 13 | cryptsetup.la \ 14 | decrypt.la \ 15 | decryptd.la \ 16 | encrypt.la \ 17 | query.la \ 18 | targets.la 19 | 20 | cryptsetup_la_LIBADD = @CRYPTSETUP_LIBS@ 21 | 22 | decryptd_la_SOURCES = \ 23 | decryptd/ctx.c \ 24 | decryptd/ctx.h \ 25 | decryptd/decrypt.c \ 26 | decryptd/decrypt.h \ 27 | decryptd/main.c 28 | 29 | askpassd_la_SOURCES = \ 30 | askpassd/question.c \ 31 | askpassd/question.h \ 32 | askpassd/askp.c \ 33 | askpassd/askp.h \ 34 | askpassd/iface.c \ 35 | askpassd/iface.h \ 36 | askpassd/list.c \ 37 | askpassd/list.h \ 38 | askpassd/main.c 39 | 40 | install-data-hook: 41 | $(MKDIR_P) $(DESTDIR)/$(DEO_CONF)/decrypt.d 42 | $(MKDIR_P) $(DESTDIR)/$(DEO_CONF)/disks.d 43 | touch $(DESTDIR)/$(DEO_CONF)/decryptd.pem 44 | touch $(DESTDIR)/$(DEO_CONF)/encrypt.pem 45 | rm -f $(DESTDIR)/$(plugindir)/*.la 46 | 47 | CLEANFILES = $(systemdsystemunit_DATA) $(dracut_SCRIPTS) 48 | EXTRA_DIST = \ 49 | decryptd/deo-decryptd.socket.in \ 50 | decryptd/deo-decryptd@.service.in \ 51 | askpassd/deo-askpassd.path.in \ 52 | askpassd/deo-askpassd.service.in \ 53 | askpassd/module-setup.sh.in 54 | 55 | dracut_SCRIPTS = askpassd/module-setup.sh 56 | systemdsystemunit_DATA = \ 57 | decryptd/deo-decryptd.socket \ 58 | decryptd/deo-decryptd@.service \ 59 | askpassd/deo-askpassd.path \ 60 | askpassd/deo-askpassd.service 61 | 62 | %: %.in 63 | $(AM_V_GEN)$(SED) \ 64 | -e 's,@bindir\@,$(bindir),g' \ 65 | -e 's,@DEO_CONF\@,$(DEO_CONF),g' \ 66 | -e 's,@DEO_SOCKET\@,$(DEO_SOCKET),g' \ 67 | -e 's,@DEO_PLUGINS\@,$(DEO_PLUGINS),g' \ 68 | $(srcdir)/$@.in > $@ 69 | -------------------------------------------------------------------------------- /src/plugins/askpassd/askp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include "askp.h" 21 | #include "question.h" 22 | #include "../../cleanup.h" 23 | #include "../../misc.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define ASK_DIR "/run/systemd/ask-password" 32 | 33 | struct askp { 34 | struct list list; 35 | int sock; 36 | int ifd; 37 | int wfd; 38 | }; 39 | 40 | static struct inotify_event * 41 | for_event(struct inotify_event *e, uint8_t *buf, size_t len) 42 | #define for_event(n, b, l) \ 43 | for (struct inotify_event *n = NULL; (n = for_event(n, b, l)); ) 44 | { 45 | uint8_t *tmp; 46 | 47 | if (e == NULL) 48 | return (struct inotify_event *) buf; 49 | 50 | tmp = (uint8_t *) e + sizeof(struct inotify_event) + e->len; 51 | if (tmp < buf + len) 52 | return (struct inotify_event *) tmp; 53 | 54 | return NULL; 55 | } 56 | 57 | int 58 | askp_new(struct askp **ctx, struct pollfd *fd) 59 | { 60 | AUTO(DIR, dir); 61 | int ret; 62 | 63 | *ctx = calloc(1, sizeof(struct askp)); 64 | if (!*ctx) 65 | return ENOMEM; 66 | 67 | (*ctx)->list = LIST_INIT((*ctx)->list); 68 | 69 | (*ctx)->sock = socket(AF_UNIX, SOCK_DGRAM, 0); 70 | if ((*ctx)->sock < 0) 71 | goto error; 72 | 73 | (*ctx)->ifd = inotify_init(); 74 | if ((*ctx)->ifd < 0) 75 | goto error; 76 | 77 | (*ctx)->wfd = inotify_add_watch((*ctx)->ifd, ASK_DIR, 78 | IN_CLOSE_WRITE | IN_MOVED_TO); 79 | if ((*ctx)->wfd < 0) 80 | goto error; 81 | 82 | dir = opendir(ASK_DIR); 83 | if (dir == NULL) 84 | goto error; 85 | 86 | for (struct dirent *de = readdir(dir); de; de = readdir(dir)) { 87 | struct question *q; 88 | 89 | if (strncmp("ask.", de->d_name, 4) != 0) 90 | continue; 91 | 92 | q = question_new(ASK_DIR, de->d_name); 93 | if (q != NULL) 94 | list_add_after(&(*ctx)->list, &q->list); 95 | } 96 | 97 | fd->events = POLLIN | POLLPRI; 98 | fd->fd = (*ctx)->ifd; 99 | return 0; 100 | 101 | error: 102 | ret = errno; 103 | askp_free(*ctx); 104 | return ret; 105 | } 106 | 107 | bool 108 | askp_question(struct askp *ctx, struct pollfd *fd) 109 | { 110 | uint8_t buf[8192] align_as(struct inotify_event); 111 | struct question *q = NULL; 112 | bool havenew = false; 113 | ssize_t len; 114 | 115 | if ((fd->revents & fd->events) == 0) 116 | return false; 117 | fd->revents = 0; 118 | 119 | while ((len = read(fd->fd, buf, sizeof(buf))) < 0) { 120 | if (errno != EAGAIN) 121 | return false; 122 | } 123 | 124 | for_event(e, buf, len) { 125 | if (strncmp("ask.", e->name, 4) != 0) 126 | continue; 127 | 128 | if (e->mask & IN_MOVED_TO) { 129 | q = question_new(ASK_DIR, e->name); 130 | if (q != NULL) { 131 | list_add_after(&ctx->list, &q->list); 132 | havenew = true; 133 | } 134 | continue; 135 | } 136 | 137 | LIST_FOREACH(&ctx->list, struct question, q, list) { 138 | if (strcmp(q->name, e->name) == 0) { 139 | list_pop(&q->list); 140 | free(q); 141 | break; 142 | } 143 | } 144 | } 145 | 146 | return havenew; 147 | } 148 | 149 | void 150 | askp_process(struct askp *ctx, char *argv[], const char *keysdir) 151 | { 152 | char hex[4096] = { '+' }; 153 | struct timespec now; 154 | 155 | if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) 156 | return; 157 | 158 | LIST_FOREACH(&ctx->list, struct question, q, list) { 159 | char path[strlen(keysdir) + strlen(q->uuid) + 2]; 160 | AUTO_FD(rfile); 161 | AUTO_FD(rpipe); 162 | 163 | strcpy(path, keysdir); 164 | strcat(path, "/"); 165 | strcat(path, q->uuid); 166 | 167 | if (q->time.tv_sec != 0 || q->time.tv_nsec != 0) { 168 | if (q->time.tv_sec < now.tv_sec) 169 | continue; 170 | 171 | if (q->time.tv_sec == now.tv_sec && 172 | q->time.tv_nsec < now.tv_nsec) 173 | continue; 174 | } 175 | 176 | rfile = open(path, O_RDONLY); 177 | if (rfile < 0) { 178 | fprintf(stderr, "Unable to open keyfile (%s): %s\n", 179 | path, strerror(errno)); 180 | continue; 181 | } else { 182 | AUTO_FD(wpipe); 183 | int err; 184 | 185 | err = deo_pipe(&rpipe, &wpipe); 186 | if (err != 0) { 187 | fprintf(stderr, "Error making pipe: %s\n", strerror(err)); 188 | continue; 189 | } 190 | 191 | /* NOTE: this code depends on the kernel's pipe buffer being 192 | * larger than size. This should always be the case with these 193 | * short keys. */ 194 | err = deo_run(argv, rfile, wpipe); 195 | if (err != 0) { 196 | fprintf(stderr, "%s decryption error: %s\n", 197 | q->uuid, strerror(err)); 198 | continue; 199 | } else { 200 | fprintf(stderr, "%s decryption success\n", q->uuid); 201 | } 202 | } 203 | 204 | for (ssize_t r; (r = read(rpipe, hex + strlen(hex), 205 | sizeof(hex) - strlen(hex) - 1)) != 0; ) { 206 | if (r < 0) { 207 | fprintf(stderr, "%s read error: %s\n", 208 | q->uuid, strerror(errno)); 209 | break; 210 | } 211 | } 212 | 213 | (void) sendto(ctx->sock, hex, strlen(hex), 0, 214 | &q->sock, sizeof(q->sock)); 215 | } 216 | } 217 | 218 | bool 219 | askp_more(struct askp *ctx) 220 | { 221 | return !LIST_EMPTY(&ctx->list); 222 | } 223 | 224 | void 225 | askp_free(struct askp *ctx) 226 | { 227 | if (!ctx) 228 | return; 229 | 230 | LIST_FOREACH(&ctx->list, struct question, q, list) 231 | free(q); 232 | 233 | if (ctx->sock > 0) 234 | close(ctx->sock); 235 | 236 | if (ctx->wfd > 0) 237 | close(ctx->wfd); 238 | 239 | if (ctx->ifd > 0) 240 | close(ctx->ifd); 241 | 242 | free(ctx); 243 | } 244 | -------------------------------------------------------------------------------- /src/plugins/askpassd/askp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "list.h" 25 | 26 | struct askp; 27 | 28 | int 29 | askp_new(struct askp **ctx, struct pollfd *fd); 30 | 31 | bool 32 | askp_question(struct askp *ctx, struct pollfd *fd); 33 | 34 | void 35 | askp_process(struct askp *ctx, char *argv[], const char *keysdir); 36 | 37 | bool 38 | askp_more(struct askp *ctx); 39 | 40 | void 41 | askp_free(struct askp *ctx); 42 | -------------------------------------------------------------------------------- /src/plugins/askpassd/deo-askpassd.path.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Deo askpass daemon path 3 | DefaultDependencies=false 4 | 5 | [Path] 6 | DirectoryNotEmpty=/run/systemd/ask-password 7 | 8 | [Install] 9 | WantedBy=basic.target 10 | -------------------------------------------------------------------------------- /src/plugins/askpassd/deo-askpassd.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Deo askpass daemon 3 | DefaultDependencies=false 4 | ConditionDirectoryNotEmpty=@DEO_CONF@/disks.d 5 | 6 | [Service] 7 | ExecStart=@bindir@/deo askpassd 8 | StandardError=syslog 9 | -------------------------------------------------------------------------------- /src/plugins/askpassd/iface.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include "iface.h" 21 | #include "../../cleanup.h" 22 | #include "../../misc.h" 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | static int 33 | request_existing(int sock, int family) 34 | { 35 | struct { 36 | struct nlmsghdr h; 37 | struct rtmsg m; 38 | } req = { 39 | { NLMSG_LENGTH(sizeof(struct rtmsg)), 40 | RTM_GETROUTE, NLM_F_REQUEST | NLM_F_DUMP, 0, getpid() }, 41 | { family } 42 | }; 43 | 44 | return send(sock, &req, sizeof(req), 0); 45 | } 46 | 47 | int 48 | iface_new(struct pollfd *fd) 49 | { 50 | struct sockaddr_nl addr = { 51 | .nl_family = AF_NETLINK, 52 | .nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE 53 | }; 54 | 55 | fd->events = POLLIN | POLLPRI; 56 | fd->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 57 | if (fd->fd < 0) 58 | return errno; 59 | 60 | if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) 61 | return errno; 62 | 63 | (void) request_existing(fd->fd, AF_INET); 64 | (void) request_existing(fd->fd, AF_INET6); 65 | 66 | return 0; 67 | } 68 | 69 | bool 70 | iface_route(struct pollfd *fd) 71 | { 72 | uint8_t buf[4096] align_as(struct nlmsghdr); 73 | int len; 74 | 75 | if ((fd->revents & fd->events) == 0) 76 | return false; 77 | fd->revents = 0; 78 | 79 | while ((len = read(fd->fd, buf, sizeof(buf))) < 0) { 80 | if (errno != EAGAIN) 81 | return false; 82 | } 83 | 84 | for (struct nlmsghdr *msghdr = (struct nlmsghdr *) buf; 85 | NLMSG_OK(msghdr, len) && msghdr->nlmsg_type != NLMSG_DONE; 86 | msghdr = NLMSG_NEXT(msghdr, len)) { 87 | struct rtmsg *rtmsg = NLMSG_DATA(msghdr); 88 | 89 | switch (msghdr->nlmsg_type) { 90 | case RTM_NEWROUTE: 91 | switch (rtmsg->rtm_type) { 92 | case RTN_LOCAL: 93 | case RTN_UNICAST: 94 | return true; 95 | } 96 | } 97 | } 98 | 99 | return false; 100 | } 101 | -------------------------------------------------------------------------------- /src/plugins/askpassd/iface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include "list.h" 24 | 25 | int 26 | iface_new(struct pollfd *fd); 27 | 28 | bool 29 | iface_route(struct pollfd *fd); 30 | 31 | void 32 | iface_decrypt(char *argv[], const char *keysdir, struct list *keys); 33 | -------------------------------------------------------------------------------- /src/plugins/askpassd/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "list.h" 20 | 21 | void 22 | list_add_after(struct list *list, struct list *item) 23 | { 24 | item->next = list->next; 25 | item->prev = list; 26 | list->next->prev = item; 27 | list->next = item; 28 | } 29 | 30 | struct list * 31 | list_pop(struct list *item) 32 | { 33 | if (item == NULL) 34 | return NULL; 35 | 36 | item->prev->next = item->next; 37 | item->next->prev = item->prev; 38 | return item; 39 | } 40 | -------------------------------------------------------------------------------- /src/plugins/askpassd/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | #include 21 | 22 | #define align_as(t) __attribute__ ((aligned(__alignof__(t)))) 23 | 24 | struct list { 25 | struct list *prev; 26 | struct list *next; 27 | }; 28 | 29 | #define LIST(name) struct list name = LIST_INIT(name) 30 | #define LIST_INIT(name) (struct list) { &(name), &(name) } 31 | #define LIST_ITEM(item, type, member) ({ \ 32 | (type *) ((void *) (item) - offsetof(type, member)); \ 33 | }) 34 | 35 | #define LIST_EMPTY(list) ((list)->prev == (list) && (list)->next == (list)) 36 | 37 | #define LIST_FOREACH(list, type, name, member) \ 38 | for (type *__lst = LIST_ITEM(list, type, member), \ 39 | *name = LIST_ITEM(__lst->member.next, type, member), \ 40 | *__nxt = LIST_ITEM(name->member.next, type, member); \ 41 | name != __lst; name = __nxt, \ 42 | __nxt = LIST_ITEM(name->member.next, type, member)) 43 | 44 | void 45 | list_add_after(struct list *list, struct list *item); 46 | 47 | struct list * 48 | list_pop(struct list *item); 49 | -------------------------------------------------------------------------------- /src/plugins/askpassd/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include "../../cleanup.h" 21 | #include "../main.h" 22 | #include "askp.h" 23 | #include "iface.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define TIMEOUT_BASE 5000 34 | #define TIMEOUT_EXT (rand() % 295000) 35 | #define ALLCNT (sizeof(struct all) / sizeof(struct pollfd)) 36 | 37 | static void 38 | on_signal(int sig) 39 | { 40 | } 41 | 42 | static bool 43 | option(char c, const char *arg, const char **misc) 44 | { 45 | *misc = arg; 46 | return true; 47 | } 48 | 49 | struct all { 50 | struct pollfd askp; 51 | struct pollfd iface; 52 | }; 53 | 54 | static int 55 | askpass(int argc, char *argv[]) 56 | { 57 | const char *keydir = DEO_CONF "/disks.d"; 58 | int timeout = TIMEOUT_BASE; 59 | AUTO_STACK(X509, anchors); 60 | struct askp *askp = NULL; 61 | int ret = EXIT_FAILURE; 62 | char *dargs[argc + 1]; 63 | struct stat st; 64 | int events; 65 | LIST(keys); 66 | 67 | union { 68 | struct pollfd all[ALLCNT]; 69 | struct all ind; 70 | } fds; 71 | 72 | if (!deo_getopt(argc, argv, "hk:", "a:", NULL, NULL, 73 | option, &keydir, deo_anchors, &anchors)) { 74 | fprintf(stderr, 75 | "Usage: deo askpassd " 76 | "[-k ] [-a ] " 77 | "[ ...]\n"); 78 | return EXIT_FAILURE; 79 | } 80 | 81 | memset(dargs, 0, sizeof(dargs)); 82 | dargs[0] = argv[0]; 83 | dargs[1] = "decrypt"; 84 | memcpy(&dargs[2], &argv[optind], (argc - optind) * sizeof(char *)); 85 | 86 | if (access(keydir, R_OK) != 0 87 | || stat(keydir, &st) != 0 88 | || !S_ISDIR(st.st_mode)) 89 | error(EXIT_FAILURE, errno, "Unable to access key directory"); 90 | 91 | if (askp_new(&askp, &fds.ind.askp) != 0) 92 | goto error; 93 | 94 | if (iface_new(&fds.ind.iface) != 0) 95 | goto error; 96 | 97 | signal(SIGINT, on_signal); 98 | signal(SIGQUIT, on_signal); 99 | signal(SIGTERM, on_signal); 100 | signal(SIGUSR1, on_signal); 101 | signal(SIGUSR2, on_signal); 102 | 103 | for (int i = 0; i < ALLCNT; i++) 104 | fds.all[i].events |= POLLRDHUP; 105 | 106 | while ((events = poll(fds.all, ALLCNT, timeout)) >= 0) { 107 | bool process = false; 108 | 109 | for (int i = 0; i < ALLCNT; i++) { 110 | short mask = ~fds.all[i].events | POLLRDHUP; 111 | if (fds.all[i].revents & mask) 112 | goto error; 113 | } 114 | 115 | if (events == 0) { 116 | askp_process(askp, dargs, keydir); 117 | 118 | if (!askp_more(askp)) 119 | break; 120 | 121 | timeout = TIMEOUT_BASE + TIMEOUT_EXT; 122 | continue; 123 | } 124 | 125 | timeout = TIMEOUT_BASE; 126 | process |= iface_route(&fds.ind.iface); 127 | process |= askp_question(askp, &fds.ind.askp); 128 | if (process) 129 | askp_process(askp, dargs, keydir); 130 | } 131 | 132 | if (errno == EINTR || errno == 0) 133 | ret = EXIT_SUCCESS; 134 | 135 | error: 136 | close(fds.ind.iface.fd); 137 | close(fds.ind.askp.fd); 138 | askp_free(askp); 139 | return ret; 140 | } 141 | 142 | deo_plugin deo = { askpass, NULL }; 143 | -------------------------------------------------------------------------------- /src/plugins/askpassd/module-setup.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | check() { 4 | # If no deo disk keys are installed, 5 | # only install deo on demand 6 | for i in @DEO_CONF@/disks.d/*; do 7 | [ -e "$i" ] || return 255 8 | done 9 | 10 | return 0 11 | } 12 | 13 | depends() { 14 | echo crypt systemd network 15 | return 0 16 | } 17 | 18 | cmdline() { 19 | echo "rd.neednet=1" 20 | } 21 | 22 | install() { 23 | if ! dracut_module_included "systemd"; then 24 | derror "deo needs systemd in the initramfs" 25 | return 1 26 | fi 27 | 28 | cmdline > "${initdir}/etc/cmdline.d/99deo.conf" 29 | 30 | inst_multiple deo \ 31 | "$systemdsystemunitdir/deo-askpassd.service" \ 32 | "$systemdsystemunitdir/deo-askpassd.path" \ 33 | @DEO_PLUGINS@/askpassd.so \ 34 | @DEO_PLUGINS@/decrypt.so \ 35 | @DEO_CONF@/disks.d/* 36 | 37 | ln_r "$systemdsystemunitdir/deo-askpassd.path" \ 38 | "$systemdsystemunitdir/sysinit.target.wants/deo-askpassd.path" 39 | 40 | dracut_need_initqueue 41 | } 42 | -------------------------------------------------------------------------------- /src/plugins/askpassd/question.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include "question.h" 21 | #include "../../cleanup.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define UUID_DIR "/dev/disk/by-uuid" 30 | #define SECTION "[Ask]\n" 31 | #define PREFIX_ID "\nId=cryptsetup:" 32 | #define PREFIX_SOCKET "\nSocket=" 33 | #define PREFIX_NOTAFTER "\nNotAfter=" 34 | 35 | static int 36 | find_prefix_in_section(const char *start, const char *end, const char *prefix, 37 | char *out, size_t outlen) 38 | { 39 | char *startl = NULL; 40 | char *endl = NULL; 41 | ssize_t plen; 42 | 43 | if (start == NULL || end == NULL || prefix == NULL) 44 | return EINVAL; 45 | 46 | plen = strlen(prefix); 47 | 48 | startl = memmem(start, end - start, prefix, plen); 49 | if (startl == NULL) 50 | return ENOENT; 51 | startl += plen; 52 | 53 | endl = memchr(startl, '\n', end - startl); 54 | if (endl == NULL) 55 | return ENOENT; 56 | 57 | if (outlen < endl - startl + 1) 58 | return E2BIG; 59 | 60 | plen = snprintf(out, endl - startl + 1, "%s", startl); 61 | if (plen < 0) 62 | return errno; 63 | 64 | return 0; 65 | } 66 | 67 | static int 68 | find_uuid_for_dev(const char *dev, char *out, size_t outlen) 69 | { 70 | char dpath[PATH_MAX]; 71 | AUTO(DIR, dir); 72 | 73 | if (realpath(dev, dpath) == NULL) 74 | return errno; 75 | 76 | dir = opendir(UUID_DIR); 77 | if (dir == NULL) 78 | return errno; 79 | 80 | for (struct dirent *de = readdir(dir); de; de = readdir(dir)) { 81 | char path[strlen(UUID_DIR) + strlen(de->d_name) + 2]; 82 | char rpath[PATH_MAX]; 83 | 84 | if (de->d_type != DT_LNK) 85 | continue; 86 | 87 | strcpy(path, UUID_DIR); 88 | strcat(path, "/"); 89 | strcat(path, de->d_name); 90 | 91 | if (!realpath(path, rpath)) 92 | continue; 93 | 94 | if (strcmp(rpath, dpath) != 0) 95 | continue; 96 | 97 | if (snprintf(out, outlen, "%s", de->d_name) != strlen(de->d_name)) 98 | return ENAMETOOLONG; 99 | 100 | return 0; 101 | } 102 | 103 | return ENOENT; 104 | } 105 | 106 | struct question * 107 | question_new(const char *dir, const char *name) 108 | { 109 | struct question *q = NULL; 110 | struct stat st = {}; 111 | char tmp[PATH_MAX]; 112 | char *start = NULL; 113 | char *file = NULL; 114 | char *end = NULL; 115 | AUTO_FD(fd); 116 | int err; 117 | 118 | q = calloc(1, sizeof(struct question)); 119 | if (!q) 120 | goto error; 121 | q->sock.sun_family = AF_UNIX; 122 | 123 | if (snprintf(q->name, sizeof(q->name), "%s", name) < 0) 124 | goto error; 125 | 126 | err = snprintf(tmp, sizeof(tmp), "%s/%s", dir, name); 127 | if (err < 0) 128 | goto error; 129 | 130 | fd = open(tmp, O_RDONLY); 131 | if (fd < 0) 132 | goto error; 133 | 134 | if (fstat(fd, &st) != 0) 135 | goto error; 136 | 137 | file = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 138 | if (!file) 139 | goto error; 140 | 141 | start = memmem(file, st.st_size, SECTION, strlen(SECTION)); 142 | if (!start) 143 | goto error; 144 | 145 | end = memmem(start, st.st_size - (start - file), "\n[", 2); 146 | if (!end) 147 | end = file + st.st_size; 148 | 149 | err = find_prefix_in_section(start, end, PREFIX_ID, tmp, sizeof(tmp)); 150 | if (err != 0) 151 | goto error; 152 | 153 | err = find_uuid_for_dev(tmp, q->uuid, sizeof(q->uuid)); 154 | if (err != 0) 155 | goto error; 156 | 157 | err = find_prefix_in_section(start, end, PREFIX_NOTAFTER, 158 | tmp, sizeof(tmp)); 159 | if (err != 0) { 160 | long long usec; 161 | 162 | errno = 0; 163 | usec = strtoll(tmp, NULL, 10); 164 | if (errno != 0) 165 | goto error; 166 | 167 | q->time.tv_sec = usec / 1000000; 168 | q->time.tv_nsec = usec % 1000000 * 1000; 169 | } 170 | 171 | err = find_prefix_in_section(start, end, PREFIX_SOCKET, 172 | q->sock.sun_path, sizeof(q->sock.sun_path)); 173 | if (err != 0) 174 | goto error; 175 | 176 | munmap(file, st.st_size); 177 | return q; 178 | 179 | error: 180 | if (file) 181 | munmap(file, st.st_size); 182 | 183 | free(q); 184 | return NULL; 185 | } 186 | -------------------------------------------------------------------------------- /src/plugins/askpassd/question.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "list.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | struct question { 26 | struct list list; 27 | char name[PATH_MAX]; 28 | char uuid[PATH_MAX]; 29 | struct sockaddr_un sock; 30 | struct timespec time; 31 | }; 32 | 33 | struct question * 34 | question_new(const char *dir, const char *name); 35 | -------------------------------------------------------------------------------- /src/plugins/cryptsetup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "../main.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | typedef struct crypt_device crypt_device; 35 | 36 | static void 37 | cleanup_crypt_device(crypt_device **cd) 38 | { 39 | if (cd == NULL || *cd == NULL) 40 | return; 41 | 42 | crypt_free(*cd); 43 | } 44 | 45 | /* Generate a hex key from nbytes of random data. 46 | * The hex parameter must be at least 2 * nbytes + 1. */ 47 | static int 48 | generate_key(size_t nbytes, char *hex) 49 | { 50 | uint8_t key[nbytes]; 51 | AUTO(FILE, file); 52 | 53 | file = fopen("/dev/urandom", "r"); 54 | if (file == NULL) 55 | return -errno; 56 | 57 | if (fread(key, 1, nbytes, file) != nbytes) 58 | return -errno; 59 | 60 | for (size_t i = 0; i < nbytes; i++) 61 | snprintf(&hex[i * 2], 3, "%02X", key[i]); 62 | 63 | return 0; 64 | } 65 | 66 | static int 67 | make_keyfile(crypt_device *cd, const char *keydir, const char *rand, 68 | char *argv[]) 69 | { 70 | const char *uuid = NULL; 71 | char keyfile[PATH_MAX]; 72 | ssize_t written; 73 | AUTO_FD(rpipe); 74 | AUTO_FD(wfile); 75 | int err; 76 | 77 | uuid = crypt_get_uuid(cd); 78 | if (uuid == NULL) 79 | return -EINVAL; 80 | 81 | written = snprintf(keyfile, sizeof(keyfile), "%s/%s", keydir, uuid); 82 | if (written < 0 || written == sizeof(keyfile)) 83 | return -ENAMETOOLONG; 84 | else { 85 | AUTO_FD(wpipe); 86 | 87 | if (deo_pipe(&rpipe, &wpipe) != 0) 88 | return -errno; 89 | 90 | /* NOTE: this code depends on the kernel's pipe buffer being larger 91 | * than size. This should always be the case with these short keys. */ 92 | written = write(wpipe, rand, strlen(rand)); 93 | if (written < 0) 94 | return -errno; 95 | if (written != (ssize_t) strlen(rand)) 96 | return -EMSGSIZE; 97 | } 98 | 99 | wfile = open(keyfile, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); 100 | if (wfile < 0) 101 | return -errno; 102 | 103 | err = deo_run(argv, rpipe, wfile); 104 | if (err != 0) { 105 | unlink(keyfile); 106 | return -err; 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | static bool 113 | option(char c, const char *arg, const char **misc) 114 | { 115 | *misc = arg; 116 | return true; 117 | } 118 | 119 | static int 120 | cryptsetup(int argc, char *argv[]) 121 | { 122 | const char *keydir = DEO_CONF "/disks.d"; 123 | const char *device = NULL; 124 | AUTO_STACK(X509, anchors); 125 | const char *type = NULL; 126 | AUTO(crypt_device, cd); 127 | char *args[argc + 1]; 128 | int keysize = 0; 129 | int nerr = 0; 130 | int slot = 0; 131 | 132 | if (!deo_getopt(argc, argv, "hk:d:", "a:", NULL, NULL, option, &keydir, 133 | option, &device, deo_anchors, &anchors) 134 | || device == NULL || sk_X509_num(anchors) == 0 || argc - optind < 1) { 135 | fprintf(stderr, "Usage: deo cryptsetup " 136 | "[-k ] -d " 137 | "-a [...]\n"); 138 | return EXIT_FAILURE; 139 | } 140 | 141 | memset(args, 0, sizeof(args)); 142 | args[0] = argv[0]; 143 | args[1] = "encrypt"; 144 | memcpy(&args[2], &argv[optind], (argc - optind) * sizeof(char *)); 145 | 146 | nerr = crypt_init(&cd, device); 147 | if (nerr != 0) 148 | error(1, -nerr, "Unable to open device (%s)", device); 149 | 150 | nerr = crypt_load(cd, NULL, NULL); 151 | if (nerr != 0) 152 | error(1, -nerr, "Unable to load device (%s)", device); 153 | 154 | type = crypt_get_type(cd); 155 | if (type == NULL) 156 | error(1, 0, "Unable to determine device type"); 157 | if (strcmp(type, CRYPT_LUKS1) != 0) 158 | error(1, 0, "%s (%s) is not a LUKS device", device, type); 159 | 160 | keysize = crypt_get_volume_key_size(cd); 161 | if (keysize < 16) /* Less than 128-bits. */ 162 | error(1, 0, "Key size (%d) is too small", keysize); 163 | 164 | char hex[keysize * 2 + 1]; 165 | 166 | nerr = generate_key(keysize, hex); 167 | if (nerr != 0) 168 | error(1, -nerr, "Unable to generate key"); 169 | 170 | slot = crypt_keyslot_add_by_passphrase(cd, CRYPT_ANY_SLOT, NULL, 0, 171 | hex, sizeof(hex) - 1); 172 | if (slot < 0) 173 | error(1, -slot, "Unable to add passphrase"); 174 | 175 | nerr = make_keyfile(cd, keydir, hex, args); 176 | if (nerr != 0) { 177 | crypt_keyslot_destroy(cd, slot); 178 | error(1, -nerr, "Unable to make keyfile"); 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | deo_plugin deo = { 185 | cryptsetup, "Enable deo on a LUKS partition" 186 | }; 187 | -------------------------------------------------------------------------------- /src/plugins/decrypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "../main.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | static DEO_HEADER * 26 | parse_header(const STACK_OF(X509) *anchors, size_t ntargets, 27 | char *targets[], FILE *in) 28 | { 29 | AUTO(DEO_HEADER, hdr); 30 | 31 | hdr = d2i_fp_max(&DEO_HEADER_it, in, NULL, DEO_MAX_INPUT); 32 | if (hdr == NULL) 33 | return NULL; 34 | 35 | /* Add specified anchors to the embedded anchors. */ 36 | for (int i = sk_X509_num(anchors) - 1; anchors != NULL && i >= 0 ; i--) { 37 | X509 *cert = X509_dup(sk_X509_value(anchors, i)); 38 | if (cert == NULL) 39 | return NULL; 40 | 41 | if (sk_X509_unshift(hdr->anchors, cert) <= 0) { 42 | X509_free(cert); 43 | return NULL; 44 | } 45 | } 46 | 47 | /* Add specified targets to the embedded targets. */ 48 | for (int i = ntargets - 1; i >= 0; i--) { 49 | ASN1_UTF8STRING *target = ASN1_UTF8STRING_new(); 50 | if (target == NULL) 51 | return NULL; 52 | 53 | if (sk_ASN1_UTF8STRING_unshift(hdr->targets, target) <= 0) { 54 | ASN1_UTF8STRING_free(target); 55 | return NULL; 56 | } 57 | 58 | if (ASN1_STRING_set(target, targets[i], strlen(targets[i])) != 1) 59 | return NULL; 60 | } 61 | 62 | return STEAL(hdr); 63 | } 64 | 65 | static ASN1_OCTET_STRING * 66 | fetch_key(const DEO_HEADER *hdr) 67 | { 68 | for (int i = 0; i < sk_ASN1_UTF8STRING_num(hdr->targets); i++) { 69 | ASN1_UTF8STRING *trgt = sk_ASN1_UTF8STRING_value(hdr->targets, i); 70 | AUTO(DEO_MSG, rep); 71 | 72 | rep = deo_request(hdr->anchors, trgt, &(DEO_MSG) { 73 | .type = DEO_MSG_TYPE_DEC_REQ, 74 | .value.dec_req = hdr->req 75 | }); 76 | 77 | if (rep != NULL && rep->type == DEO_MSG_TYPE_DEC_REP) 78 | return STEAL(rep->value.dec_rep); 79 | } 80 | 81 | return NULL; 82 | } 83 | 84 | static bool 85 | decrypt_body(const DEO_HEADER *hdr, const ASN1_OCTET_STRING *key, 86 | FILE *in, FILE *out) 87 | { 88 | const EVP_CIPHER *cipher = NULL; 89 | AUTO(EVP_CIPHER_CTX, cctx); 90 | uint8_t ct[4096]; 91 | size_t tlen = 0; 92 | 93 | cipher = EVP_get_cipherbyobj(hdr->req->cipher); 94 | if (cipher == NULL) 95 | return false; 96 | 97 | switch (EVP_CIPHER_nid(cipher)) { 98 | case NID_aes_128_gcm: 99 | case NID_aes_192_gcm: 100 | case NID_aes_256_gcm: 101 | tlen = EVP_GCM_TLS_TAG_LEN; 102 | break; 103 | 104 | default: 105 | return false; 106 | } 107 | 108 | if (cipher->iv_len != hdr->iv->length) 109 | return false; 110 | 111 | if (key->length < cipher->key_len) 112 | return false; 113 | 114 | cctx = EVP_CIPHER_CTX_new(); 115 | if (!cctx) 116 | return false; 117 | 118 | if (EVP_DecryptInit_ex(cctx, cipher, NULL, 119 | key->data, hdr->iv->data) != 1) 120 | return false; 121 | 122 | for (size_t ctl = 0; !feof(stdin); ) { 123 | uint8_t pt[sizeof(ct) + EVP_MAX_BLOCK_LENGTH]; 124 | int ptl = 0; 125 | 126 | ctl += fread(ct + ctl, 1, sizeof(ct) - ctl, in); 127 | if (ferror(in)) 128 | return false; 129 | 130 | if (ctl < tlen) { 131 | if (feof(in)) 132 | return false; 133 | continue; 134 | } 135 | 136 | ptl = 0; 137 | if (EVP_DecryptUpdate(cctx, pt, &ptl, ct, ctl - tlen) != 1) 138 | return false; 139 | 140 | memmove(ct, ct + ctl - tlen, tlen); 141 | ctl = tlen; 142 | 143 | if (feof(in)) { 144 | int tmp = 0; 145 | 146 | if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_SET_TAG, 147 | tlen, ct) != 1) 148 | return false; 149 | 150 | if (EVP_DecryptFinal_ex(cctx, pt + ptl, &tmp) != 1) 151 | return false; 152 | 153 | ptl += tmp; 154 | } 155 | 156 | if (fwrite(pt, 1, ptl, out) != (size_t) ptl) 157 | return false; 158 | } 159 | 160 | return true; 161 | } 162 | 163 | static int 164 | decrypt(int argc, char *argv[]) 165 | { 166 | AUTO(ASN1_OCTET_STRING, key); 167 | AUTO_STACK(X509, anchors); 168 | AUTO(DEO_HEADER, hdr); 169 | 170 | if (!deo_getopt(argc, argv, "ha:", "", NULL, NULL, 171 | deo_anchors, &anchors)) { 172 | fprintf(stderr, 173 | "Usage: deo decrypt " 174 | "[-a ] [ ...] " 175 | "< CIPHERTEXT > PLAINTEXT\n"); 176 | return EXIT_FAILURE; 177 | } 178 | 179 | hdr = parse_header(anchors, argc - optind, &argv[optind], stdin); 180 | if (hdr == NULL) 181 | error(EXIT_FAILURE, 0, "Unable to parse header"); 182 | 183 | key = fetch_key(hdr); 184 | if (key == NULL) 185 | error(EXIT_FAILURE, 0, "Unable to retrieve key"); 186 | 187 | return decrypt_body(hdr, key, stdin, stdout) ? EXIT_SUCCESS : EXIT_FAILURE; 188 | } 189 | 190 | deo_plugin deo = { 191 | decrypt, "Decrypts input using any of the targets" 192 | }; 193 | -------------------------------------------------------------------------------- /src/plugins/decryptd/ctx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "ctx.h" 20 | #include "../cleanup.h" 21 | #include "../misc.h" 22 | 23 | #include 24 | 25 | static STACK_OF(X509_INFO) * 26 | load_decryption_certs_keys(const char *dirname) 27 | { 28 | AUTO_STACK(X509_INFO, infos); 29 | AUTO(DIR, dir); 30 | 31 | if (dirname == NULL) 32 | return NULL; 33 | 34 | infos = sk_X509_INFO_new_null(); 35 | if (infos == NULL) 36 | return NULL; 37 | 38 | dir = opendir(dirname); 39 | if (dir == NULL) 40 | return NULL; 41 | 42 | for (struct dirent *de = readdir(dir); de != NULL; de = readdir(dir)) { 43 | char path[strlen(dirname) + strlen(de->d_name) + 2]; 44 | AUTO(FILE, file); 45 | 46 | if (!deo_isreg(dirname, de)) 47 | continue; 48 | 49 | strcpy(path, dirname); 50 | strcat(path, "/"); 51 | strcat(path, de->d_name); 52 | 53 | file = fopen(path, "r"); 54 | if (file == NULL) 55 | return NULL; 56 | 57 | if (PEM_X509_INFO_read(file, infos, NULL, NULL) == NULL) 58 | return NULL; 59 | } 60 | 61 | if (sk_X509_INFO_num(infos) == 0) 62 | return NULL; 63 | 64 | return STEAL(infos); 65 | } 66 | 67 | static EVP_PKEY * 68 | load_prv(const char *filename) 69 | { 70 | AUTO(BIO, bio); 71 | 72 | bio = BIO_new_file(filename, "r"); 73 | if (bio == NULL) 74 | return NULL; 75 | 76 | return PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); 77 | } 78 | 79 | static void 80 | ctx_free(ctx *ctx) 81 | { 82 | if (ctx == NULL) 83 | return; 84 | 85 | sk_X509_INFO_pop_free(ctx->dec, X509_INFO_free); 86 | sk_X509_pop_free(ctx->crt, X509_free); 87 | SSL_CTX_free(ctx->ctx); 88 | OPENSSL_free(ctx); 89 | } 90 | 91 | ctx * 92 | ctx_init(const char *tls, const char *enc, const char *dec) 93 | { 94 | const int ops = SSL_OP_NO_COMPRESSION | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; 95 | AUTO(EVP_PKEY, prv); 96 | AUTO(FILE, file); 97 | AUTO(ctx, ctx); 98 | 99 | if (tls == NULL || enc == NULL || dec == NULL) 100 | return NULL; 101 | 102 | ctx = OPENSSL_malloc(sizeof(*ctx)); 103 | if (ctx == NULL) 104 | return NULL; 105 | memset(ctx, 0, sizeof(*ctx)); 106 | 107 | ctx->ctx = SSL_CTX_new(SSLv23_server_method()); 108 | if (ctx->ctx == NULL) 109 | return NULL; 110 | 111 | if (SSL_CTX_set_options(ctx->ctx, ops) <= 0) 112 | return NULL; 113 | 114 | if (SSL_CTX_use_certificate_chain_file(ctx->ctx, tls) <= 0) 115 | return NULL; 116 | 117 | prv = load_prv(tls); 118 | if (prv == NULL) 119 | return NULL; 120 | 121 | if (SSL_CTX_use_PrivateKey(ctx->ctx, prv) <= 0) 122 | return NULL; 123 | 124 | file = fopen(enc, "r"); 125 | if (file == NULL) 126 | return NULL; 127 | 128 | ctx->crt = sk_X509_new_null(); 129 | if (ctx->crt == NULL) 130 | return NULL; 131 | 132 | if (!deo_load(file, ctx->crt)) 133 | return NULL; 134 | 135 | ctx->dec = load_decryption_certs_keys(dec); 136 | if (ctx->dec == NULL) 137 | return NULL; 138 | 139 | /* Check to ensure that the TLS connection key is not also listed 140 | * in the decryption keys. This prevents an attack where, upon 141 | * misconfiguration, this service could be used to decrypt its own 142 | * traffic. */ 143 | for (int i = 0; i < sk_X509_INFO_num(ctx->dec); i++) { 144 | X509_INFO *info = sk_X509_INFO_value(ctx->dec, i); 145 | 146 | if (info->x_pkey == NULL) 147 | continue; 148 | 149 | if (EVP_PKEY_cmp(prv, info->x_pkey->dec_pkey) == 1) { 150 | fprintf(stderr, "TLS private key is exposed!\n"); 151 | return NULL; 152 | } 153 | } 154 | 155 | return STEAL(ctx); 156 | } 157 | 158 | DEFINE_CLEANUP(ctx) 159 | -------------------------------------------------------------------------------- /src/plugins/decryptd/ctx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "../cleanup.h" 22 | 23 | #include 24 | 25 | typedef struct { 26 | SSL_CTX *ctx; 27 | STACK_OF(X509) *crt; 28 | STACK_OF(X509_INFO) *dec; 29 | } ctx; 30 | 31 | ctx * 32 | ctx_init(const char *tls, const char *enc, const char *dec); 33 | 34 | DECLARE_CLEANUP(ctx); 35 | -------------------------------------------------------------------------------- /src/plugins/decryptd/decrypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "decrypt.h" 20 | 21 | #include 22 | 23 | static EVP_PKEY * 24 | find_prv(STACK_OF(X509_INFO) *infos, X509 *cert) 25 | { 26 | if (infos == NULL || cert == NULL) 27 | return NULL; 28 | 29 | for (int i = 0; i < sk_X509_INFO_num(infos); i++) { 30 | X509_INFO *info = sk_X509_INFO_value(infos, i); 31 | 32 | if (info->x_pkey == NULL) 33 | continue; 34 | 35 | if (X509_check_private_key(cert, info->x_pkey->dec_pkey) == 1) 36 | return info->x_pkey->dec_pkey; 37 | } 38 | 39 | return NULL; 40 | } 41 | 42 | static X509 * 43 | find_cert(STACK_OF(X509_INFO) *infos, const EVP_MD *md, 44 | ASN1_OCTET_STRING *hash) 45 | { 46 | if (infos == NULL || md == NULL || hash == NULL) 47 | return NULL; 48 | 49 | for (int i = 0; i < sk_X509_INFO_num(infos); i++) { 50 | X509_INFO *info = sk_X509_INFO_value(infos, i); 51 | uint8_t digest[EVP_MD_size(md)]; 52 | unsigned int dlen; 53 | 54 | if (info->x509 == NULL) 55 | continue; 56 | 57 | if (X509_digest(info->x509, md, digest, &dlen) <= 0) 58 | return NULL; 59 | 60 | if ((int) dlen != hash->length) 61 | continue; 62 | 63 | if (memcmp(digest, hash->data, dlen) != 0) 64 | continue; 65 | 66 | return info->x509; 67 | } 68 | 69 | return NULL; 70 | } 71 | 72 | static bool 73 | find_key_prv(STACK_OF(X509_INFO) *infos, const EVP_MD *md, 74 | STACK_OF(DEO_KEY) *keys, ASN1_OCTET_STRING **key, 75 | EVP_PKEY **prv) 76 | { 77 | if (infos == NULL || md == NULL || key == NULL || prv == NULL) 78 | return NULL; 79 | 80 | for (int i = 0; i < SKM_sk_num(DEO_KEY, keys); i++) { 81 | DEO_KEY *k = SKM_sk_value(DEO_KEY, keys, i); 82 | 83 | *prv = find_prv(infos, find_cert(infos, md, k->hash)); 84 | if (*prv != NULL) { 85 | *key = k->key; 86 | return true; 87 | } 88 | } 89 | 90 | return false; 91 | } 92 | 93 | static ASN1_OCTET_STRING * 94 | unseal(const EVP_CIPHER *cipher, ASN1_OCTET_STRING *key, EVP_PKEY *prv, 95 | DEO_MSG_DEC_REQ *dr) 96 | { 97 | uint8_t buf[dr->data->length]; 98 | ASN1_OCTET_STRING pt = { 99 | .type = V_ASN1_OCTET_STRING, 100 | .length = 0, 101 | .data = buf 102 | }; 103 | AUTO(EVP_CIPHER_CTX, cctx); 104 | int len; 105 | 106 | cctx = EVP_CIPHER_CTX_new(); 107 | if (cctx == NULL) 108 | return NULL; 109 | 110 | if (EVP_OpenInit(cctx, cipher, key->data, key->length, 111 | dr->iv->data, prv) != 1) 112 | return NULL; 113 | 114 | if (EVP_OpenUpdate(cctx, buf, &len, dr->data->data, 115 | dr->data->length) != 1) 116 | return NULL; 117 | pt.length = len; 118 | 119 | if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_SET_TAG, 120 | dr->tag->length, dr->tag->data) != 1) 121 | return NULL; 122 | 123 | if (EVP_OpenFinal(cctx, &buf[len], &len) != 1) 124 | return NULL; 125 | pt.length += len; 126 | 127 | return ASN1_OCTET_STRING_dup(&pt); 128 | } 129 | 130 | static const EVP_CIPHER * 131 | load_cipher(DEO_MSG_DEC_REQ *dr) 132 | { 133 | const EVP_CIPHER *cipher = NULL; 134 | 135 | switch (OBJ_obj2nid(dr->cipher)) { 136 | case NID_aes_128_gcm: 137 | case NID_aes_192_gcm: 138 | case NID_aes_256_gcm: 139 | if (EVP_GCM_TLS_TAG_LEN == dr->tag->length) 140 | break; 141 | 142 | default: 143 | return NULL; 144 | } 145 | 146 | cipher = EVP_get_cipherbyobj(dr->cipher); 147 | if (cipher == NULL) 148 | return NULL; 149 | 150 | if (EVP_CIPHER_iv_length(cipher) != dr->iv->length) 151 | return NULL; 152 | 153 | return cipher; 154 | } 155 | 156 | static const EVP_MD * 157 | load_digest(DEO_MSG_DEC_REQ *dr) 158 | { 159 | switch (OBJ_obj2nid(dr->digest)) { 160 | case NID_sha1: 161 | case NID_sha224: 162 | case NID_sha256: 163 | case NID_sha384: 164 | case NID_sha512: 165 | return EVP_get_digestbyobj(dr->digest); 166 | 167 | default: 168 | return NULL; 169 | } 170 | } 171 | 172 | DEO_ERR 173 | decrypt(ctx *ctx, DEO_MSG_DEC_REQ *dr, ASN1_OCTET_STRING **pt) 174 | { 175 | const EVP_CIPHER *cipher = NULL; 176 | const EVP_MD *digest = NULL; 177 | ASN1_OCTET_STRING *key = NULL; 178 | EVP_PKEY *prv = NULL; 179 | 180 | cipher = load_cipher(dr); 181 | if (cipher == NULL) 182 | return DEO_ERR_NOSUPPORT_CIPHER; 183 | 184 | digest = load_digest(dr); 185 | if (digest == NULL) 186 | return DEO_ERR_NOSUPPORT_DIGEST; 187 | 188 | if (!find_key_prv(ctx->dec, digest, dr->keys, &key, &prv)) 189 | return DEO_ERR_NOTFOUND_KEY; 190 | 191 | *pt = unseal(cipher, key, prv, dr); 192 | return *pt == NULL ? DEO_ERR_INTERNAL : DEO_ERR_NONE; 193 | } 194 | -------------------------------------------------------------------------------- /src/plugins/decryptd/decrypt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "ctx.h" 22 | #include "../asn1.h" 23 | 24 | DEO_ERR 25 | decrypt(ctx *ctx, DEO_MSG_DEC_REQ *dr, ASN1_OCTET_STRING **pt); 26 | -------------------------------------------------------------------------------- /src/plugins/decryptd/deo-decryptd.socket.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Deo decryption daemon socket 3 | ConditionDirectoryNotEmpty=@DEO_CONF@/decrypt.d 4 | ConditionFileNotEmpty=@DEO_CONF@/decryptd.pem 5 | ConditionPathExists=@DEO_CONF@/encrypt.pem 6 | 7 | [Socket] 8 | ListenStream=@DEO_SOCKET@ 9 | Accept=true 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /src/plugins/decryptd/deo-decryptd@.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Deo decryption daemon 3 | ConditionDirectoryNotEmpty=@DEO_CONF@/decrypt.d 4 | ConditionFileNotEmpty=@DEO_CONF@/decryptd.pem 5 | ConditionPathExists=@DEO_CONF@/encrypt.pem 6 | 7 | [Service] 8 | ExecStart=@bindir@/deo decryptd -t @DEO_CONF@/decryptd.pem -e @DEO_CONF@/encrypt.pem -d @DEO_CONF@/decrypt.d 9 | -------------------------------------------------------------------------------- /src/plugins/decryptd/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | #include "decrypt.h" 21 | #include "../../d2i.h" 22 | #include "../../main.h" 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | static bool 35 | option(char c, const char *arg, const char **misc) 36 | { 37 | *misc = arg; 38 | return true; 39 | } 40 | 41 | static ctx * 42 | load_ctx(int argc, char *argv[]) 43 | { 44 | const char *tlsfile = NULL; 45 | const char *encfile = NULL; 46 | const char *decdir = NULL; 47 | ctx *ctx; 48 | 49 | if (!deo_getopt(argc, argv, "ht:e:d:", "", NULL, NULL, 50 | option, &tlsfile, option, &encfile, option, &decdir)) 51 | goto usage; 52 | 53 | if (tlsfile == NULL || encfile == NULL || decdir == NULL) 54 | goto usage; 55 | 56 | ctx = ctx_init(tlsfile, encfile, decdir); 57 | if (ctx != NULL) 58 | return ctx; 59 | 60 | usage: 61 | fprintf(stderr, "Usage: deo decryptd " 62 | "-t -e -d \n"); 63 | return NULL; 64 | } 65 | 66 | static BIO * 67 | start_tls(SSL_CTX *ctx) 68 | { 69 | AUTO(BIO, sio); 70 | SSL *ssl; 71 | 72 | sio = BIO_new_ssl(ctx, 0); 73 | if (sio == NULL) 74 | return NULL; 75 | 76 | if (BIO_get_ssl(sio, &ssl) <= 0) 77 | return NULL; 78 | 79 | if (SSL_set_fd(ssl, SD_LISTEN_FDS_START) <= 0) 80 | return NULL; 81 | 82 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); 83 | 84 | return STEAL(sio); 85 | } 86 | 87 | static int 88 | decryptd(int argc, char *argv[]) 89 | { 90 | int ret = EXIT_FAILURE; 91 | AUTO(DEO_MSG, in); 92 | AUTO(BIO, sio); 93 | AUTO(ctx, ctx); 94 | 95 | ctx = load_ctx(argc, argv); 96 | if (ctx == NULL) 97 | goto error; 98 | 99 | sio = start_tls(ctx->ctx); 100 | if (sio == NULL) 101 | goto error; 102 | 103 | in = d2i_bio_max(&DEO_MSG_it, sio, NULL, DEO_MAX_INPUT); 104 | if (in == NULL) 105 | goto error; 106 | 107 | switch (in->type) { 108 | case DEO_MSG_TYPE_CRT_REQ: 109 | ASN1_item_i2d_bio(&DEO_MSG_it, sio, &(DEO_MSG) { 110 | .type = DEO_MSG_TYPE_CRT_REP, 111 | .value.crt_rep = ctx->crt 112 | }); 113 | break; 114 | 115 | case DEO_MSG_TYPE_DEC_REQ: { 116 | DEO_ERR err = DEO_ERR_NONE; 117 | AUTO(ASN1_OCTET_STRING, pt); 118 | 119 | err = decrypt(ctx, in->value.dec_req, &pt); 120 | if (err != DEO_ERR_NONE) { 121 | SEND_ERR(sio, err); 122 | break; 123 | } 124 | 125 | ASN1_item_i2d_bio(&DEO_MSG_it, sio, &(DEO_MSG) { 126 | .type = DEO_MSG_TYPE_DEC_REP, 127 | .value.dec_rep = pt 128 | }); 129 | break; 130 | } 131 | 132 | default: 133 | break; 134 | } 135 | 136 | ret = EXIT_SUCCESS; 137 | 138 | error: 139 | if (ret != EXIT_SUCCESS) 140 | ERR_print_errors_fp(stderr); 141 | 142 | return ret; 143 | } 144 | 145 | deo_plugin deo = { decryptd, NULL }; 146 | -------------------------------------------------------------------------------- /src/plugins/encrypt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "../main.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define PROCESS_BLOCK 4096 31 | 32 | static int 33 | make_targets(const STACK_OF(X509) *certs, STACK_OF(ASN1_UTF8STRING) *targets) 34 | { 35 | for (int i = 0; i < sk_X509_num(certs); i++) { 36 | const ASN1_STRING *string = NULL; 37 | ASN1_UTF8STRING *target = NULL; 38 | X509_NAME_ENTRY *entry = NULL; 39 | X509 *cert = NULL; 40 | int idx; 41 | 42 | cert = sk_X509_value(certs, i); 43 | 44 | target = ASN1_UTF8STRING_new(); 45 | if (target == NULL) 46 | return ENOMEM; 47 | 48 | if (sk_ASN1_UTF8STRING_push(targets, target) <= 0) { 49 | ASN1_UTF8STRING_free(target); 50 | return ENOMEM; 51 | } 52 | 53 | idx = X509_NAME_get_index_by_NID(cert->cert_info->subject, 54 | NID_commonName, -1); 55 | if (idx < 0) 56 | return EINVAL; 57 | 58 | entry = X509_NAME_get_entry(cert->cert_info->subject, idx); 59 | if (entry == NULL) 60 | return EINVAL; 61 | 62 | string = X509_NAME_ENTRY_get_data(entry); 63 | if (string == NULL) 64 | return EINVAL; 65 | 66 | if (ASN1_STRING_set(target, string->data, string->length) != 1) 67 | return ENOMEM; 68 | } 69 | 70 | return 0; 71 | } 72 | 73 | static bool 74 | make_dec_req(const EVP_CIPHER *cipher, const EVP_MD *md, 75 | const STACK_OF(X509) *certs, const uint8_t *key, 76 | DEO_MSG_DEC_REQ *dr) 77 | { 78 | uint8_t ct[cipher->key_len + EVP_CIPHER_block_size(cipher) - 1]; 79 | uint8_t iv[EVP_CIPHER_iv_length(cipher)]; 80 | uint8_t *ekeys[sk_X509_num(certs)]; 81 | EVP_PKEY *keys[sk_X509_num(certs)]; 82 | uint8_t tag[EVP_GCM_TLS_TAG_LEN]; 83 | int ekeysl[sk_X509_num(certs)]; 84 | AUTO(EVP_CIPHER_CTX, cctx); 85 | bool ret = false; 86 | int ctl = 0; 87 | int tmp = 0; 88 | 89 | memset(ct, 0, sizeof(ct)); 90 | memset(iv, 0, sizeof(iv)); 91 | memset(ekeys, 0, sizeof(ekeys)); 92 | memset(keys, 0, sizeof(keys)); 93 | memset(tag, 0, sizeof(tag)); 94 | memset(ekeysl, 0, sizeof(ekeysl)); 95 | 96 | ASN1_OBJECT_free(dr->cipher); 97 | ASN1_OBJECT_free(dr->digest); 98 | dr->cipher = OBJ_nid2obj(EVP_CIPHER_nid(cipher)); 99 | dr->digest = OBJ_nid2obj(EVP_MD_type(md)); 100 | if (dr->cipher == NULL || dr->digest == NULL) 101 | return false; 102 | 103 | for (int i = 0; i < sk_X509_num(certs); i++) { 104 | X509 *cert = sk_X509_value(certs, i); 105 | uint8_t digest[EVP_MAX_MD_SIZE]; 106 | unsigned int dlen; 107 | DEO_KEY *k; 108 | 109 | keys[i] = cert->cert_info->key->pkey; 110 | ekeys[i] = OPENSSL_malloc(EVP_PKEY_size(keys[i])); 111 | if (ekeys[i] == NULL) 112 | goto error; 113 | 114 | k = DEO_KEY_new(); 115 | if (k == NULL) 116 | goto error; 117 | 118 | if (SKM_sk_push(DEO_KEY, dr->keys, k) <= 0) { 119 | DEO_KEY_free(k); 120 | goto error; 121 | } 122 | 123 | if (X509_digest(cert, md, digest, &dlen) != 1) 124 | goto error; 125 | 126 | if (ASN1_OCTET_STRING_set(k->hash, digest, dlen) != 1) 127 | goto error; 128 | } 129 | 130 | cctx = EVP_CIPHER_CTX_new(); 131 | if (!cctx) 132 | return false; 133 | 134 | if (EVP_SealInit(cctx, cipher, ekeys, ekeysl, 135 | iv, keys, sk_X509_num(certs)) != sk_X509_num(certs)) 136 | goto error; 137 | 138 | if (ASN1_OCTET_STRING_set(dr->iv, iv, sizeof(iv)) != 1) 139 | goto error; 140 | 141 | for (int i = 0; i < SKM_sk_num(DEO_KEY, dr->keys); i++) { 142 | DEO_KEY *k = SKM_sk_value(DEO_KEY, dr->keys, i); 143 | if (ASN1_OCTET_STRING_set(k->key, ekeys[i], ekeysl[i]) != 1) 144 | goto error; 145 | } 146 | 147 | if (EVP_SealUpdate(cctx, ct, &tmp, key, cipher->key_len) != 1) 148 | goto error; 149 | ctl = tmp; 150 | 151 | if (EVP_SealFinal(cctx, ct + ctl, &tmp) != 1) 152 | goto error; 153 | ctl += tmp; 154 | 155 | if (ASN1_OCTET_STRING_set(dr->data, ct, ctl) != 1) 156 | goto error; 157 | 158 | if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_GET_TAG, 159 | sizeof(tag), tag) != 1) 160 | goto error; 161 | 162 | if (ASN1_OCTET_STRING_set(dr->tag, tag, sizeof(tag)) != 1) 163 | goto error; 164 | 165 | ret = true; 166 | 167 | error: 168 | for (int i = 0; i < sk_X509_num(certs) && ekeys[i] != NULL; i++) 169 | OPENSSL_free(ekeys[i]); 170 | 171 | return ret; 172 | } 173 | 174 | static DEO_HEADER * 175 | make_header(const STACK_OF(X509) *anchors, const STACK_OF(X509) *targets, 176 | const uint8_t *key) 177 | { 178 | const EVP_CIPHER *cipher = EVP_aes_256_gcm(); 179 | const EVP_MD *md = EVP_sha256(); 180 | uint8_t iv[EVP_MAX_IV_LENGTH]; 181 | AUTO(DEO_HEADER, hdr); 182 | 183 | if (cipher == NULL || md == NULL) 184 | return NULL; 185 | 186 | hdr = DEO_HEADER_new(); 187 | if (hdr == NULL) 188 | return NULL; 189 | 190 | if (!make_dec_req(cipher, md, targets, key, hdr->req)) 191 | return NULL; 192 | 193 | for (int i = 0; anchors != NULL && i < sk_X509_num(anchors); i++) { 194 | X509 *cert = X509_dup(sk_X509_value(anchors, i)); 195 | if (cert == NULL) 196 | return NULL; 197 | 198 | if (sk_X509_push(hdr->anchors, cert) <= 0) { 199 | X509_free(cert); 200 | return NULL; 201 | } 202 | } 203 | 204 | if (make_targets(targets, hdr->targets) != 0) 205 | return NULL; 206 | 207 | if (RAND_bytes(iv, cipher->iv_len) != 1) 208 | return NULL; 209 | 210 | if (ASN1_OCTET_STRING_set(hdr->iv, iv, cipher->iv_len) != 1) 211 | return NULL; 212 | 213 | return STEAL(hdr); 214 | } 215 | 216 | static bool 217 | encrypt_body(const DEO_HEADER *hdr, const uint8_t *key, FILE *in, FILE *out) 218 | { 219 | const EVP_CIPHER *cipher = EVP_get_cipherbyobj(hdr->req->cipher); 220 | uint8_t ct[PROCESS_BLOCK + EVP_MAX_BLOCK_LENGTH]; 221 | uint8_t tag[EVP_GCM_TLS_TAG_LEN]; 222 | AUTO(EVP_CIPHER_CTX, cctx); 223 | uint8_t pt[PROCESS_BLOCK]; 224 | size_t ptl; 225 | int ctl; 226 | 227 | if (ASN1_item_i2d_fp(&DEO_HEADER_it, stdout, (void *) hdr) != 1) 228 | return false; 229 | 230 | cctx = EVP_CIPHER_CTX_new(); 231 | if (!cctx) 232 | return false; 233 | 234 | if (EVP_EncryptInit_ex(cctx, cipher, NULL, key, hdr->iv->data) != 1) 235 | return false; 236 | 237 | while (!feof(stdin)) { 238 | ptl = fread(pt, 1, sizeof(pt), stdin); 239 | if (ferror(stdin)) 240 | return false; 241 | 242 | ctl = 0; 243 | if (EVP_EncryptUpdate(cctx, ct, &ctl, pt, ptl) != 1) 244 | return false; 245 | 246 | if (fwrite(ct, 1, ctl, stdout) != (size_t) ctl) 247 | return false; 248 | } 249 | 250 | ctl = 0; 251 | if (EVP_EncryptFinal(cctx, ct, &ctl) != 1) 252 | return false; 253 | 254 | if (fwrite(ct, 1, ctl, stdout) != (size_t) ctl) 255 | return false; 256 | 257 | if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_GCM_GET_TAG, 258 | sizeof(tag), tag) != 1) 259 | return false; 260 | 261 | return fwrite(tag, 1, sizeof(tag), stdout) == sizeof(tag); 262 | } 263 | 264 | static int 265 | encrypt(int argc, char *argv[]) 266 | { 267 | uint8_t key[EVP_MAX_KEY_LENGTH]; 268 | AUTO_STACK(X509, anchors); 269 | AUTO(DEO_HEADER, hdr); 270 | AUTO_STACK(X509, certs); 271 | 272 | if (!deo_getopt(argc, argv, "ha:", "", NULL, NULL, 273 | deo_anchors, &anchors) 274 | || sk_X509_num(anchors) == 0 || argc - optind < 1) { 275 | fprintf(stderr, "Usage: deo encrypt " 276 | "-a [...] " 277 | "< PLAINTEXT > CIPHERTEXT\n"); 278 | return EXIT_FAILURE; 279 | } 280 | 281 | certs = sk_X509_new_null(); 282 | if (certs == NULL) 283 | error(EXIT_FAILURE, ENOMEM, "Unable to create anchors"); 284 | 285 | for (int i = optind; i < argc; i++) { 286 | AUTO_STACK(X509, chain); 287 | AUTO(FILE, fp); 288 | X509 *tmp; 289 | 290 | fp = fopen(argv[i], "r"); 291 | if (fp != NULL) { 292 | chain = sk_X509_new_null(); 293 | if (chain == NULL) 294 | error(EXIT_FAILURE, ENOMEM, "Unable to create anchors chain"); 295 | 296 | if (!deo_load(fp, chain)) 297 | error(EXIT_FAILURE, 0, "Unable to load anchors"); 298 | 299 | if (sk_X509_num(chain) == 0) 300 | error(EXIT_FAILURE, 0, "File '%s' is empty", argv[i]); 301 | } else { 302 | AUTO(DEO_MSG, rep); 303 | 304 | rep = deo_request(anchors, &(ASN1_UTF8STRING) { 305 | .data = (uint8_t *) argv[i], 306 | .length = strlen(argv[i]) 307 | }, &(DEO_MSG) { 308 | .type = DEO_MSG_TYPE_CRT_REQ, 309 | .value.crt_req = &(ASN1_NULL) {0} 310 | }); 311 | 312 | if (rep == NULL) 313 | error(EXIT_FAILURE, 0, "Unable to communicate with server"); 314 | 315 | switch (rep->type) { 316 | case DEO_MSG_TYPE_CRT_REP: 317 | if (!deo_validate(anchors, rep->value.crt_rep)) 318 | error(EXIT_FAILURE, 0, "Server returned untrusted certs"); 319 | 320 | chain = STEAL(rep->value.crt_rep); 321 | break; 322 | 323 | case DEO_MSG_TYPE_ERR: 324 | error(EXIT_FAILURE, ENOMEM, "Server returned: %s", 325 | deo_err_string(ASN1_ENUMERATED_get(rep->value.err))); 326 | 327 | default: 328 | error(EXIT_FAILURE, 0, "Received unknown message from server"); 329 | } 330 | 331 | if (sk_X509_num(chain) == 0) 332 | error(EXIT_FAILURE, 0, "Empty reply from '%s'", argv[i]); 333 | } 334 | 335 | tmp = sk_X509_shift(chain); 336 | if (sk_X509_push(certs, tmp) <= 0) { 337 | X509_free(tmp); 338 | error(EXIT_FAILURE, ENOMEM, "Unable to add target certificate"); 339 | } 340 | } 341 | 342 | if (RAND_bytes(key, sizeof(key)) != 1) 343 | error(EXIT_FAILURE, ENOMEM, "Unable to generate random key"); 344 | 345 | hdr = make_header(anchors, certs, key); 346 | if (hdr == NULL) 347 | error(EXIT_FAILURE, ENOMEM, "Error building header"); 348 | 349 | if (!encrypt_body(hdr, key, stdin, stdout)) 350 | error(EXIT_FAILURE, 0, "Failure encrypting message body"); 351 | 352 | return 0; 353 | } 354 | 355 | deo_plugin deo = { 356 | encrypt, "Encrypts input to all specified targets" 357 | }; 358 | -------------------------------------------------------------------------------- /src/plugins/query.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "../main.h" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | static int 27 | query(int argc, char *argv[]) 28 | { 29 | DEO_ERR err = DEO_ERR_NONE; 30 | AUTO_STACK(X509, anchors); 31 | AUTO(DEO_MSG, rep); 32 | 33 | if (!deo_getopt(argc, argv, "ha:", "", NULL, NULL, 34 | deo_anchors, &anchors) 35 | || sk_X509_num(anchors) == 0 || argc - optind != 1) { 36 | fprintf(stderr, "Usage: deo query -a \n"); 37 | return EXIT_FAILURE; 38 | } 39 | 40 | rep = deo_request(anchors, &(ASN1_UTF8STRING) { 41 | .data = (uint8_t *) argv[optind], 42 | .length = strlen(argv[optind]) 43 | }, &(DEO_MSG) { 44 | .type = DEO_MSG_TYPE_CRT_REQ, 45 | .value.crt_req = &(ASN1_NULL) {0} 46 | }); 47 | 48 | if (rep == NULL) { 49 | ERR_print_errors_fp(stderr); 50 | return EXIT_FAILURE; 51 | } 52 | 53 | switch (rep->type) { 54 | case DEO_MSG_TYPE_ERR: 55 | err = ASN1_ENUMERATED_get(rep->value.err); 56 | error(EXIT_FAILURE, 0, "Server error: %s", deo_err_string(err)); 57 | 58 | case DEO_MSG_TYPE_CRT_REP: 59 | if (!deo_validate(anchors, rep->value.crt_rep)) 60 | error(EXIT_FAILURE, 0, "Validation failed: %s", argv[optind]); 61 | 62 | for (int i = 0; i < sk_X509_num(rep->value.crt_rep); i++) 63 | PEM_write_X509(stdout, sk_X509_value(rep->value.crt_rep, i)); 64 | 65 | return 0; 66 | 67 | default: 68 | error(EXIT_FAILURE, 0, "Invalid response"); 69 | } 70 | 71 | return EXIT_FAILURE; 72 | } 73 | 74 | deo_plugin deo = { 75 | query, "Fetches and verifies a server's encryption certificate chain" 76 | }; 77 | -------------------------------------------------------------------------------- /src/plugins/targets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Red Hat, Inc. 3 | * Author: Nathaniel McCallum 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "../main.h" 20 | 21 | #include 22 | 23 | static int 24 | targets(int argc, char *argv[]) 25 | { 26 | AUTO(DEO_HEADER, hdr); 27 | 28 | if (!deo_getopt(argc, argv, "h", "", NULL, NULL)) { 29 | fprintf(stderr, "Usage: deo targets < ENCDATA > TARGETS\n"); 30 | return EXIT_FAILURE; 31 | } 32 | 33 | hdr = d2i_fp_max(&DEO_HEADER_it, stdin, NULL, DEO_MAX_INPUT); 34 | if (hdr == NULL) { 35 | ERR_print_errors_fp(stderr); 36 | return EXIT_FAILURE; 37 | } 38 | 39 | for (int i = 0; i < sk_ASN1_UTF8STRING_num(hdr->targets); i++) { 40 | ASN1_UTF8STRING *str = sk_ASN1_UTF8STRING_value(hdr->targets, i); 41 | fprintf(stdout, "%*s\n", str->length, str->data); 42 | } 43 | 44 | return EXIT_SUCCESS; 45 | } 46 | 47 | deo_plugin deo = { 48 | targets, "Prints the targets for encrypted input" 49 | }; 50 | -------------------------------------------------------------------------------- /tests/ca.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright (c) 2015 Red Hat, Inc. 4 | # Author: Nathaniel McCallum 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | import atexit 21 | import os 22 | import shutil 23 | import subprocess 24 | import tempfile 25 | 26 | 27 | class CA(object): 28 | CNF = """ 29 | HOME = . 30 | RANDFILE = $ENV::HOME/.rnd 31 | 32 | [ ca ] 33 | default_ca = CA_default 34 | 35 | [ CA_default ] 36 | dir = %s 37 | certs = $dir/certs 38 | crl_dir = $dir/crl 39 | database = $dir/index.txt 40 | new_certs_dir = $dir/newcerts 41 | certificate = $dir/ca.pem 42 | serial = $dir/serial 43 | crlnumber = $dir/crlnumber 44 | crl = $dir/crl.pem 45 | private_key = $dir/private/ca.pem 46 | RANDFILE = $dir/private/.rand 47 | 48 | x509_extensions = usr_cert 49 | name_opt = ca_default 50 | cert_opt = ca_default 51 | default_days = 1 52 | default_crl_days = 30 53 | default_md = sha256 54 | preserve = no 55 | policy = policy_anything 56 | 57 | [ policy_anything ] 58 | countryName = optional 59 | stateOrProvinceName = optional 60 | localityName = optional 61 | organizationName = optional 62 | organizationalUnitName = optional 63 | commonName = supplied 64 | emailAddress = optional 65 | 66 | [ req ] 67 | default_bits = 2048 68 | default_md = sha256 69 | default_keyfile = privkey.pem 70 | x509_extensions = v3_ca 71 | string_mask = utf8only 72 | 73 | [ usr_cert ] 74 | basicConstraints = CA:FALSE 75 | subjectKeyIdentifier = hash 76 | authorityKeyIdentifier = keyid,issuer 77 | 78 | [ v3_ca ] 79 | subjectKeyIdentifier = hash 80 | authorityKeyIdentifier = keyid:always,issuer 81 | basicConstraints = CA:true 82 | """ 83 | 84 | @property 85 | def certificate(self): 86 | return os.path.join(self.__dir, 'ca.pem') 87 | 88 | def __cmd(self, *args): 89 | with open("/dev/null", "w") as f: 90 | r = subprocess.call(("openssl",) + args, stdout=f, stderr=f) 91 | assert r == 0 92 | 93 | def __name(self): 94 | with open(os.path.join(self.__dir, 'serial')) as f: 95 | return f.read().strip() 96 | 97 | def key(self): 98 | out = os.path.join(self.__dir, 'private', '%s.pem' % self.__name()) 99 | self.__cmd('genrsa', '-out', out) 100 | return out 101 | 102 | def csr(self, key, subj, *args): 103 | out = os.path.join(self.__dir, 'csr', '%s.csr' % self.__name()) 104 | self.__cmd('req', '-new', '-key', key, '-subj', subj, 105 | '-out', out, *args) 106 | return out 107 | 108 | def crt(self, csr, *args): 109 | out = os.path.join(self.__dir, 'newcerts', '%s.pem' % self.__name()) 110 | self.__cmd('ca', '-batch', '-config', os.path.join(self.__dir, "cnf"), 111 | '-in', csr, *args) 112 | return out 113 | 114 | def __init__(self, subj, parent=None): 115 | self.__dir = tempfile.mkdtemp() 116 | atexit.register(lambda: shutil.rmtree(self.__dir)) 117 | 118 | for d in ('certs', 'crl', 'newcerts', 'private', 'csr'): 119 | os.mkdir(os.path.join(self.__dir, d)) 120 | 121 | open(os.path.join(self.__dir, 'index.txt'), 'w').close() 122 | with open(os.path.join(self.__dir, 'serial'), 'w') as f: 123 | f.write('1000') 124 | 125 | with open(os.path.join(self.__dir, 'cnf'), 'w') as f: 126 | f.write(self.CNF % self.__dir) 127 | 128 | if parent is None: 129 | key = self.key() 130 | csr = self.csr(key, subj, '-x509', '-extensions', 'v3_ca') 131 | os.rename(key, os.path.join(self.__dir, 'private', 'ca.pem')) 132 | os.rename(csr, os.path.join(self.__dir, 'ca.pem')) 133 | 134 | else: 135 | key = parent.key() 136 | csr = parent.csr(key, subj) 137 | crt = parent.crt(csr, '-extensions', 'v3_ca') 138 | shutil.copyfile(key, os.path.join(self.__dir, 'private', 'ca.pem')) 139 | shutil.copyfile(crt, os.path.join(self.__dir, 'ca.pem')) 140 | -------------------------------------------------------------------------------- /tests/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright (c) 2015 Red Hat, Inc. 4 | # Author: Nathaniel McCallum 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | import atexit 21 | import multiprocessing 22 | import os 23 | import shutil 24 | import socket 25 | import tempfile 26 | 27 | 28 | class Server(object): 29 | @property 30 | def hp(self): 31 | if self.port is None: 32 | port = '' 33 | else: 34 | port = ':%d' % self.port 35 | 36 | return self.host + port 37 | 38 | def __file(self, path, srcs): 39 | with open(path, "w") as f: 40 | for src in srcs: 41 | with open(src) as g: 42 | f.write(g.read()) 43 | 44 | def __init__(self, tls, enc, dec, host, port=None): 45 | self.host = host 46 | self.port = port 47 | super(Server, self).__init__() 48 | 49 | self.__dir = tempfile.mkdtemp() 50 | atexit.register(lambda: shutil.rmtree(self.__dir)) 51 | 52 | t = os.path.join(self.__dir, "tls.pem") 53 | e = os.path.join(self.__dir, "enc.pem") 54 | d = os.path.join(self.__dir, "dec.d") 55 | 56 | bin = os.environ['DEO_BIN'] 57 | self.__arg = [bin, 'decryptd', '-t', t, '-e', e, '-d', d] 58 | 59 | os.mkdir(d) 60 | self.__file(t, tls) 61 | self.__file(e, enc) 62 | for name, srcs in dec.items(): 63 | self.__file(os.path.join(d, name + '.pem'), srcs) 64 | 65 | def __enter__(self): 66 | def f(host, port): 67 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 68 | s.bind((host, port)) 69 | s.listen(1) 70 | 71 | conn = s.accept()[0] 72 | s.close() 73 | 74 | os.dup2(conn.fileno(), 3) 75 | conn.close() 76 | os.execv(self.__arg[0], self.__arg) 77 | 78 | port = self.port if self.port else 5700 79 | self.p = multiprocessing.Process(target=f, args=(self.host, port)) 80 | self.p.start() 81 | 82 | def __exit__(self, type, value, tb): 83 | if self.p.is_alive(): 84 | self.p.terminate() 85 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright (c) 2015 Red Hat, Inc. 4 | # Author: Nathaniel McCallum 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | from ca import CA 21 | from server import Server 22 | import unittest 23 | import os 24 | import time 25 | import subprocess 26 | 27 | 28 | class Test(unittest.TestCase): 29 | def __makeServer(self, subj, host, port=None): 30 | tls_key = self.ca_A_tls.key() 31 | tls_csr = self.ca_A_tls.csr(tls_key, subj) 32 | tls_crt = self.ca_A_tls.crt(tls_csr) 33 | 34 | enc_key = self.ca_A_enc.key() 35 | enc_csr = self.ca_A_enc.csr(enc_key, subj) 36 | enc_crt = self.ca_A_enc.crt(enc_csr) 37 | 38 | return Server([ 39 | tls_key, 40 | tls_crt, 41 | self.ca_A_tls.certificate, 42 | self.ca_A.certificate, 43 | ], [ 44 | enc_crt, 45 | self.ca_A_enc.certificate, 46 | self.ca_A.certificate, 47 | ], { 48 | 'decrypt': [ 49 | enc_key, 50 | enc_crt, 51 | self.ca_A_enc.certificate, 52 | self.ca_A.certificate, 53 | ] 54 | }, host, port) 55 | 56 | def __query(self, ca, srv): 57 | cmd = "$DEO_BIN query -a %s %s" % (ca.certificate, srv.hp) 58 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) 59 | out, err = p.communicate() 60 | sep = "-----BEGIN CERTIFICATE-----".encode('utf-8') 61 | return p.returncode == 0 and len(out.split(sep)) == 4 62 | 63 | def __encrypt(self, input, ca, *argv): 64 | arg = " ".join(map(lambda x: x.hp, argv)) 65 | cmd = "$DEO_BIN encrypt -a " + ca.certificate + " " + arg 66 | 67 | p = subprocess.Popen(cmd, stdin=subprocess.PIPE, 68 | stdout=subprocess.PIPE, shell=True) 69 | out, err = p.communicate(input=input) 70 | return None if p.returncode != 0 else out 71 | 72 | def __targets(self, input): 73 | p = subprocess.Popen("$DEO_BIN targets", stdin=subprocess.PIPE, 74 | stdout=subprocess.PIPE, shell=True) 75 | out, err = p.communicate(input=input) 76 | if p.returncode != 0: 77 | return None 78 | 79 | return out.decode('utf-8').strip().split("\n") 80 | 81 | def __decrypt(self, input): 82 | p = subprocess.Popen("$DEO_BIN decrypt", stdin=subprocess.PIPE, 83 | stdout=subprocess.PIPE, shell=True) 84 | out, err = p.communicate(input=input) 85 | return None if p.returncode != 0 else out 86 | 87 | def setUp(self): 88 | self.ca_A = CA('/CN=A') 89 | self.ca_B = CA('/CN=B') 90 | 91 | self.ca_A_tls = CA('/CN=A_tls', self.ca_A) 92 | self.ca_A_enc = CA('/CN=A_enc', self.ca_A) 93 | 94 | self.srvA = self.__makeServer('/CN=localhost', 'localhost') 95 | self.srvB = self.__makeServer('/CN=localhost:5701', 'localhost', 5701) 96 | 97 | def testQuery(self): 98 | "Test that basic querying works." 99 | 100 | with self.srvA: 101 | assert self.__query(self.ca_A, self.srvA) 102 | with self.srvB: 103 | assert self.__query(self.ca_A, self.srvB) 104 | 105 | def testQueryValidation(self): 106 | "Test that queries fail when a certificate doesn't validate." 107 | 108 | with self.srvA: 109 | assert not self.__query(self.ca_B, self.srvA) 110 | with self.srvB: 111 | assert not self.__query(self.ca_B, self.srvB) 112 | with self.srvA: 113 | assert not self.__query(self.ca_A_tls, self.srvA) 114 | with self.srvB: 115 | assert not self.__query(self.ca_A_tls, self.srvB) 116 | with self.srvA: 117 | assert not self.__query(self.ca_A_enc, self.srvA) 118 | with self.srvB: 119 | assert not self.__query(self.ca_A_enc, self.srvB) 120 | 121 | def testEncTargetsDec(self): 122 | "Test that basic multi-target encryption/decryption works." 123 | 124 | pt = "hello".encode('utf-8') 125 | 126 | with self.srvA: 127 | with self.srvB: 128 | out = self.__encrypt(pt, self.ca_A, self.srvA, self.srvB) 129 | 130 | assert out is not None 131 | assert self.__targets(out) == [self.srvA.hp, self.srvB.hp] 132 | 133 | with self.srvA: 134 | assert self.__decrypt(out) == pt 135 | 136 | with self.srvB: 137 | assert self.__decrypt(out) == pt 138 | 139 | assert self.__decrypt(out) is None 140 | 141 | def testEncValidation(self): 142 | "Test that encryption fails when a certificate doesn't validate." 143 | 144 | pt = "hello".encode('utf-8') 145 | 146 | with self.srvA: 147 | assert self.__encrypt(pt, self.ca_B, self.srvA) is None 148 | with self.srvA: 149 | assert self.__encrypt(pt, self.ca_A_tls, self.srvA) is None 150 | with self.srvA: 151 | assert self.__encrypt(pt, self.ca_A_enc, self.srvA) is None 152 | 153 | def testHostnameVerification(self): 154 | srv = self.__makeServer('/CN=localhost:5702', 'localhost') 155 | with srv: 156 | assert not self.__query(self.ca_A, srv) 157 | 158 | pt = "hello".encode('utf-8') 159 | with srv: 160 | assert self.__encrypt(pt, self.ca_A, srv) is None 161 | 162 | if __name__ == '__main__': 163 | unittest.main() 164 | --------------------------------------------------------------------------------