├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README.org ├── authinfo.pc.in ├── bootstrap ├── config.site └── coverage ├── configure.ac ├── dist └── archlinux │ ├── .gitignore │ └── PKGBUILD.in ├── docs ├── Doxyfile.in ├── Makefile.am └── offlineimap.py ├── include └── authinfo.h ├── m4 ├── .gitignore └── ac_define_dir.m4 ├── python ├── __init__.py.in └── authinfo.py ├── src ├── authinfo.c ├── authinfo_data.h ├── base64.c ├── base64.h ├── cli.c ├── pinentry.c ├── pinentry.h ├── utils.c └── utils.h └── tests ├── authinfo_gpg_tests.c ├── authinfo_parsing_tests.c └── files └── gpg_tests ├── gpged_password ├── gpged_password_bad ├── private.key ├── public.key ├── read_file └── read_file.gpg /.gitignore: -------------------------------------------------------------------------------- 1 | .deps/ 2 | .libs/ 3 | /Makefile 4 | /Makefile.in 5 | /aclocal.m4 6 | /autom4te.cache/ 7 | /config.h 8 | /config.h.in 9 | /config.log 10 | /config.guess 11 | /config.status 12 | /config.sub 13 | /configure 14 | /depcomp 15 | /install-sh 16 | /libauthinfo.la 17 | /libauthinfo_impl.la 18 | /libtool 19 | /ltmain.sh 20 | /missing 21 | /authinfo 22 | /stamp-h1 23 | /compile 24 | /test-driver 25 | /*.log 26 | /*.trs 27 | /authinfo_parsing_tests 28 | /authinfo_gpg_tests 29 | /GPATH 30 | /GRTAGS 31 | /GTAGS 32 | *.gcno 33 | *.gcda 34 | *.gcov 35 | lcov.info 36 | /lcov/ 37 | *.o 38 | *.lo 39 | /docs/doxygen 40 | /docs/Doxyfile 41 | /docs/Makefile 42 | /docs/Makefile.in 43 | /py-compile 44 | /python/__init__.py 45 | /src/.dirstamp 46 | /tests/.dirstamp 47 | /authinfo.pc 48 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aartamonau/authinfo/0098545479fa81234ebc1c2951610cb8c70f5c44/AUTHORS -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aartamonau/authinfo/0098545479fa81234ebc1c2951610cb8c70f5c44/ChangeLog -------------------------------------------------------------------------------- /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 commands `./configure; make; make install' should 16 | 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 | # -*- mode: makefile-automake; -*- 2 | ACLOCAL_AMFLAGS = -I m4 3 | 4 | SUBDIRS = docs 5 | 6 | if DEBUG 7 | DEBUG_CPPFLAGS = -DDEBUG 8 | endif 9 | 10 | if GCC 11 | GCC_CFLAGS = -fvisibility=hidden -Wall -Werror 12 | 13 | if SANITIZER 14 | SANITIZER_CFLAGS = -fsanitize=undefined -fsanitize=address 15 | endif 16 | 17 | endif 18 | 19 | AM_CFLAGS = $(GCC_CFLAGS) $(SANITIZER_CFLAGS) $(GPGME_PTHREAD_CFLAGS) $(LIBASSUAN_CFLAGS) 20 | AM_CPPFLAGS = -I$(top_srcdir)/include/ $(DEBUG_CPPFLAGS) 21 | AM_LDFLAGS = $(GPGME_PTHREAD_LIBS) $(LIBASSUAN_LIBS) 22 | 23 | EXTRA_DIST = $(top_srcdir)/config.site/coverage \ 24 | $(top_srcdir)/tests/files/gpg_tests/public.key \ 25 | $(top_srcdir)/tests/files/gpg_tests/private.key \ 26 | $(top_srcdir)/tests/files/gpg_tests/read_file \ 27 | $(top_srcdir)/tests/files/gpg_tests/read_file.gpg \ 28 | $(top_srcdir)/tests/files/gpg_tests/gpged_password \ 29 | $(top_srcdir)/tests/files/gpg_tests/gpged_password_bad 30 | 31 | pkginclude_HEADERS = include/authinfo.h 32 | 33 | noinst_LTLIBRARIES = libauthinfo_impl.la 34 | libauthinfo_impl_la_SOURCES = src/utils.h \ 35 | src/utils.c \ 36 | src/base64.h \ 37 | src/base64.c \ 38 | src/authinfo_data.h \ 39 | src/authinfo.c \ 40 | src/pinentry.h \ 41 | src/pinentry.c 42 | 43 | lib_LTLIBRARIES = libauthinfo.la 44 | libauthinfo_la_SOURCES = 45 | libauthinfo_la_LIBADD = libauthinfo_impl.la 46 | libauthinfo_la_LDFLAGS = -version-info $(LIBAUTHINFO_VERSION_INFO) 47 | 48 | if BUILD_CLI 49 | AUTHINFO = authinfo 50 | endif 51 | 52 | bin_PROGRAMS = $(AUTHINFO) 53 | authinfo_SOURCES = src/cli.c 54 | authinfo_LDADD = libauthinfo_impl.la 55 | 56 | pkgconfigdir = $(libdir)/pkgconfig 57 | pkgconfig_DATA = authinfo.pc 58 | 59 | if HAVE_CHECK 60 | check_PROGRAMS = authinfo_parsing_tests authinfo_gpg_tests 61 | else 62 | check_PROGRAMS = 63 | endif 64 | 65 | TESTS = $(check_PROGRAMS) 66 | 67 | authinfo_parsing_tests_SOURCES = tests/authinfo_parsing_tests.c 68 | authinfo_parsing_tests_LDADD = $(CHECK_LIBS) libauthinfo_impl.la 69 | authinfo_parsing_tests_CFLAGS = $(CHECK_CFLAGS) $(AM_CFLAGS) 70 | 71 | authinfo_gpg_tests_SOURCES = tests/authinfo_gpg_tests.c 72 | authinfo_gpg_tests_LDADD = $(CHECK_LIBS) libauthinfo_impl.la 73 | authinfo_gpg_tests_CFLAGS = $(CHECK_CFLAGS) $(AM_CFLAGS) 74 | authinfo_gpg_tests_CPPFLAGS = -DTOP_SRCDIR='"$(top_srcdir)"' $(AM_CPPFLAGS) 75 | 76 | if ENABLE_PYTHON 77 | pkgpython_PYTHON = python/__init__.py python/authinfo.py 78 | endif 79 | 80 | clean-local: 81 | find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete 82 | rm -rf lcov.info lcov 83 | 84 | .PHONY: lcov 85 | if HAVE_LCOV 86 | lcov: 87 | lcov --capture --directory . --output-file lcov.info 88 | genhtml lcov.info --output-directory lcov/ 89 | 90 | @echo "lcov report is ready: $$(pwd)/lcov/index.html" 91 | if HAVE_XDG_OPEN 92 | xdg-open lcov/index.html 93 | endif 94 | 95 | else 96 | lcov: 97 | @echo "lcov not installed" 98 | endif 99 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aartamonau/authinfo/0098545479fa81234ebc1c2951610cb8c70f5c44/NEWS -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+AUTHOR: Aliaksey Artamonau 2 | #+EMAIL: aliaksiej.artamonau@gmail.com 3 | 4 | * What is authinfo? 5 | 6 | Authinfo is supposed to be a "keep it simple stupid" and "don't reinvent the 7 | wheel" password manager. It's simple in that the passwords are stored in a 8 | human readable text file. Actually, it doesn't even support updating that 9 | file. It doesn't reinvent the wheel because it reuses the file format that 10 | has been there for decades, namely [[http://linux.about.com/library/cmd/blcmdl5_netrc.htm][netrc]] with extensions introduced in 11 | [[https://www.gnu.org/software/emacs/manual/html_node/auth/Help-for-users.html#Help-for-users][Emacs]]. Similarly, authinfo can read files that are encrypted with 12 | [[http://www.gnupg.org/][GnuPG]]. Since authinfo cannot update password file on its own, it's should be 13 | used in conjunction with your favorite text editor. It's especially 14 | convenient if your editor can also seamlessly decrypt GnuPG files (like 15 | [[https://www.gnu.org/software/emacs/][Emacs]] does). 16 | 17 | * Getting authinfo 18 | ** Building from source 19 | 20 | Authinfo follows usual GNU Autotools build procedure: 21 | 22 | #+BEGIN_SRC sh 23 | # Only required if you're missing ./configure (e.g. you're building from git) 24 | $ ./autogen.sh 25 | 26 | # Check build deps and configure the project. 27 | $ ./configure 28 | 29 | # Compile and install everything. 30 | $ make all install 31 | #+END_SRC 32 | 33 | If you encounter an error about undefined macro =AM_PATH_GPGME_PTHREAD= when 34 | running =./autogen.sh= you will need to install the =libgpgme= autoconf 35 | macros. On Ubuntu/Debian install the =libgpgme-dev= package, on Fedora or 36 | other RPM based distros install the =gpgme-devel= package. 37 | 38 | *** Optional features: 39 | - GPG support (default is on) 40 | 41 | To disable pass =--disable-gpg= flag to configure script. When enabled, 42 | [[http://www.gnupg.org/related_software/gpgme/][GPGME]] must be installed in the system. 43 | 44 | - Python2 bindings (default is on) 45 | 46 | To disable pass =--disable-python= flag to configure script. Minimal 47 | supported python version is 2.5. Python3 is not supported. 48 | 49 | - CLI tool (default is on) 50 | 51 | To disable pass =--disable-cli= flag to configure script. 52 | 53 | * Using it 54 | ** Password file 55 | 56 | By default authinfo will look for password file in the following locations 57 | (in order): 58 | 59 | - =$HOME/.authinfo.gpg= (if GPG support is enabled) 60 | - =$HOME/.authinfo= 61 | - =$HOME/.netrc.gpg= (if GPG support is enabled) 62 | - =$HOME/.netrc= 63 | - =/etc/authinfo.gpg= (if GPG support is enabled) 64 | - =/etc/authinfo= 65 | - =/etc/netrc.gpg= (if GPG support is enabled) 66 | - =/etc/netrc= 67 | 68 | When password file has .gpg extension, it is assumed to be GPG encrypted 69 | and authinfo will try to decrypt it. 70 | 71 | ** Password file format 72 | 73 | Authinfo uses extended netrc file format introduced by Gnus. In short, 74 | password file consists of one or more lines of the following format: 75 | 76 | #+BEGIN_EXAMPLE 77 | host [user ] [password ] [port ] [force true|yes] 78 | #+END_EXAMPLE 79 | 80 | Some keywords has synonyms: 81 | 82 | - =machine= is synonymous to =host= 83 | - =login= and =account= are synonymous to =user= 84 | - =protocol= is synonymous to =port= 85 | 86 | The meaning of the most keywords should be obvious. Keyword =force= has no 87 | practical use, it's supported just for compliance with Gnus. 88 | 89 | Additionally, =host = key/value pair can be substituted by a single 90 | keyword =default=. The latter matches any host, so it should (but not 91 | required) be the last one in the file. 92 | 93 | Any of the key/value pairs can be omitted. 94 | 95 | Password can be specified in two formats: plain text and 96 | GPG-encrypted. Plain text passwords can be optionally put in double 97 | quotes. This allows to use password having white-space characters. Double 98 | quote can be escaped inside double-quoted password by prepending it by 99 | backslash. Similarly, backslash can be escaped by another 100 | backslash. GPG-encrypted passwords are of the form =gpg:=. 102 | 103 | There's also =macdef= construct that is supported only for compliance with 104 | original netrc file format. It's a multi-line construct that starts with a 105 | line of the form =macdef = and ends with an empty line. See also 106 | example below. 107 | 108 | Lines that start with # character are considered comments. 109 | 110 | Password files are processed line by line in order. This means that more 111 | specific entries should be put first, more general ones should be put in 112 | the end of the file. 113 | 114 | Example of password file: 115 | 116 | #+BEGIN_EXAMPLE 117 | # macro definition (silently ignored) 118 | macdef test 119 | test 120 | 121 | # password is 'pass"\ word' 122 | host hostname user username password "pass\"\\ word" protocol protocol 123 | 124 | # GPG-encrypted password 125 | host hostname user username password gpg:hQEMA2iK9nrzfXUQAQf+NNAyrTm6HH9T267LOdDIpxGgkG2yvd+2C179zHrTmxLqGs0oVH1Fi2kQIlnACATF/JxoCN9+dKJ1qOmNRx0l9bSkoLBqGPOI8yDu0jyYMZw35Bz7+12uMaDFtapluYq6YZrNcLIpHkSB/dq5is127+abUY68C1+lvGgO9ry+r74e5AcHl8xBOFly3rj/hTuRTDwPemog6kZ2gs9Swjffiqt5kJm/fgctKRhntPqWYQz3jfcc1oQQN9SRuy6y3cy4jaqB7VyQNi38630vqHiuf0Ha+kFe9xYonkWtAxpJyPPzQMegjd0IsCjvZyKezyQeX9EcMSEd1b9U/Ot0KS+1+9JDAd0Z87Cp7q+rYThR5OThbIu3iW9L4ofIqMolHqwsXux2BbiRafzjzF/RVzoy+KkBv0P5GBX0lPXR0ytWlwsTWRSLkQ== 126 | 127 | # default password 128 | default password default-password 129 | #+END_EXAMPLE 130 | 131 | Password file can be checked for syntactical correctness by authinfo CLI 132 | utility as follows: 133 | 134 | #+BEGIN_SRC sh 135 | authinfo --validate --path 136 | #+END_SRC 137 | 138 | ** CLI tool 139 | 140 | #+BEGIN_EXAMPLE 141 | $ authinfo --help 142 | Usage: authinfo [COMMAND] [OPTIONS] 143 | 144 | Supported commands: 145 | --query query authinfo file for matching entries 146 | --user match user name 147 | --host match host name 148 | --protocol match protocol 149 | --path use this authinfo file instead of autodiscovered 150 | --validate check authinfo file for syntax errors 151 | --path use this authinfo file instead of autodiscovered 152 | --version print version info 153 | --help print this help 154 | #+END_EXAMPLE 155 | 156 | 157 | The CLI tool can work in syntax checking and querying mode. 158 | 159 | *** Syntax checking 160 | 161 | In syntax checking mode only the syntax of password file is checked and in 162 | case there exist any errors they are reported: 163 | 164 | #+BEGIN_EXAMPLE 165 | $ echo "hostt hostname password password" > /tmp/authinfo 166 | $ authinfo --path /tmp/authinfo --validate 167 | Parsing /tmp/authinfo. 168 | 1:0: Unknown keyword used 169 | 1:5: Unknown keyword used 170 | 1:0: Host not specified 171 | $ echo "host hostname password password" > /tmp/authinfo 172 | $ authinfo --path /tmp/authinfo --validate 173 | Parsing /tmp/authinfo. 174 | No errors found 175 | #+END_EXAMPLE 176 | 177 | *** Querying 178 | 179 | In querying mode the first entry that matches user input is returned. This 180 | mode is desgined to be used in conjunction with shell =eval= function. It 181 | will set several environment variables to the corresponding values from the 182 | matching entry: 183 | 184 | #+BEGIN_EXAMPLE 185 | $ echo "host hostname user user password password protocol 80" > /tmp/authinfo 186 | $ echo "default password default-password" >> /tmp/authinfo 187 | $ eval $(authinfo --path /tmp/authinfo --query --host hostname --user user) 188 | $ env | grep AUTHINFO_ 189 | AUTHINFO_PROTOCOL=80 190 | AUTHINFO_USER=user 191 | AUTHINFO_PASSWORD=password 192 | AUTHINFO_HOST=hostname 193 | $ eval $(authinfo --path /tmp/authinfo --query --host other-host --user user) 194 | AUTHINFO_PROTOCOL= 195 | AUTHINFO_USER= 196 | AUTHINFO_PASSWORD=default-password 197 | AUTHINFO_HOST= 198 | #+END_EXAMPLE 199 | 200 | In case password file contains syntax errors, authinfo reports to the 201 | standard error the first encountered error and exits with non-zero exit 202 | code. 203 | 204 | #+BEGIN_EXAMPLE 205 | $ echo "hostt hostname password password" > /tmp/authinfo 206 | $ vars=$(authinfo --path /tmp/authinfo --query hostname) 207 | authinfo: parse error at /tmp/authinfo:1:0 (Unknown keyword used) 208 | $ echo $? 209 | 1 210 | #+END_EXAMPLE 211 | 212 | ** Library 213 | *** C 214 | 215 | authinfo can be used as a library. API is briefly documented using 216 | Doxygen. As an example of using the API one can refer to [[src/cli.c][authinfo cli]]. 217 | 218 | *** Python 219 | 220 | Authinfo provides Python bindings for a subset of functionality. Refer to 221 | [[python/authinfo.py][the source]] for details. [[docs/offlineimap.py][This script]] can also be used as an example. 222 | 223 | ** OfflineImap 224 | 225 | Authinfo can be used for storing passwords for [[http://offlineimap.org/][OfflineImap]]. Just copy 226 | [[docs/offlineimap.py]] to =~/.offlineimap.py=. And then use it from your 227 | =.offlineimaprc=: 228 | 229 | #+BEGIN_EXAMPLE 230 | [general] 231 | accounts = Gmail 232 | maxsyncaccounts = 3 233 | pythonfile = ~/.offlineimap.py 234 | 235 | [Account Gmail] 236 | localrepository = Local 237 | remoterepository = Remote 238 | 239 | [Repository Local] 240 | type = Maildir 241 | localfolders = ~/mail/aliaksiej.artamonau@gmail.com 242 | 243 | [Repository Remote] 244 | type = IMAP 245 | remotehost = imap.gmail.com 246 | remoteuser = aliaksiej.artamonau@gmail.com 247 | remotepasseval = get_password("imap.gmail.com", "aliaksiej.artamonau@gmail.com") 248 | ssl = yes 249 | maxconnections = 1 250 | realdelete = no 251 | cert_fingerprint = b0ba392bba326e6feb1add4d04fa0fb86cd173fa 252 | #+END_EXAMPLE 253 | 254 | 255 | ** Pidgin 256 | 257 | Authinfo can also be used to keep passwords for libpurple based IM clients 258 | like [[http://pidgin.im/][Pidgin]]. Please refer to [[https://github.com/aartamonau/pidgin-authinfo][pidgin-authinfo]] page for details. 259 | -------------------------------------------------------------------------------- /authinfo.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: @PACKAGE_NAME@ 7 | Description: Library for parsing authinfo files 8 | Requires: 9 | Version: @PACKAGE_VERSION@ 10 | Libs: -L${libdir} -lauthinfo @GPGME_PTHREAD_LIBS@ 11 | Cflags: -I${includedir}/authinfo 12 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "`dirname $0`" 4 | autoreconf -i --force 5 | -------------------------------------------------------------------------------- /config.site/coverage: -------------------------------------------------------------------------------- 1 | CFLAGS="-O0 --coverage -fno-inline" 2 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.69]) 2 | 3 | AC_INIT([authinfo], [0.1], [https://github.com/aartamonau/authinfo/issues]) 4 | AM_INIT_AUTOMAKE([subdir-objects foreign]) 5 | LT_INIT([static]) 6 | 7 | AC_CONFIG_MACRO_DIR([m4]) 8 | AC_CONFIG_SRCDIR([src/cli.c]) 9 | AC_CONFIG_FILES([Makefile docs/Makefile docs/Doxyfile authinfo.pc] 10 | [dist/archlinux/PKGBUILD]) 11 | AC_CONFIG_HEADERS([config.h]) 12 | 13 | AC_PROG_CC_C99 14 | 15 | if test "x${ac_cv_prog_cc_c99}" = xno; then 16 | AC_MSG_ERROR([A compiler supporting c99 is required]) 17 | fi 18 | 19 | AC_CHECK_DECL([__GNUC__], [gcc=yes], [gcc=no]) 20 | AM_CONDITIONAL([GCC], [test x$gcc = xyes]) 21 | 22 | AC_DEFINE_DIR([SYSCONF_DIR], [sysconfdir], 23 | [System configuration directory (typically /etc).]) 24 | 25 | AM_PROG_CC_C_O 26 | 27 | PKG_CHECK_MODULES([CHECK], [check >= 0.9.0], [have_check=yes], [have_check=no]) 28 | AM_CONDITIONAL([HAVE_CHECK], [test x$have_check = xyes]) 29 | 30 | AC_ARG_ENABLE([debug], 31 | [AS_HELP_STRING([--enable-debug], 32 | [enable debug build @<:@default=no@:>@])], 33 | [enable_debug="$enableval"], 34 | [enable_debug="no"]) 35 | AM_CONDITIONAL([DEBUG], [test x$enable_debug = xyes]) 36 | 37 | AC_ARG_ENABLE([sanitizer], 38 | [AS_HELP_STRING([--enable-sanitizer], 39 | [enable address and undefined behavior sanitizers @<:@default=no@:>@])], 40 | [enable_sanitizer="$enableval"], 41 | [enable_sanitizer="no"]) 42 | AM_CONDITIONAL([SANITIZER], [test x$enable_sanitizer = xyes]) 43 | 44 | AC_CHECK_PROG([have_lcov], [lcov], [yes]) 45 | AM_CONDITIONAL([HAVE_LCOV], [test x$have_lcov = xyes]) 46 | 47 | AC_CHECK_PROG([have_xdg_open], [xdg-open], [yes]) 48 | AM_CONDITIONAL([HAVE_XDG_OPEN], [test x$have_xdg_open = xyes]) 49 | 50 | AC_CHECK_PROG([have_doxygen], [doxygen], [yes]) 51 | AM_CONDITIONAL([HAVE_DOXYGEN], [test x$have_doxygen = xyes]) 52 | 53 | if test x$have_doxygen != xyes; then 54 | AC_MSG_WARN( 55 | [Doxygen not found. API documentation won't be generated]) 56 | fi 57 | 58 | dnl TODO: refine version restriction 59 | AM_PATH_GPGME_PTHREAD([1.4.0], [have_gpgme=yes], [have_gpgme=no]) 60 | 61 | if test x$have_gpgme = xno; then 62 | AC_MSG_ERROR([Couldn't find GPGME.]) 63 | fi 64 | 65 | dnl TODO: version 66 | AM_PATH_LIBASSUAN([2.1.0], [have_libassuan=yes], [have_libassuan=no]) 67 | 68 | if test x$have_libassuan = xno; then 69 | AC_MSG_ERROR([Couldn't find libassuan.]) 70 | fi 71 | 72 | AC_ARG_ENABLE([cli], 73 | [AS_HELP_STRING([--enable-cli], 74 | [build cli tool @<:@default=yes@:>@])], 75 | [enable_cli="$enableval"], 76 | [enable_cli="yes"]) 77 | 78 | AM_CONDITIONAL([BUILD_CLI], [test x$enable_cli = xyes]) 79 | 80 | if test x$enable_cli = xyes; then 81 | AC_CHECK_FUNCS([getopt_long]) 82 | fi 83 | 84 | AC_ARG_ENABLE([python], 85 | [AS_HELP_STRING([--enable-python], 86 | [install python bindings @<:@default=yes@:>@])], 87 | [enable_python="$enableval"], 88 | [enable_python="yes"]) 89 | 90 | if test x$enable_python = xyes; then 91 | if test x$enable_shared != xyes; then 92 | AC_MSG_ERROR("--enable-python requires --enable-shared") 93 | fi 94 | 95 | AM_PATH_PYTHON([2.5]) 96 | AC_CONFIG_FILES([python/__init__.py]) 97 | fi 98 | 99 | AM_CONDITIONAL([ENABLE_PYTHON], [test x$enable_python = xyes]) 100 | 101 | LIBAUTHINFO_INTERFACE=0 102 | LIBAUTHINFO_REVISION=0 103 | LIBAUTHINFO_AGE=0 104 | LIBAUTHINFO_VERSION_INFO=${LIBAUTHINFO_INTERFACE}:${LIBAUTHINFO_REVISION}:${LIBAUTHINFO_AGE} 105 | 106 | AC_SUBST([LIBAUTHINFO_INTERFACE]) 107 | AC_SUBST([LIBAUTHINFO_REVISION]) 108 | AC_SUBST([LIBAUTHINFO_AGE]) 109 | AC_SUBST([LIBAUTHINFO_VERSION_INFO]) 110 | 111 | AC_OUTPUT 112 | -------------------------------------------------------------------------------- /dist/archlinux/.gitignore: -------------------------------------------------------------------------------- 1 | /PKGBUILD 2 | /authinfo-* 3 | /pkg 4 | /src 5 | -------------------------------------------------------------------------------- /dist/archlinux/PKGBUILD.in: -------------------------------------------------------------------------------- 1 | # -*- mode: pkgbuild; -*- 2 | # Maintainer: Aliaksey Artamonau 3 | 4 | pkgname=@PACKAGE_NAME@ 5 | pkgver=@PACKAGE_VERSION@ 6 | pkgrel=1 7 | pkgdesc="Library for parsing authinfo files" 8 | url="https://github.com/aartamonau/authinfo" 9 | arch=('i686' 'x86_64') 10 | license=('LGPL3') 11 | depends=('gpgme' 'python2') 12 | makedepends=('git') 13 | 14 | _gitroot=git@github.com:aartamonau/authinfo.git 15 | _gitname=authinfo 16 | 17 | build() { 18 | cd "$srcdir" 19 | msg "Connecting to GIT server...." 20 | 21 | if [[ -d "$_gitname" ]]; then 22 | cd "$_gitname" && git pull origin 23 | msg "The local files are updated." 24 | else 25 | git clone "$_gitroot" "$_gitname" 26 | fi 27 | 28 | msg "GIT checkout done or server timeout" 29 | msg "Starting build..." 30 | 31 | rm -rf "$srcdir/$_gitname-build" 32 | git clone "$srcdir/$_gitname" "$srcdir/$_gitname-build" 33 | cd "$srcdir/$_gitname-build" 34 | 35 | # 36 | # BUILD HERE 37 | # 38 | ./bootstrap 39 | ./configure --prefix=/usr --sysconfdir=/etc 40 | make 41 | } 42 | 43 | package() { 44 | cd "$srcdir/$_gitname-build" 45 | make DESTDIR="$pkgdir/" install 46 | } 47 | -------------------------------------------------------------------------------- /docs/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_doc_DATA = $(top_srcdir)/docs/offlineimap.py 2 | 3 | if HAVE_DOXYGEN 4 | htmldocdir = $(docdir)/html 5 | 6 | doxygen/html/index.html: Doxyfile $(top_srcdir)/include/ 7 | doxygen $< 8 | 9 | all-local: doxygen/html/index.html 10 | 11 | install-html: 12 | $(INSTALL) -d $(DESTDIR)$(htmldocdir) 13 | cd doxygen/html && \ 14 | find . -type f -exec $(INSTALL_DATA) -D {} $(DESTDIR)$(htmldocdir)/{} \; 15 | 16 | install-data-local: install-html 17 | 18 | uninstall-local: 19 | rm -rf $(DESTDIR)$(htmldocdir) 20 | 21 | clean-local: 22 | rm -rf doxygen/ 23 | 24 | endif 25 | -------------------------------------------------------------------------------- /docs/offlineimap.py: -------------------------------------------------------------------------------- 1 | # This is a script that can be used in conjunction with offlineimap. 2 | # 3 | # Your remote repository in .offlineimaprc should look similar to this: 4 | # 5 | # [Repository Remote] 6 | # type = IMAP 7 | # remotehost = imap.gmail.com 8 | # remoteuser = user@gmail.com 9 | # remotepasseval = get_password("imap.gmail.com", "user@gmail.com") 10 | # ssl = yes 11 | # 12 | # And don't forget to set 'pythonfile' in the 'general' section. 13 | 14 | import authinfo 15 | 16 | def get_password(host, user): 17 | entry = authinfo.query(host=host, user=user) 18 | 19 | if entry is None: 20 | return None 21 | 22 | return entry.password 23 | -------------------------------------------------------------------------------- /include/authinfo.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2013 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | /** 23 | * @file authinfo.h 24 | * @author Aliaksey Artamonau 25 | * 26 | * @brief libauthinfo public interface 27 | * 28 | * 29 | */ 30 | 31 | #ifndef _AUTHINFO_H_ 32 | #define _AUTHINFO_H_ 33 | 34 | #include 35 | 36 | #ifdef __GNUC__ 37 | #define EXPORT_FUNCTION __attribute__((visibility("default"))) 38 | #else 39 | #define EXPORT_FUNCTION 40 | #endif 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif /* __cplusplus */ 45 | 46 | /// Indicates if certain call completed successfully 47 | enum authinfo_result_t { 48 | AUTHINFO_OK, /**< Everything went fine. */ 49 | AUTHINFO_EACCESS, /**< Couldn't access some path. */ 50 | AUTHINFO_ENOENT, /**< Path does not exist. */ 51 | AUTHINFO_ENOMEM, /**< Not enough memory. */ 52 | AUTHINFO_EUNKNOWN, /**< Some unexpected condition happened. */ 53 | AUTHINFO_EGPG, /**< Generic GPGME error. */ 54 | AUTHINFO_EGPG_DECRYPT_FAILED, /**< Decryption failed. */ 55 | AUTHINFO_EGPG_BAD_PASSPHRASE, /**< Invalid passphrase supplied. */ 56 | AUTHINFO_EGPG_BAD_BASE64, /**< Malformed base64-encoded password. */ 57 | AUTHINFO_EGPG_CANCELED, /**< Operation canceled. */ 58 | AUTHINFO_ENOMATCH, /**< No matching entry was found. */ 59 | AUTHINFO_EPARSE, /**< Parse error. */ 60 | AUTHINFO_ENOLOOPBACK, /**< Internal. allow-loopback-pinentry is 61 | * false */ 62 | AUTHINFO_RESULT_MAX 63 | }; 64 | 65 | /** 66 | * Initialize authinfo library. Should be called before any other function in 67 | * the library. And if your program is multithreaded, it must be called in a 68 | * single thread. 69 | * 70 | * Among other things this function will query current locale to set up GPG 71 | * accordingly. So you might want to call setlocale to set it to the desired 72 | * value. 73 | * 74 | * @param name Program name to show in the pinentry prompt. 75 | * 76 | * @retval AUTHINFO_OK 77 | * @retval AUTHINFO_ENOMEM 78 | * @retval AUTHINFO_EUNKNOWN 79 | * @retval AUTHINFO_EGPG 80 | */ 81 | EXPORT_FUNCTION enum authinfo_result_t authinfo_init(const char *name); 82 | 83 | /** 84 | * Return a human readable description for a status code. 85 | * 86 | * @param status status to describe 87 | * 88 | * @return description 89 | */ 90 | EXPORT_FUNCTION const char *authinfo_strerror(enum authinfo_result_t status); 91 | 92 | /** 93 | * Find an authinfo file to use. 94 | * 95 | * @param[out] path Return authinfo file path here. Must be freed by the caller. 96 | * 97 | * @retval AUTHINFO_OK 98 | * @retval AUTHINFO_ENOMEM 99 | * @retval AUTHINFO_ENOENT 100 | * @retval AUTHINFO_EACCESS 101 | * @retval AUTHINFO_EUNKNOWN 102 | */ 103 | EXPORT_FUNCTION enum authinfo_result_t authinfo_find_file(char **path); 104 | 105 | struct authinfo_data_t; 106 | 107 | /** 108 | * Create #authinfo_data_t from user buffer. 109 | * 110 | * @param buffer buffer 111 | * @param size size of the buffer 112 | * @param[out] data resulting data object 113 | * 114 | * @retval AUTHINFO_OK 115 | * @retval AUTHINFO_ENOMEM 116 | */ 117 | EXPORT_FUNCTION enum authinfo_result_t 118 | authinfo_data_from_mem(const char *buffer, size_t size, 119 | struct authinfo_data_t **data); 120 | 121 | /** 122 | * Create #authinfo_data_t object from file contents. If the file has .gpg 123 | * extension, it's considered to be gpg-encrypted and it's automatically 124 | * decrypted. 125 | * 126 | * @param path file path 127 | * @param[out] data resulting data object 128 | * 129 | * @retval AUTHINFO_OK 130 | * @retval AUTHINFO_ENOENT 131 | * @retval AUTHINFO_EACCESS 132 | * @retval AUTHINFO_EUNKNOWN 133 | * @retval AUTHINFO_EGPG 134 | * @retval AUTHINFO_EGPG_DECRYPT_FAILED 135 | * @retval AUTHINFO_EGPG_BAD_PASSPHRASE 136 | */ 137 | EXPORT_FUNCTION enum authinfo_result_t 138 | authinfo_data_from_file(const char *path, struct authinfo_data_t **data); 139 | 140 | /** 141 | * Get a size and a pointer to a memory associated with data object. Note that 142 | * this returns a pointer to the actual underlying memory used by the data 143 | * object. So its life-time is the same as a life-time of the data object. 144 | * 145 | * @param data data object 146 | * @param[out] mem pointer to memory region 147 | * @param[out] size size 148 | */ 149 | EXPORT_FUNCTION void 150 | authinfo_data_get_mem(const struct authinfo_data_t *data, 151 | const char **mem, size_t *size); 152 | 153 | /** 154 | * Free a data object. 155 | * 156 | * @param data data object to free 157 | */ 158 | EXPORT_FUNCTION void 159 | authinfo_data_free(struct authinfo_data_t *data); 160 | 161 | /// Opaque representation of a password. 162 | struct authinfo_password_t; 163 | 164 | /// Represents an entry in authinfo file. 165 | struct authinfo_parse_entry_t { 166 | const char *host; /**< Host. NULL for the "default" entry of if 167 | * omitted. */ 168 | const char *protocol; /**< Protocol. NULL if omitted. */ 169 | const char *user; /**< User. NULL if omitted. */ 170 | struct authinfo_password_t *password; /**< Password. NULL if omitted. */ 171 | bool force; /**< Force. 'false' by default. */ 172 | }; 173 | 174 | /// A callback that is called for every entry in the file. The callback takes 175 | /// a parsed entry and an arbitrary argument passed to authinfo_parse(). The 176 | /// callback should return a boolean value that indicates if parsing should be 177 | /// stopped or continued. 178 | typedef bool 179 | (*authinfo_parse_entry_cb_t)(const struct authinfo_parse_entry_t *, void *); 180 | 181 | /// Possible parsing error. 182 | enum authinfo_parse_error_type_t { 183 | AUTHINFO_PET_NO_ERROR, /**< No error. This is never returned to the 184 | * user. */ 185 | AUTHINFO_PET_MISSING_VALUE, /**< Expected a value but got nothing. */ 186 | AUTHINFO_PET_VALUE_TOO_LONG, /**< Value is too long to be handled. */ 187 | AUTHINFO_PET_BAD_VALUE, /**< Invalid value. */ 188 | AUTHINFO_PET_BAD_KEYWORD, /**< Encountered unknown keyword. */ 189 | AUTHINFO_PET_DUPLICATED_KEYWORD, /**< Encountered duplicate or synonymous 190 | * keyword */ 191 | AUTHINFO_PET_UNTERMINATED_QUOTED_TOKEN, /**< Quoted token ends 192 | * unexpectedly. */ 193 | AUTHINFO_PET_UNSUPPORTED_ESCAPE, /**< Unsupported escape sequence found. */ 194 | AUTHINFO_PET_MAX 195 | }; 196 | 197 | /** 198 | * Convert parse error to a human readable description. 199 | * 200 | * @param error parse error to describe 201 | * 202 | * @return description 203 | */ 204 | EXPORT_FUNCTION const char * 205 | authinfo_parse_strerror(enum authinfo_parse_error_type_t error); 206 | 207 | /// A structure representing parsing error. 208 | struct authinfo_parse_error_t { 209 | unsigned int line; /**< Line number. */ 210 | unsigned int column; /**< Column number. */ 211 | enum authinfo_parse_error_type_t type; /**< Error type. */ 212 | }; 213 | 214 | /// A callback that is called for every parsing error. The callback takes 215 | /// error type, line, column and arbitrary argument passed to 216 | /// authinfo_parse(). The callback should return a boolean value that 217 | /// indicates if parsing should be stopped or continued. 218 | typedef bool 219 | (*authinfo_parse_error_cb_t)(const struct authinfo_parse_error_t *, void *); 220 | 221 | /** 222 | * Parse authinfo file. 223 | * 224 | * @param data data object to parse 225 | * @param arg arbitrary argument that will be passed to callbacks 226 | * @param entry_callback a callback to be called for every parsed entry 227 | * @param error_callback a callback to be called for every parsing error 228 | */ 229 | EXPORT_FUNCTION void authinfo_parse(const struct authinfo_data_t *data, 230 | void *arg, 231 | authinfo_parse_entry_cb_t entry_callback, 232 | authinfo_parse_error_cb_t error_callback); 233 | 234 | /** 235 | * Extracts a password from #authinfo_password_t structure. Extracted password 236 | * is returned in @em data. Note that returned value is valid only during 237 | * lifetime of the containing #authinfo_parse_entry_t structure. It should be 238 | * copied, if longer lifetime is required. 239 | * 240 | * @param password #authinfo_password_t structure to extract password from 241 | * @param[out] data extracted password is returned here 242 | * 243 | * @retval AUTHINFO_OK 244 | * @retval AUTHINFO_EGPG 245 | * @retval AUTHINFO_EGPG_BAD_BASE64 246 | * @retval AUTHINFO_EGPG_DECRYPT_FAILED 247 | * @retval AUTHINFO_EGPG_BAD_PASSPHRASE 248 | */ 249 | EXPORT_FUNCTION enum authinfo_result_t 250 | authinfo_password_extract(struct authinfo_password_t *password, 251 | const char **data); 252 | 253 | /** 254 | * Release resources held by the #authinfo_parse_entry_t structure returned by 255 | * authinfo_simple_query() function. Note this function must not be 256 | * called on the entries that are passed to the entry callback by 257 | * authinfo_parse() function. 258 | * 259 | * @param entry an entry to free 260 | */ 261 | EXPORT_FUNCTION void 262 | authinfo_parse_entry_free(struct authinfo_parse_entry_t *entry); 263 | 264 | /** 265 | * A shortcut function that looks for the first matching entry in the authinfo 266 | * file. 267 | * 268 | * @param data data object to parse 269 | * @param host host name to match; NULL matches any host name 270 | * @param protocol protocol to match; NULL matches everything 271 | * @param user user to match; NULL matches everything 272 | * @param[out] entry parsed entry returned here in case of success; after it's 273 | * not needed anymore it should be freed by 274 | * authinfo_parse_entry_free() function 275 | * @param[out] error parse error returned here if data could not be parsed; if 276 | * NULL, then ignored 277 | * 278 | * @retval AUTHINFO_OK matching entry found 279 | * @retval AUTHINFO_ENOMATCH no matching entry found 280 | * @retval AUTHINFO_EPARSE data could not be parsed 281 | * @retval AUTHINFO_ENOMEM could not allocate memory 282 | */ 283 | EXPORT_FUNCTION enum authinfo_result_t 284 | authinfo_simple_query(const struct authinfo_data_t *data, 285 | const char *host, const char *protocol, const char *user, 286 | struct authinfo_parse_entry_t *entry, 287 | struct authinfo_parse_error_t *error); 288 | 289 | #ifdef __cplusplus 290 | } 291 | #endif 292 | 293 | #endif /* _AUTHINFO_H_ */ 294 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | /libtool.m4 2 | /ltoptions.m4 3 | /ltsugar.m4 4 | /ltversion.m4 5 | /lt~obsolete.m4 6 | -------------------------------------------------------------------------------- /m4/ac_define_dir.m4: -------------------------------------------------------------------------------- 1 | dnl Available from the GNU Autoconf Macro Archive at: 2 | dnl http://www.gnu.org/software/ac-archive/htmldoc/ac_define_dir.html 3 | dnl 4 | AC_DEFUN([AC_DEFINE_DIR], [ 5 | test "x$prefix" = xNONE && prefix="$ac_default_prefix" 6 | test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' 7 | ac_define_dir=`eval echo [$]$2` 8 | ac_define_dir=`eval echo [$]ac_define_dir` 9 | $1="$ac_define_dir" 10 | AC_SUBST($1) 11 | ifelse($3, , 12 | AC_DEFINE_UNQUOTED($1, "$ac_define_dir"), 13 | AC_DEFINE_UNQUOTED($1, "$ac_define_dir", $3)) 14 | ]) 15 | -------------------------------------------------------------------------------- /python/__init__.py.in: -------------------------------------------------------------------------------- 1 | from ctypes import cdll 2 | from authinfo import * 3 | 4 | __all__ = ['__version__', 5 | 'AuthinfoError', 'AuthinfoParseError', 'AuthinfoEntry', 'query'] 6 | 7 | __version__ = '@PACKAGE_VERSION@' 8 | libauthinfo_interface = @LIBAUTHINFO_INTERFACE@ 9 | -------------------------------------------------------------------------------- /python/authinfo.py: -------------------------------------------------------------------------------- 1 | from ctypes import cdll, Structure, POINTER, cast, byref, create_string_buffer 2 | from ctypes import c_char, c_char_p, c_int, c_uint, c_bool, c_void_p, c_size_t 3 | from ctypes.util import find_library 4 | 5 | from __init__ import libauthinfo_interface 6 | 7 | 8 | __all__ = ['AuthinfoError', 'AuthinfoParseError', 'AuthinfoEntry', 'query'] 9 | 10 | class authinfo_parse_entry_t(Structure): 11 | _fields_ = [('host', POINTER(c_char)), 12 | ('protocol', POINTER(c_char)), 13 | ('user', POINTER(c_char)), 14 | ('password', c_void_p), 15 | ('force', c_bool)] 16 | 17 | class authinfo_parse_error_t(Structure): 18 | _fields_ = [('line', c_uint), 19 | ('column', c_uint), 20 | ('type', c_int)] 21 | 22 | libc = cdll.LoadLibrary(find_library('c')) 23 | libauthinfo = cdll.LoadLibrary('libauthinfo.so.%d' % libauthinfo_interface) 24 | 25 | free = libc.free 26 | free.restype = None 27 | free.argtypes = [c_void_p] 28 | 29 | authinfo_init = libauthinfo.authinfo_init 30 | authinfo_init.restype = c_int 31 | authinfo_init.argtypes = [c_char_p] 32 | 33 | authinfo_strerror = libauthinfo.authinfo_strerror 34 | authinfo_strerror.restype = c_char_p 35 | authinfo_strerror.argtypes = [c_int] 36 | 37 | authinfo_parse_strerror = libauthinfo.authinfo_parse_strerror 38 | authinfo_parse_strerror.restype = c_char_p 39 | authinfo_parse_strerror.argtypes = [c_int] 40 | 41 | authinfo_find_file = libauthinfo.authinfo_find_file 42 | authinfo_find_file.restype = c_int 43 | authinfo_find_file.argtypes = [POINTER(c_char_p)] 44 | 45 | authinfo_data_from_file = libauthinfo.authinfo_data_from_file 46 | authinfo_data_from_file.restype = c_int 47 | authinfo_data_from_file.argtypes = [c_char_p, POINTER(c_void_p)] 48 | 49 | authinfo_data_free = libauthinfo.authinfo_data_free 50 | authinfo_data_free.restype = None 51 | authinfo_data_free.argtypes = [c_void_p] 52 | 53 | authinfo_simple_query = libauthinfo.authinfo_simple_query 54 | authinfo_simple_query.restype = c_int 55 | authinfo_simple_query.argtypes = [c_void_p, 56 | c_char_p, c_char_p, c_char_p, 57 | POINTER(authinfo_parse_entry_t), 58 | POINTER(authinfo_parse_error_t)] 59 | 60 | authinfo_parse_entry_free = libauthinfo.authinfo_parse_entry_free 61 | authinfo_parse_entry_free.restype = None 62 | authinfo_parse_entry_free.argtypes = [POINTER(authinfo_parse_entry_t)] 63 | 64 | authinfo_password_extract = libauthinfo.authinfo_password_extract 65 | authinfo_password_extract.restype = c_int 66 | authinfo_password_extract.argtypes = [c_void_p, POINTER(c_char_p)] 67 | 68 | 69 | class AuthinfoError(Exception): 70 | ''' 71 | Represents various authinfo errors. 72 | 73 | Error type is stored in `AuthinfoError.type`. Human-readable error message 74 | is stored in `AuthinfoError.message`. 75 | 76 | Error Types 77 | ----------- 78 | 79 | `AuthinfoError.AUTHINFO_OK` 80 | No error occurred 81 | 82 | `AuthinfoError.AUTHINFO_EACCESS` 83 | Authinfo file was inaccessible 84 | 85 | `AuthinfoError.AUTHINFO_ENOENT` 86 | Authinfo file could not be found 87 | 88 | `AuthinfoError.AUTHINFO_ENOMEM` 89 | Memory could not be allocated 90 | 91 | `AuthinfoError.AUTHINFO_EUNKNOWN` 92 | Unknown error occurred 93 | 94 | `AuthinfoError.AUTHINFO_EGPG` 95 | Unexpected GPG error 96 | 97 | `AuthinfoError.AUTHINFO_EGPG_DECRYPT_FAILED` 98 | Failed to decrypt authinfo file 99 | 100 | `AuthinfoError.AUTHINFO_EGPG_BAD_PASSPHRASE` 101 | Bad passphrase supplied by user 102 | 103 | `AuthinfoError.AUTHINFO_EGPG_BAD_BASE64` 104 | Malformed base64-encode password 105 | 106 | `AuthinfoError.AUTHINFO_ENOMATCH` 107 | No matching entries found 108 | 109 | `AuthinfoError.AUTHINFO_EPARSE` 110 | Failed to parse authinfo file 111 | ''' 112 | 113 | __slots__ = ['type', 'msg'] 114 | 115 | AUTHINFO_OK = 0 116 | AUTHINFO_EACCESS = 1 117 | AUTHINFO_ENOENT = 2 118 | AUTHINFO_ENOMEM = 3 119 | AUTHINFO_EUNKNOWN = 4 120 | AUTHINFO_EGPG = 5 121 | AUTHINFO_EGPG_DECRYPT_FAILED = 6 122 | AUTHINFO_EGPG_BAD_PASSPHRASE = 7 123 | AUTHINFO_EGPG_BAD_BASE64 = 8 124 | AUTHINFO_ENOMATCH = 9 125 | AUTHINFO_EPARSE = 10 126 | 127 | def __init__(self, type): 128 | super(AuthinfoError, self).__init__() 129 | 130 | self.type = type 131 | self.message = authinfo_strerror(type) 132 | 133 | def __str__(self): 134 | return self.message 135 | 136 | 137 | class AuthinfoParseError(Exception): 138 | ''' 139 | Represents parsing errors. 140 | 141 | Error type is stored in `AuthinfoParseError.type`. Human-readable error 142 | description is stored in `AuthinfoParseError.message`. Line and column 143 | where the error occurred are stored in `AuthinfoParseError.line` and 144 | `AuthinfoParseError.column` respectively. 145 | 146 | Error types 147 | ----------- 148 | 149 | `AuthinfoParseError.AUTHINFO_PET_NO_ERROR` 150 | No error 151 | 152 | `AuthinfoParseError.AUTHINFO_PET_MISSING_VALUE` 153 | Value was not specified for an attribute 154 | 155 | `AuthinfoParseError.AUTHINFO_PET_VALUE_TOO_LONG` 156 | Token exceeds maximum supported size 157 | 158 | `AuthinfoParseError.AUTHINFO_PET_BAD_VALUE` 159 | Invalid value provided for an attribute 160 | 161 | `AuthinfoParseError.AUTHINFO_PET_BAD_KEYWORD` 162 | Unrecognized keyword used 163 | 164 | `AuthinfoParseError.AUTHINFO_PET_DUPLICATED_KEYWORD` 165 | Duplicate or synonymous attribute 166 | 167 | `AuthinfoParseError.AUTHINFO_PET_UNTERMINATED_QUOTED_TOKEN` 168 | No matching closing double quote 169 | 170 | `AuthinfoParseError.AUTHINFO_PET_UNSUPPORTED_ESCAPE` 171 | Unsupported escape sequence used 172 | ''' 173 | 174 | __slots__ = ['type', 'line', 'column', 'msg'] 175 | 176 | AUTHINFO_PET_NO_ERROR = 0 177 | '''No error''' 178 | 179 | AUTHINFO_PET_MISSING_VALUE = 1 180 | '''Value was not specified for an attribute''' 181 | 182 | AUTHINFO_PET_VALUE_TOO_LONG = 2 183 | '''Token exceeds maximum supported size''' 184 | 185 | AUTHINFO_PET_BAD_VALUE = 3 186 | '''Invalid value provided for an attribute''' 187 | 188 | AUTHINFO_PET_BAD_KEYWORD = 4 189 | '''Unrecognized keyword used''' 190 | 191 | AUTHINFO_PET_DUPLICATED_KEYWORD = 5 192 | '''Duplicate or synonymous attribute''' 193 | 194 | AUTHINFO_PET_UNTERMINATED_QUOTED_TOKEN = 6 195 | '''No matching closing double quote''' 196 | 197 | AUTHINFO_PET_UNSUPPORTED_ESCAPE = 7 198 | '''Unsupported escape sequence used''' 199 | 200 | def __init__(self, c_error): 201 | super(AuthinfoParseError, self).__init__() 202 | 203 | self.type = c_error.type 204 | self.line = c_error.line 205 | self.column = c_error.column 206 | self.message = authinfo_parse_strerror(c_error.type) 207 | 208 | def __str__(self): 209 | return '%s (line %d, column %d)' % (self.message, self.line, self.column) 210 | 211 | 212 | class AuthinfoEntry(object): 213 | ''' 214 | Represents a single entry in authinfo file. 215 | 216 | `AuthinfoEntry.host`, `Authinfo.protocol`, `Authinfo.user`, 217 | `Authinfo.password` and `Authinfo.force` contain corresponding attributes 218 | of the entry. If some of the attributes were omitted, then the value will 219 | be `None` (except for `Authinfo.force` which defaults to `False`). 220 | 221 | ''' 222 | 223 | __slots__ = ['host', 'protocol', 'user', 'password', 'force'] 224 | 225 | def __init__(self, c_entry): 226 | super(AuthinfoEntry, self).__init__() 227 | 228 | self.host = None 229 | self.protocol = None 230 | self.user = None 231 | self.password = None 232 | 233 | if c_entry.host: 234 | self.host = cast(c_entry.host, c_char_p).value 235 | 236 | if c_entry.protocol: 237 | self.protocol = cast(c_entry.protocol, c_char_p).value 238 | 239 | if c_entry.user: 240 | self.user = cast(c_entry.user, c_char_p).value 241 | 242 | if c_entry.password: 243 | c_password = c_char_p() 244 | _handle_authinfo_result( 245 | authinfo_password_extract(c_entry.password, byref(c_password))) 246 | self.password = c_password.value 247 | 248 | self.force = c_entry.force 249 | 250 | def __str__(self): 251 | return '<%s host=%s protocol=%s user=%s password=%s force=%s>' % \ 252 | (type(self).__name__, 253 | self.host, self.protocol, self.user, self.password, self.force) 254 | 255 | def is_default(self): 256 | ''' 257 | Returns `True` if the entry is the "default" entry. I.e. "default" 258 | keyword was used for the entry instead of specifying concrete host name. 259 | ''' 260 | 261 | return self.host is None 262 | 263 | 264 | def _handle_authinfo_result(ret): 265 | if ret == AuthinfoError.AUTHINFO_OK: 266 | return 267 | 268 | raise AuthinfoError(ret) 269 | 270 | 271 | class AuthinfoData(object): 272 | __slots__ = ['_data'] 273 | 274 | def __init__(self, path): 275 | self._data = None 276 | 277 | if path is None: 278 | c_path = c_char_p() 279 | _handle_authinfo_result(authinfo_find_file(byref(c_path))) 280 | else: 281 | c_path = c_char_p(path) 282 | 283 | try: 284 | data = c_void_p() 285 | _handle_authinfo_result(authinfo_data_from_file(c_path, byref(data))) 286 | self._data = data 287 | finally: 288 | if path is None: 289 | free(c_path) 290 | 291 | def __del__(self): 292 | if self._data is not None: 293 | authinfo_data_free(self._data) 294 | 295 | def get_data(self): 296 | return self._data 297 | 298 | 299 | def init(name=None): 300 | ''' 301 | Initialize libauthinfo. `name` is a program name shown in a pinentry 302 | prompt. If None, sys.argv[0] is used. 303 | 304 | When you import authinfo for the first time, init(None) is called 305 | implicitly. 306 | ''' 307 | 308 | if name is None: 309 | import sys 310 | name = sys.argv[0] 311 | 312 | c_name = c_char_p(name) 313 | _handle_authinfo_result(authinfo_init(c_name)) 314 | 315 | 316 | def query(host=None, user=None, protocol=None, path=None): 317 | ''' 318 | Find an entry matching `host`, `user` and `protocol`. Any of these can be 319 | omitted. Optional `path` specifies a path to authinfo file that has to be 320 | used. 321 | ''' 322 | 323 | data = AuthinfoData(path) 324 | 325 | c_host = host and c_char_p(host) 326 | c_user = user and c_char_p(user) 327 | c_protocol = protocol and c_char_p(protocol) 328 | c_entry = authinfo_parse_entry_t() 329 | c_error = authinfo_parse_error_t() 330 | 331 | ret = authinfo_simple_query(data.get_data(), 332 | c_host, c_protocol, c_user, 333 | byref(c_entry), byref(c_error)) 334 | if ret == AuthinfoError.AUTHINFO_OK: 335 | pass 336 | elif ret == AuthinfoError.AUTHINFO_EPARSE: 337 | raise AuthinfoParseError(c_error) 338 | elif ret == AuthinfoError.AUTHINFO_ENOMATCH: 339 | return None 340 | else: 341 | _handle_authinfo_result(ret) 342 | 343 | try: 344 | return AuthinfoEntry(c_entry) 345 | finally: 346 | authinfo_parse_entry_free(byref(c_entry)) 347 | 348 | 349 | init() 350 | -------------------------------------------------------------------------------- /src/authinfo.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2013 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | /** 23 | * @file authinfo.c 24 | * @author Aliaksey Artamonau 25 | * 26 | */ 27 | 28 | #include "config.h" 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include "base64.h" 42 | 43 | #include "authinfo.h" 44 | #include "authinfo_data.h" 45 | #include "utils.h" 46 | #include "pinentry.h" 47 | 48 | #define DOT "." 49 | #define GPG_EXT ".gpg" 50 | #define GPG_PREFIX "gpg:" 51 | #define TOKEN_SIZE_MAX 8192 52 | #define LOOPBACK_RETRIES 3 53 | 54 | struct authinfo_password_t { 55 | struct authinfo_data_t *data; 56 | bool encrypted; 57 | }; 58 | 59 | struct authinfo_stream_t { 60 | const char *data; 61 | size_t size; 62 | 63 | unsigned int line; 64 | unsigned int column; 65 | }; 66 | 67 | struct authinfo_simple_query_data_t { 68 | const char *host; 69 | const char *protocol; 70 | const char *user; 71 | 72 | enum authinfo_result_t status; 73 | 74 | struct authinfo_parse_entry_t *entry; 75 | struct authinfo_parse_error_t *error; 76 | }; 77 | 78 | struct authinfo_ctx_t { 79 | char *name; 80 | char *lc_ctype; 81 | char *lc_messages; 82 | }; 83 | 84 | static struct authinfo_ctx_t ctx = {0}; 85 | 86 | struct authinfo_passphrase_cb_ctx_t { 87 | enum authinfo_result_t ret; 88 | struct pinentry_t *pinentry; 89 | }; 90 | 91 | /* internal macros */ 92 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 93 | #define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) 94 | /* internal macros end */ 95 | 96 | /* internal functions prototypes */ 97 | static enum authinfo_result_t authinfo_gpgme_init(void); 98 | 99 | static enum authinfo_result_t 100 | authinfo_gpgme_do_decrypt(gpgme_ctx_t ctx, 101 | gpgme_data_t cipher_text, 102 | gpgme_data_t *plain_text); 103 | 104 | static gpg_error_t 105 | authinfo_gpgme_passphrase_cb(void *data, 106 | const char *uid_hint, const char *info, 107 | int was_bad, int fd); 108 | 109 | static enum authinfo_result_t 110 | authinfo_gpgme_decrypt_loopback(gpgme_ctx_t ctx, 111 | gpgme_data_t cipher_text, 112 | gpgme_data_t *plain_text); 113 | 114 | static enum authinfo_result_t 115 | authinfo_gpgme_decrypt_no_loopback(gpgme_ctx_t ctx, 116 | gpgme_data_t cipher_text, 117 | gpgme_data_t *plain_text); 118 | 119 | static enum authinfo_result_t 120 | authinfo_gpgme_decrypt(const struct authinfo_data_t *cipher_text, 121 | struct authinfo_data_t **plain_text); 122 | 123 | static bool authinfo_is_gpged_file(const char *path); 124 | 125 | static bool authinfo_is_gpged_password(const char *password); 126 | 127 | static enum authinfo_result_t authinfo_errno2result(int errnum); 128 | 129 | static char *authinfo_path_join(const char *dir, const char *name); 130 | 131 | static enum authinfo_result_t authinfo_path_probe(const char *path); 132 | 133 | static enum authinfo_result_t 134 | authinfo_find_files_in_dir(const char *dir, 135 | const char **name, size_t count, char **pathp); 136 | 137 | static enum authinfo_result_t 138 | authinfo_find_file_in_dir(const char *dir, const char *name, char **pathp); 139 | 140 | static enum authinfo_result_t 141 | authinfo_do_read_file(const char *path, struct authinfo_data_t **data); 142 | 143 | static int 144 | authinfo_lookahead(struct authinfo_stream_t *stream); 145 | 146 | static int 147 | authinfo_next_char(struct authinfo_stream_t *stream); 148 | 149 | static void authinfo_skip_spaces(struct authinfo_stream_t *stream); 150 | 151 | static bool authinfo_eol(struct authinfo_stream_t *stream); 152 | 153 | static bool authinfo_eof(struct authinfo_stream_t *stream); 154 | 155 | static void authinfo_skip_line(struct authinfo_stream_t *stream); 156 | 157 | static bool authinfo_skip_comment(struct authinfo_stream_t *stream); 158 | 159 | static bool authinfo_skip_macdef(struct authinfo_stream_t *stream); 160 | 161 | static bool 162 | authinfo_next_token(struct authinfo_stream_t *stream, char *token, 163 | struct authinfo_parse_error_t *error); 164 | 165 | static bool 166 | authinfo_quoted_token(struct authinfo_stream_t *stream, char *token, 167 | struct authinfo_parse_error_t *error); 168 | 169 | static bool authinfo_report_entry(authinfo_parse_entry_cb_t entry_callback, 170 | authinfo_parse_error_cb_t error_callback, 171 | void *arg, 172 | unsigned int line, 173 | const struct authinfo_parse_entry_t *entry); 174 | 175 | static bool 176 | authinfo_report_error(authinfo_parse_error_cb_t error_callback, void *arg, 177 | enum authinfo_parse_error_type_t type, 178 | unsigned int line, unsigned int column); 179 | 180 | static bool 181 | authinfo_simple_query_entry(const struct authinfo_parse_entry_t *entry, 182 | void *arg); 183 | 184 | static bool 185 | authinfo_simple_query_error(const struct authinfo_parse_error_t *error, 186 | void *arg); 187 | 188 | static enum authinfo_result_t 189 | authinfo_b64decode(const struct authinfo_data_t *b64data, 190 | struct authinfo_data_t **data); 191 | 192 | static enum authinfo_result_t 193 | authinfo_null_terminate(const struct authinfo_data_t *input, 194 | struct authinfo_data_t **output); 195 | 196 | static enum authinfo_result_t 197 | authinfo_data_copy(const struct authinfo_data_t *data, 198 | struct authinfo_data_t **copy); 199 | 200 | static void 201 | authinfo_wipe(char *buffer, size_t size); 202 | 203 | static void 204 | authinfo_ctx_free(void); 205 | /* internal functions prototypes end */ 206 | 207 | enum authinfo_result_t 208 | authinfo_init(const char *name) 209 | { 210 | enum authinfo_result_t ret = AUTHINFO_OK; 211 | 212 | authinfo_ctx_free(); 213 | 214 | const char *lc_ctype = setlocale(LC_CTYPE, NULL); 215 | const char *lc_messages = setlocale(LC_MESSAGES, NULL); 216 | 217 | if (lc_ctype == NULL || lc_messages == NULL) { 218 | TRACE("Failed to get locale\n"); 219 | return AUTHINFO_EUNKNOWN; 220 | } 221 | 222 | if (name != NULL) { 223 | ctx.name = strdup(name); 224 | } else { 225 | ctx.name = ""; 226 | } 227 | 228 | ctx.lc_ctype = strdup(lc_ctype); 229 | ctx.lc_messages = strdup(lc_messages); 230 | 231 | if (ctx.name == NULL || ctx.lc_ctype == NULL || ctx.lc_messages == NULL) { 232 | ret = AUTHINFO_ENOMEM; 233 | goto authinfo_init_return; 234 | } 235 | 236 | ret = authinfo_gpgme_init(); 237 | 238 | authinfo_init_return: 239 | if (ret != AUTHINFO_OK) { 240 | authinfo_ctx_free(); 241 | } 242 | return ret; 243 | } 244 | 245 | static const char *authinfo_result2str[] = { 246 | [AUTHINFO_OK] = "Success", 247 | [AUTHINFO_EACCESS] = "Permission denied", 248 | [AUTHINFO_ENOENT] = "File or directory not found", 249 | [AUTHINFO_ENOMEM] = "Could not allocate memory", 250 | [AUTHINFO_EUNKNOWN] = "Unknown error happened", 251 | [AUTHINFO_EGPG] = "Unknown GPG error", 252 | [AUTHINFO_EGPG_DECRYPT_FAILED] = "Decryption failed", 253 | [AUTHINFO_EGPG_BAD_PASSPHRASE] = "Bad passphrase supplied", 254 | [AUTHINFO_EGPG_BAD_BASE64] = "Malformed base64-encoded password", 255 | [AUTHINFO_EGPG_CANCELED] = "Operation canceled", 256 | [AUTHINFO_ENOMATCH] = "No matching entry was found", 257 | [AUTHINFO_EPARSE] = "Parsing error", 258 | [AUTHINFO_ENOLOOPBACK] = "[internal] loopback pinentry disallowed", 259 | }; 260 | 261 | const char * 262 | authinfo_strerror(enum authinfo_result_t status) 263 | { 264 | if (status >= AUTHINFO_RESULT_MAX) { 265 | return "Got unexpected status code"; 266 | } 267 | 268 | return authinfo_result2str[status]; 269 | } 270 | 271 | enum authinfo_result_t 272 | authinfo_find_file(char **path) 273 | { 274 | char *home; 275 | enum authinfo_result_t ret; 276 | 277 | home = getenv("HOME"); 278 | if (home) { 279 | const char *names[] = { DOT "authinfo", 280 | DOT "netrc" }; 281 | 282 | ret = authinfo_find_files_in_dir(home, names, ARRAY_SIZE(names), path); 283 | if (ret != AUTHINFO_ENOENT) { 284 | return ret; 285 | } 286 | } 287 | 288 | const char *names[] = { "authinfo", 289 | "netrc" }; 290 | 291 | return authinfo_find_files_in_dir(SYSCONF_DIR, names, 292 | ARRAY_SIZE(names), path); 293 | } 294 | 295 | enum authinfo_result_t 296 | authinfo_data_from_mem(const char *buffer, size_t size, 297 | struct authinfo_data_t **data) 298 | { 299 | *data = malloc(sizeof(**data)); 300 | if (*data == NULL) { 301 | return AUTHINFO_ENOMEM; 302 | } 303 | 304 | (*data)->type = ALLOCATED; 305 | (*data)->buffer_type = USER; 306 | (*data)->buffer = buffer; 307 | (*data)->size = size; 308 | (*data)->sensitive = false; 309 | 310 | return AUTHINFO_OK; 311 | } 312 | 313 | void 314 | authinfo_data_get_mem(const struct authinfo_data_t *data, 315 | const char **mem, size_t *size) 316 | { 317 | *mem = data->buffer; 318 | *size = data->size; 319 | } 320 | 321 | void 322 | authinfo_data_free(struct authinfo_data_t *data) 323 | { 324 | if (data->sensitive) { 325 | assert(data->buffer_type != USER); 326 | authinfo_wipe((char *) data->buffer, data->size); 327 | } 328 | 329 | switch (data->buffer_type) { 330 | case USER: 331 | break; 332 | case MALLOC: 333 | free((void *) data->buffer); 334 | break; 335 | case GPGME: 336 | gpgme_free((void *) data->buffer); 337 | break; 338 | default: 339 | assert(false); 340 | } 341 | 342 | if (data->type == ALLOCATED) { 343 | free(data); 344 | } 345 | } 346 | 347 | enum authinfo_result_t 348 | authinfo_data_from_file(const char *path, struct authinfo_data_t **data) 349 | { 350 | enum authinfo_result_t ret; 351 | 352 | /* suppress bogus gcc warning about uninitialized use of file_data */ 353 | struct authinfo_data_t *file_data = file_data; 354 | 355 | ret = authinfo_do_read_file(path, &file_data); 356 | if (ret != AUTHINFO_OK) { 357 | return ret; 358 | } 359 | 360 | if (authinfo_is_gpged_file(path)) { 361 | struct authinfo_data_t *decrypted_data; 362 | 363 | ret = authinfo_gpgme_decrypt(file_data, &decrypted_data); 364 | *data = decrypted_data; 365 | 366 | authinfo_data_free(file_data); 367 | } else { 368 | *data = file_data; 369 | ret = AUTHINFO_OK; 370 | } 371 | 372 | return ret; 373 | } 374 | 375 | enum parse_state_t { 376 | LINE_START, 377 | WAITING_NEXT_PAIR, 378 | WAITING_HOST, 379 | WAITING_PROTOCOL, 380 | WAITING_USER, 381 | WAITING_PASSWORD, 382 | WAITING_FORCE, 383 | LINE_END, 384 | PARSE_STATE_MAX 385 | }; 386 | 387 | #ifdef DEBUG 388 | const char *parse_state2str(enum parse_state_t state) 389 | { 390 | const char *strs[] = { 391 | [LINE_START] = "LINE_START", 392 | [WAITING_NEXT_PAIR] = "WAITING_NEXT_PAIR", 393 | [WAITING_HOST] = "WAITING_HOST", 394 | [WAITING_PROTOCOL] = "WAITING_PROTOCOL", 395 | [WAITING_USER] = "WAITING_USER", 396 | [WAITING_PASSWORD] = "WAITING_PASSWORD", 397 | [WAITING_FORCE] = "WAITING_FORCE", 398 | [LINE_END] = "LINE_END" 399 | }; 400 | 401 | if (state >= PARSE_STATE_MAX) { 402 | return "UNKNOWN"; 403 | } else { 404 | return strs[state]; 405 | } 406 | } 407 | #endif 408 | 409 | void 410 | authinfo_parse(const struct authinfo_data_t *data, 411 | void *arg, 412 | authinfo_parse_entry_cb_t entry_callback, 413 | authinfo_parse_error_cb_t error_callback) 414 | { 415 | char host[TOKEN_SIZE_MAX]; 416 | char protocol[TOKEN_SIZE_MAX]; 417 | char user[TOKEN_SIZE_MAX]; 418 | 419 | char password_buffer[TOKEN_SIZE_MAX]; 420 | struct authinfo_data_t password_data = { .type = STATIC, 421 | .buffer_type = USER, 422 | .sensitive = false, 423 | .buffer = password_buffer, 424 | .size = TOKEN_SIZE_MAX }; 425 | struct authinfo_password_t password; 426 | 427 | struct authinfo_parse_entry_t entry; 428 | 429 | bool stop = false; 430 | 431 | enum parse_state_t state = LINE_START; 432 | 433 | struct authinfo_stream_t stream = { 434 | .data = data->buffer, 435 | .size = data->size, 436 | .line = 1, 437 | .column = 0, 438 | }; 439 | 440 | while (!stop) { 441 | char token[TOKEN_SIZE_MAX]; 442 | struct authinfo_parse_error_t parse_error; 443 | 444 | unsigned long token_column = stream.column; 445 | authinfo_skip_spaces(&stream); 446 | 447 | TRACE("\n"); 448 | TRACE("State: %s\n", parse_state2str(state)); 449 | TRACE("Position: %u:%u\n", stream.line, stream.column); 450 | 451 | if (authinfo_eol(&stream)) { 452 | TRACE("Encountered EOL at %u:%u\n", stream.line, stream.column); 453 | 454 | switch (state) { 455 | case LINE_START: 456 | /* is this really a EOF? */ 457 | if (authinfo_eof(&stream)) { 458 | TRACE("Encountered EOF at %u:%u\n", 459 | stream.line, stream.column); 460 | stop = true; 461 | } else { 462 | TRACE("Skipping empty line %u\n", stream.line); 463 | /* we haven't read anything; just go to the next line */ 464 | authinfo_skip_line(&stream); 465 | } 466 | continue; 467 | case WAITING_NEXT_PAIR: 468 | /* report the entry */ 469 | state = LINE_END; 470 | break; 471 | default: 472 | state = LINE_END; 473 | /* we were waiting for some value; report an error */ 474 | stop = authinfo_report_error(error_callback, arg, 475 | AUTHINFO_PET_MISSING_VALUE, 476 | stream.line, token_column); 477 | 478 | } 479 | } 480 | 481 | TRACE("Updated state: %s\n", parse_state2str(state)); 482 | 483 | switch (state) { 484 | case LINE_START: 485 | entry.host = NULL; 486 | entry.protocol = NULL; 487 | entry.user = NULL; 488 | entry.password = NULL; 489 | entry.force = false; 490 | 491 | if (!authinfo_skip_comment(&stream)) { 492 | if (!authinfo_skip_macdef(&stream)) { 493 | state = WAITING_NEXT_PAIR; 494 | } 495 | } 496 | 497 | break; 498 | case LINE_END: 499 | stop = authinfo_report_entry(entry_callback, error_callback, arg, 500 | stream.line, &entry); 501 | 502 | if (entry.password != NULL) { 503 | /* authinfo_password_extract may be called on a password in the 504 | * callback; and it can allocate new data buffer; so we need to 505 | * free it here */ 506 | authinfo_data_free(entry.password->data); 507 | } 508 | 509 | authinfo_skip_line(&stream); 510 | state = LINE_START; 511 | break; 512 | case WAITING_NEXT_PAIR: 513 | if (authinfo_next_token(&stream, token, &parse_error)) { 514 | bool report_duplicate = false; 515 | 516 | TRACE("Read token \"%s\"\n", token); 517 | 518 | if (strcmp(token, "default") == 0) { 519 | /* this is needed only to be able to report duplicates */ 520 | host[0] = '\0'; 521 | report_duplicate = (entry.host != NULL); 522 | entry.host = host; 523 | } else if (strcmp(token, "machine") == 0 || 524 | strcmp(token, "host") == 0) { 525 | state = WAITING_HOST; 526 | report_duplicate = (entry.host != NULL); 527 | } else if (strcmp(token, "login") == 0 || 528 | strcmp(token, "user") == 0 || 529 | strcmp(token, "account") == 0) { 530 | state = WAITING_USER; 531 | report_duplicate = (entry.user != NULL); 532 | } else if (strcmp(token, "password") == 0) { 533 | state = WAITING_PASSWORD; 534 | report_duplicate = (entry.password != NULL); 535 | } else if (strcmp(token, "force") == 0) { 536 | state = WAITING_FORCE; 537 | report_duplicate = entry.force; 538 | } else if (strcmp(token, "port") == 0 || 539 | strcmp(token, "protocol") == 0) { 540 | state = WAITING_PROTOCOL; 541 | report_duplicate = (entry.protocol != NULL); 542 | } else { 543 | stop = authinfo_report_error(error_callback, arg, 544 | AUTHINFO_PET_BAD_KEYWORD, 545 | stream.line, token_column); 546 | } 547 | 548 | if (report_duplicate) { 549 | stop = authinfo_report_error(error_callback, arg, 550 | AUTHINFO_PET_DUPLICATED_KEYWORD, 551 | stream.line, token_column); 552 | 553 | } 554 | } else { 555 | TRACE("Failed to read token at (%u:%u): %s\n", 556 | parse_error.line, parse_error.column, 557 | authinfo_parse_strerror(parse_error.type)); 558 | 559 | switch (parse_error.type) { 560 | case AUTHINFO_PET_VALUE_TOO_LONG: 561 | stop = authinfo_report_error(error_callback, arg, 562 | AUTHINFO_PET_BAD_KEYWORD, 563 | parse_error.line, 564 | parse_error.column); 565 | break; 566 | default: 567 | stop = authinfo_report_error(error_callback, arg, 568 | parse_error.type, 569 | parse_error.line, 570 | parse_error.column); 571 | } 572 | } 573 | break; 574 | case WAITING_HOST: 575 | case WAITING_PROTOCOL: 576 | case WAITING_USER: 577 | case WAITING_PASSWORD: 578 | case WAITING_FORCE: 579 | if (authinfo_next_token(&stream, token, &parse_error)) { 580 | TRACE("Read token \"%s\"\n", token); 581 | 582 | switch (state) { 583 | case WAITING_FORCE: 584 | if (strcmp(token, "yes") == 0) { 585 | entry.force = true; 586 | } else { 587 | stop = authinfo_report_error(error_callback, arg, 588 | AUTHINFO_PET_BAD_VALUE, 589 | stream.line, token_column); 590 | } 591 | 592 | break; 593 | 594 | #define ASSIGN(name) if (entry.name == NULL) { \ 595 | strcpy(name, token); \ 596 | entry.name = name; \ 597 | } 598 | 599 | case WAITING_HOST: 600 | ASSIGN(host); 601 | break; 602 | case WAITING_PROTOCOL: 603 | ASSIGN(protocol); 604 | break; 605 | case WAITING_USER: 606 | ASSIGN(user); 607 | break; 608 | case WAITING_PASSWORD: 609 | if (entry.password == NULL) { 610 | strcpy(password_buffer, token); 611 | password.data = &password_data; 612 | password.encrypted = authinfo_is_gpged_password(token); 613 | entry.password = &password; 614 | } 615 | break; 616 | default: 617 | /* should not happen */ 618 | assert(false); 619 | 620 | } 621 | #undef ASSIGN 622 | 623 | state = WAITING_NEXT_PAIR; 624 | } else { 625 | stop = authinfo_report_error(error_callback, arg, 626 | parse_error.type, 627 | parse_error.line, 628 | parse_error.column); 629 | state = WAITING_NEXT_PAIR; 630 | } 631 | 632 | break; 633 | default: 634 | /* should not happen */ 635 | assert(false); 636 | } 637 | } 638 | } 639 | 640 | 641 | static const char *authinfo_parse_error_type2str[] = { 642 | [AUTHINFO_PET_MISSING_VALUE] = "Expected a value", 643 | [AUTHINFO_PET_VALUE_TOO_LONG] = "Value is too long", 644 | [AUTHINFO_PET_BAD_VALUE] = "Invalid value", 645 | [AUTHINFO_PET_BAD_KEYWORD] = "Unknown keyword used", 646 | [AUTHINFO_PET_DUPLICATED_KEYWORD] = "Duplicate or synonymous keyword", 647 | [AUTHINFO_PET_UNTERMINATED_QUOTED_TOKEN] = "Quoted token ended unexpectedly", 648 | [AUTHINFO_PET_UNSUPPORTED_ESCAPE] = "Unsupported escape sequence" 649 | }; 650 | 651 | const char * 652 | authinfo_parse_strerror(enum authinfo_parse_error_type_t error) 653 | { 654 | if (error >= AUTHINFO_PET_MAX) { 655 | return "Unknown"; 656 | } else { 657 | return authinfo_parse_error_type2str[error]; 658 | } 659 | } 660 | 661 | enum authinfo_result_t 662 | authinfo_password_extract(struct authinfo_password_t *password, 663 | const char **data) 664 | { 665 | enum authinfo_result_t ret; 666 | 667 | if (password->encrypted) { 668 | struct authinfo_data_t *raw; 669 | struct authinfo_data_t *plain; 670 | struct authinfo_data_t *plainz; 671 | 672 | ret = authinfo_b64decode(password->data, &raw); 673 | if (ret != AUTHINFO_OK) { 674 | return ret; 675 | } 676 | 677 | ret = authinfo_gpgme_decrypt(raw, &plain); 678 | if (ret != AUTHINFO_OK) { 679 | authinfo_data_free(raw); 680 | return ret; 681 | } 682 | 683 | ret = authinfo_null_terminate(plain, &plainz); 684 | if (ret != AUTHINFO_OK) { 685 | authinfo_data_free(raw); 686 | authinfo_data_free(plain); 687 | return ret; 688 | } 689 | 690 | authinfo_data_free(raw); 691 | authinfo_data_free(plain); 692 | authinfo_data_free(password->data); 693 | 694 | password->data = plainz; 695 | password->encrypted = false; 696 | } 697 | 698 | *data = password->data->buffer; 699 | return AUTHINFO_OK; 700 | } 701 | 702 | enum authinfo_result_t 703 | authinfo_simple_query(const struct authinfo_data_t *data, 704 | const char *host, const char *protocol, const char *user, 705 | struct authinfo_parse_entry_t *entry, 706 | struct authinfo_parse_error_t *error) 707 | { 708 | struct authinfo_simple_query_data_t arg = { 709 | .host = host, 710 | .protocol = protocol, 711 | .user = user, 712 | 713 | .status = AUTHINFO_ENOMATCH, 714 | .entry = entry, 715 | .error = error, 716 | }; 717 | 718 | authinfo_parse(data, (void *) &arg, 719 | authinfo_simple_query_entry, authinfo_simple_query_error); 720 | 721 | return arg.status; 722 | } 723 | 724 | void 725 | authinfo_parse_entry_free(struct authinfo_parse_entry_t *entry) 726 | { 727 | if (entry->host != NULL) { 728 | free((void *) entry->host); 729 | } 730 | 731 | if (entry->user != NULL) { 732 | free((void *) entry->user); 733 | } 734 | 735 | if (entry->protocol != NULL) { 736 | free((void *) entry->protocol); 737 | } 738 | 739 | if (entry->password != NULL) { 740 | authinfo_data_free(entry->password->data); 741 | free((void *) entry->password); 742 | } 743 | } 744 | 745 | /* internal */ 746 | 747 | static enum authinfo_result_t 748 | authinfo_gpgme_init(void) 749 | { 750 | gpgme_error_t ret; 751 | 752 | assert(ctx.lc_ctype != NULL); 753 | assert(ctx.lc_messages != NULL); 754 | 755 | gpgme_check_version(NULL); 756 | 757 | ret = gpgme_set_locale(NULL, LC_CTYPE, ctx.lc_ctype); 758 | if (ret != GPG_ERR_NO_ERROR) { 759 | TRACE_GPG_ERROR("Couldn't set GPGME locale", ret); 760 | return authinfo_gpg_error2result(ret); 761 | } 762 | 763 | ret = gpgme_set_locale(NULL, LC_MESSAGES, ctx.lc_messages); 764 | if (ret != GPG_ERR_NO_ERROR) { 765 | TRACE_GPG_ERROR("Couldn't set GPGME locale", ret); 766 | return authinfo_gpg_error2result(ret); 767 | } 768 | 769 | return AUTHINFO_OK; 770 | } 771 | 772 | static enum authinfo_result_t 773 | authinfo_gpgme_do_decrypt(gpgme_ctx_t ctx, 774 | gpgme_data_t cipher_text, 775 | gpgme_data_t *plain_text) 776 | { 777 | int ret = gpgme_data_new(plain_text); 778 | if (ret != GPG_ERR_NO_ERROR) { 779 | TRACE_GPG_ERROR("Could not create GPGME data buffer", ret); 780 | return authinfo_gpg_error2result(ret); 781 | } 782 | 783 | ret = gpgme_op_decrypt(ctx, cipher_text, *plain_text); 784 | if (ret != GPG_ERR_NO_ERROR) { 785 | TRACE_GPG_ERROR("Could not decrypt cipher text", ret); 786 | gpgme_data_release(*plain_text); 787 | return authinfo_gpg_error2result(ret); 788 | } 789 | 790 | return AUTHINFO_OK; 791 | } 792 | 793 | static gpg_error_t 794 | authinfo_gpgme_passphrase_cb(void *data, 795 | const char *uid_hint, const char *info, 796 | int was_bad, int fd) 797 | { 798 | int ret; 799 | struct authinfo_passphrase_cb_ctx_t *ctx = data; 800 | struct authinfo_data_t *pindata; 801 | 802 | ret = pinentry_get_pin(ctx->pinentry, &pindata); 803 | if (ret != AUTHINFO_OK) { 804 | TRACE("pinentry_get_pin failed: %s\n", authinfo_strerror(ret)); 805 | 806 | ctx->ret = ret; 807 | return gpg_error(GPG_ERR_GENERAL); 808 | } 809 | 810 | struct { 811 | const void *data; 812 | size_t size; 813 | } datavec[] = { 814 | { 815 | .data = pindata->buffer, 816 | .size = pindata->size, 817 | }, 818 | 819 | { 820 | .data = "\n", 821 | .size = sizeof("\n") 822 | }, 823 | }; 824 | 825 | for (int i = 0; i < ARRAY_SIZE(datavec); i++) { 826 | ret = gpgme_io_writen(fd, datavec[i].data, datavec[i].size); 827 | if (ret != 0) { 828 | ret = gpg_error_from_syserror(); 829 | TRACE_GPG_ERROR("gpgme_io_writen failed", ret); 830 | 831 | ctx->ret = authinfo_gpg_error2result(ret); 832 | goto gpgme_passphrase_cb_free_pindata; 833 | } 834 | } 835 | 836 | ctx->ret = AUTHINFO_OK; 837 | ret = GPG_ERR_NO_ERROR; 838 | 839 | gpgme_passphrase_cb_free_pindata: 840 | authinfo_data_free(pindata); 841 | 842 | return ret; 843 | } 844 | 845 | static enum authinfo_result_t 846 | authinfo_gpgme_decrypt_loopback(gpgme_ctx_t gpgme_ctx, 847 | gpgme_data_t cipher_text, 848 | gpgme_data_t *plain_text) 849 | { 850 | int ret; 851 | 852 | struct pinentry_t pinentry; 853 | struct pinentry_settings_t settings = { 854 | .lc_ctype = ctx.lc_ctype, 855 | .lc_messages = ctx.lc_messages, 856 | .timeout = 60, 857 | .title = "title", 858 | .description = "description", 859 | .prompt = "prompt", 860 | }; 861 | 862 | struct authinfo_passphrase_cb_ctx_t cb_ctx = { 863 | /* if callback doesn't get called we assume that loopback mode in 864 | * gpg-agent is disabled */ 865 | .ret = AUTHINFO_ENOLOOPBACK, 866 | .pinentry = &pinentry, 867 | }; 868 | 869 | ret = gpgme_set_pinentry_mode(gpgme_ctx, GPGME_PINENTRY_MODE_LOOPBACK); 870 | if (ret != GPG_ERR_NO_ERROR) { 871 | TRACE_GPG_ERROR("Could not set pinentry mode", ret); 872 | return authinfo_gpg_error2result(ret); 873 | } 874 | 875 | gpgme_set_passphrase_cb(gpgme_ctx, 876 | authinfo_gpgme_passphrase_cb, (void *) &cb_ctx); 877 | 878 | ret = pinentry_new(&settings, &pinentry); 879 | if (ret != AUTHINFO_OK) { 880 | return ret; 881 | } 882 | 883 | for (int i = 0; i < LOOPBACK_RETRIES; i++) { 884 | if (gpgme_data_seek(cipher_text, 0, SEEK_SET) == -1) { 885 | TRACE("Failed to rewind cipher text data"); 886 | ret = AUTHINFO_EGPG; 887 | break; 888 | } 889 | 890 | if (i > 0) { 891 | char error_msg[100]; 892 | 893 | snprintf(error_msg, ARRAY_SIZE(error_msg), 894 | "Bad Passpharse (try %d of %d)", 895 | i + 1, LOOPBACK_RETRIES); 896 | 897 | ret = pinentry_set_error(&pinentry, error_msg); 898 | if (ret != AUTHINFO_OK) { 899 | break; 900 | } 901 | } 902 | 903 | ret = authinfo_gpgme_do_decrypt(gpgme_ctx, cipher_text, plain_text); 904 | if (ret == AUTHINFO_EGPG_DECRYPT_FAILED && 905 | cb_ctx.ret == AUTHINFO_OK) { 906 | ret = AUTHINFO_EGPG_BAD_PASSPHRASE; 907 | } else { 908 | break; 909 | } 910 | } 911 | 912 | if (ret != AUTHINFO_OK) { 913 | /* return error from the callback if any */ 914 | if (cb_ctx.ret != AUTHINFO_OK) { 915 | ret = cb_ctx.ret; 916 | } 917 | } 918 | 919 | pinentry_release(&pinentry); 920 | 921 | return ret; 922 | } 923 | 924 | static enum authinfo_result_t 925 | authinfo_gpgme_decrypt_no_loopback(gpgme_ctx_t gpgme_ctx, 926 | gpgme_data_t cipher_text, 927 | gpgme_data_t *plain_text) 928 | { 929 | int ret; 930 | 931 | ret = gpgme_set_pinentry_mode(gpgme_ctx, GPGME_PINENTRY_MODE_DEFAULT); 932 | if (ret != GPG_ERR_NO_ERROR) { 933 | TRACE_GPG_ERROR("Could not set pinentry mode", ret); 934 | return authinfo_gpg_error2result(ret); 935 | } 936 | 937 | return authinfo_gpgme_do_decrypt(gpgme_ctx, cipher_text, plain_text); 938 | } 939 | 940 | static enum authinfo_result_t 941 | authinfo_gpgme_decrypt(const struct authinfo_data_t *cipher_text, 942 | struct authinfo_data_t **plain_text) 943 | { 944 | gpgme_error_t gpgme_ret; 945 | gpgme_ctx_t ctx; 946 | gpgme_data_t cipher; 947 | gpgme_data_t plain; 948 | 949 | enum authinfo_result_t ret; 950 | 951 | gpgme_ret = gpgme_new(&ctx); 952 | if (gpgme_ret != GPG_ERR_NO_ERROR) { 953 | TRACE_GPG_ERROR("Could not create GPGME context", gpgme_ret); 954 | return authinfo_gpg_error2result(gpgme_ret); 955 | } 956 | 957 | gpgme_ret = gpgme_data_new_from_mem(&cipher, 958 | cipher_text->buffer, 959 | cipher_text->size, 0); 960 | if (gpgme_ret != GPG_ERR_NO_ERROR) { 961 | TRACE_GPG_ERROR("Could not create GPGME data buffer", gpgme_ret); 962 | ret = authinfo_gpg_error2result(gpgme_ret); 963 | goto gpgme_decrypt_release_ctx; 964 | } 965 | 966 | gpgme_ret = gpgme_data_set_encoding(cipher, GPGME_DATA_ENCODING_NONE); 967 | if (gpgme_ret != GPG_ERR_NO_ERROR) { 968 | TRACE_GPG_ERROR("Could not set buffer data encoding", gpgme_ret); 969 | ret = authinfo_gpg_error2result(gpgme_ret); 970 | goto gpgme_decrypt_release_cipher; 971 | } 972 | 973 | *plain_text = malloc(sizeof(**plain_text)); 974 | if (*plain_text == NULL) { 975 | ret = AUTHINFO_ENOMEM; 976 | goto gpgme_decrypt_release_cipher; 977 | } 978 | 979 | ret = authinfo_gpgme_decrypt_loopback(ctx, cipher, &plain); 980 | if (ret != AUTHINFO_OK) { 981 | if (ret == AUTHINFO_ENOLOOPBACK) { 982 | TRACE("Falling back to default pinentry mode\n"); 983 | 984 | if (gpgme_data_seek(cipher, 0, SEEK_SET) == -1) { 985 | TRACE("Failed to rewind ciphertext data"); 986 | ret = AUTHINFO_EGPG; 987 | goto gpgme_decrypt_free_plain_text; 988 | } 989 | 990 | ret = authinfo_gpgme_decrypt_no_loopback(ctx, cipher, &plain); 991 | if (ret != AUTHINFO_OK) { 992 | goto gpgme_decrypt_free_plain_text; 993 | } 994 | } else { 995 | goto gpgme_decrypt_free_plain_text; 996 | } 997 | } 998 | 999 | (*plain_text)->type = ALLOCATED; 1000 | (*plain_text)->buffer_type = GPGME; 1001 | (*plain_text)->sensitive = true; 1002 | (*plain_text)->buffer = gpgme_data_release_and_get_mem(plain, 1003 | &(*plain_text)->size); 1004 | 1005 | ret = AUTHINFO_OK; 1006 | goto gpgme_decrypt_release_cipher; 1007 | 1008 | gpgme_decrypt_free_plain_text: 1009 | free(*plain_text); 1010 | gpgme_decrypt_release_cipher: 1011 | gpgme_data_release(cipher); 1012 | gpgme_decrypt_release_ctx: 1013 | gpgme_release(ctx); 1014 | 1015 | return ret; 1016 | } 1017 | 1018 | static bool 1019 | authinfo_is_gpged_file(const char *path) 1020 | { 1021 | size_t extlen = strlen(GPG_EXT); 1022 | size_t n = strlen(path); 1023 | 1024 | if (n < extlen) { 1025 | return false; 1026 | } 1027 | 1028 | path += n - extlen; 1029 | return (strcasecmp(path, GPG_EXT) == 0); 1030 | } 1031 | 1032 | static bool 1033 | authinfo_is_gpged_password(const char *password) 1034 | { 1035 | return (strncmp(password, GPG_PREFIX, strlen(GPG_PREFIX)) == 0); 1036 | } 1037 | 1038 | static enum authinfo_result_t 1039 | authinfo_errno2result(int errnum) 1040 | { 1041 | enum authinfo_result_t ret; 1042 | 1043 | switch (errnum) { 1044 | case EACCES: 1045 | ret = AUTHINFO_EACCESS; 1046 | break; 1047 | case ENOENT: 1048 | case ENOTDIR: 1049 | case ELOOP: 1050 | ret = AUTHINFO_ENOENT; 1051 | break; 1052 | case ENOMEM: 1053 | ret = AUTHINFO_ENOMEM; 1054 | break; 1055 | default: 1056 | ret = AUTHINFO_EUNKNOWN; 1057 | } 1058 | 1059 | return ret; 1060 | } 1061 | 1062 | static char * 1063 | authinfo_path_join(const char *dir, const char *name) 1064 | { 1065 | char *path = NULL; 1066 | size_t length = strlen(dir) + strlen(name) + 2; 1067 | 1068 | path = malloc(length); 1069 | if (!path) { 1070 | TRACE("could not allocate %zu bytes\n", length); 1071 | return NULL; 1072 | } 1073 | 1074 | snprintf(path, length, "%s/%s", dir, name); 1075 | return path; 1076 | } 1077 | 1078 | static enum authinfo_result_t 1079 | authinfo_find_files_in_dir(const char *dir, 1080 | const char **names, size_t count, char **path) 1081 | { 1082 | enum authinfo_result_t ret = AUTHINFO_ENOENT; 1083 | 1084 | for (int i = 0; i < count; ++i) { 1085 | const char *name = names[i]; 1086 | 1087 | /* probe GPGed file first */ 1088 | size_t name_length = strlen(name); 1089 | char gpged_name[name_length + strlen(GPG_EXT) + 1]; 1090 | 1091 | memcpy(gpged_name, name, name_length); 1092 | strcpy(gpged_name + name_length, GPG_EXT); 1093 | 1094 | ret = authinfo_find_file_in_dir(dir, gpged_name, path); 1095 | if (ret != AUTHINFO_ENOENT) { 1096 | break; 1097 | } 1098 | 1099 | ret = authinfo_find_file_in_dir(dir, name, path); 1100 | if (ret != AUTHINFO_ENOENT) { 1101 | break; 1102 | } 1103 | } 1104 | 1105 | return ret; 1106 | } 1107 | 1108 | static enum authinfo_result_t 1109 | authinfo_find_file_in_dir(const char *dir, const char *name, char **pathp) 1110 | { 1111 | char *path; 1112 | enum authinfo_result_t ret; 1113 | 1114 | path = authinfo_path_join(dir, name); 1115 | if (!path) { 1116 | return AUTHINFO_ENOMEM; 1117 | } 1118 | *pathp = path; 1119 | 1120 | ret = authinfo_path_probe(path); 1121 | TRACE("Probed %s: %s\n", path, authinfo_strerror(ret)); 1122 | 1123 | if (ret != AUTHINFO_OK) { 1124 | free(path); 1125 | } 1126 | 1127 | return ret; 1128 | } 1129 | 1130 | static enum authinfo_result_t 1131 | authinfo_path_probe(const char *path) 1132 | { 1133 | enum authinfo_result_t ret = AUTHINFO_OK; 1134 | 1135 | if (access(path, R_OK) != 0) { 1136 | ret = authinfo_errno2result(errno); 1137 | } 1138 | 1139 | return ret; 1140 | } 1141 | 1142 | static enum authinfo_result_t 1143 | authinfo_do_read_file(const char *path, struct authinfo_data_t **data) 1144 | { 1145 | int fd; 1146 | int ret; 1147 | 1148 | char *buffer; 1149 | size_t buffer_size; 1150 | 1151 | struct stat stat; 1152 | 1153 | while (true) { 1154 | fd = open(path, O_RDONLY); 1155 | if (fd != -1) { 1156 | break; 1157 | } 1158 | 1159 | if (errno == EINTR) { 1160 | continue; 1161 | } else { 1162 | TRACE("Could not open authinfo file: %s\n", strerror(errno)); 1163 | return authinfo_errno2result(errno); 1164 | } 1165 | } 1166 | 1167 | if (fstat(fd, &stat) != 0) { 1168 | TRACE("Could not stat %s: %s", path, strerror(errno)); 1169 | return authinfo_errno2result(errno); 1170 | } 1171 | 1172 | *data = malloc(sizeof(**data)); 1173 | if (*data == NULL) { 1174 | return AUTHINFO_ENOMEM; 1175 | } 1176 | 1177 | buffer_size = stat.st_size; 1178 | buffer = malloc(buffer_size); 1179 | if (buffer == NULL) { 1180 | TRACE("Could not allocate buffer for %s", path); 1181 | free(*data); 1182 | return AUTHINFO_ENOMEM; 1183 | } 1184 | 1185 | (*data)->type = ALLOCATED; 1186 | (*data)->buffer_type = MALLOC; 1187 | (*data)->sensitive = false; 1188 | (*data)->buffer = buffer; 1189 | (*data)->size = 0; 1190 | 1191 | while (true) { 1192 | ssize_t nread; 1193 | 1194 | if (buffer_size == 0) { 1195 | break; 1196 | } 1197 | 1198 | nread = read(fd, buffer, MIN(buffer_size, 0xffff)); 1199 | if (nread == -1) { 1200 | if (errno == EINTR) { 1201 | continue; 1202 | } else { 1203 | TRACE("Could not read authinfo file: %s\n", strerror(errno)); 1204 | ret = authinfo_errno2result(errno); 1205 | goto do_read_file_error; 1206 | } 1207 | } else if (nread == 0) { 1208 | assert(buffer_size != 0); 1209 | break; 1210 | } 1211 | 1212 | buffer_size -= nread; 1213 | (*data)->size += nread; 1214 | } 1215 | 1216 | return AUTHINFO_OK; 1217 | 1218 | do_read_file_error: 1219 | authinfo_data_free(*data); 1220 | return ret; 1221 | } 1222 | 1223 | static int 1224 | authinfo_lookahead(struct authinfo_stream_t *stream) 1225 | { 1226 | assert(stream->size); 1227 | return *stream->data; 1228 | } 1229 | 1230 | static int 1231 | authinfo_next_char(struct authinfo_stream_t *stream) 1232 | { 1233 | int c; 1234 | 1235 | assert(stream->size); 1236 | c = *stream->data; 1237 | 1238 | stream->data += 1; 1239 | stream->size -= 1; 1240 | if (c == '\n') { 1241 | stream->line += 1; 1242 | stream->column = 0; 1243 | } else { 1244 | stream->column += 1; 1245 | } 1246 | 1247 | return c; 1248 | } 1249 | 1250 | static void 1251 | authinfo_skip_spaces(struct authinfo_stream_t *stream) 1252 | { 1253 | while (!authinfo_eof(stream)) { 1254 | int c = authinfo_lookahead(stream); 1255 | if (c != ' ' && c != '\t') { 1256 | break; 1257 | } 1258 | 1259 | (void) authinfo_next_char(stream); 1260 | } 1261 | } 1262 | 1263 | static bool 1264 | authinfo_eol(struct authinfo_stream_t *stream) 1265 | { 1266 | return authinfo_eof(stream) || authinfo_lookahead(stream) == '\n'; 1267 | } 1268 | 1269 | static bool 1270 | authinfo_eof(struct authinfo_stream_t *stream) 1271 | { 1272 | return stream->size == 0; 1273 | } 1274 | 1275 | static void 1276 | authinfo_skip_line(struct authinfo_stream_t *stream) 1277 | { 1278 | while (!authinfo_eof(stream) && 1279 | authinfo_lookahead(stream) != '\n') { 1280 | (void) authinfo_next_char(stream); 1281 | } 1282 | 1283 | if (!authinfo_eof(stream)) { 1284 | /* we stopped on new line so we want to skip it */ 1285 | (void) authinfo_next_char(stream); 1286 | } 1287 | } 1288 | 1289 | static bool 1290 | authinfo_skip_comment(struct authinfo_stream_t *stream) 1291 | { 1292 | /* this is ensured by authinfo_parse function */ 1293 | assert(!authinfo_eof(stream)); 1294 | 1295 | if (authinfo_lookahead(stream) == '#') { 1296 | TRACE("Skipping comment at line %u\n", stream->line); 1297 | authinfo_skip_line(stream); 1298 | return true; 1299 | } 1300 | 1301 | return false; 1302 | } 1303 | 1304 | static bool 1305 | authinfo_skip_macdef(struct authinfo_stream_t *stream) 1306 | { 1307 | char token[TOKEN_SIZE_MAX]; 1308 | 1309 | struct authinfo_stream_t tmp_stream = *stream; 1310 | 1311 | if (authinfo_next_token(&tmp_stream, token, NULL) && 1312 | strcmp(token, "macdef") == 0) { 1313 | 1314 | do { 1315 | authinfo_skip_line(&tmp_stream); 1316 | } while (!authinfo_eol(&tmp_stream)); 1317 | 1318 | TRACE("Skipped macdef on lines %u-%u\n", stream->line, tmp_stream.line); 1319 | 1320 | /* skip current empty line (if any) */ 1321 | authinfo_skip_line(&tmp_stream); 1322 | 1323 | *stream = tmp_stream; 1324 | 1325 | return true; 1326 | } 1327 | 1328 | return false; 1329 | } 1330 | 1331 | static bool 1332 | authinfo_report_entry(authinfo_parse_entry_cb_t entry_callback, 1333 | authinfo_parse_error_cb_t error_callback, 1334 | void *arg, 1335 | unsigned int line, 1336 | const struct authinfo_parse_entry_t *entry) 1337 | { 1338 | bool stop; 1339 | struct authinfo_parse_entry_t e = *entry; 1340 | 1341 | /* if host is empty it means that this's a "default" entry; we report 1342 | * this by setting host to NULL */ 1343 | if (e.host && strcmp(e.host, "") == 0) { 1344 | e.host = NULL; 1345 | } 1346 | 1347 | TRACE("Reporting an entry: host -> %s, protocol -> %s, " 1348 | "user -> %s, password -> %s, force -> %d\n", 1349 | e.host, e.protocol, e.user, 1350 | e.password ? e.password->data->buffer : "(null)", 1351 | (int) e.force); 1352 | stop = (*entry_callback)(&e, arg); 1353 | TRACE(" ====> %s\n", stop ? "stopping" : "continuing"); 1354 | 1355 | return stop; 1356 | } 1357 | 1358 | static bool 1359 | authinfo_report_error(authinfo_parse_error_cb_t error_callback, void *arg, 1360 | enum authinfo_parse_error_type_t type, 1361 | unsigned int line, unsigned int column) 1362 | { 1363 | struct authinfo_parse_error_t error = { 1364 | .line = line, 1365 | .column = column, 1366 | .type = type 1367 | }; 1368 | 1369 | TRACE("Reporting an error: %s (%u:%u)\n", 1370 | authinfo_parse_strerror(type), line, column); 1371 | bool stop = (*error_callback)(&error, arg); 1372 | TRACE(" ====> %s\n", 1373 | stop ? "stopping" : "continuing"); 1374 | 1375 | return stop; 1376 | } 1377 | 1378 | static bool 1379 | authinfo_next_token(struct authinfo_stream_t *stream, char *token, 1380 | struct authinfo_parse_error_t *error) 1381 | { 1382 | bool ret = true; 1383 | size_t span; 1384 | struct authinfo_stream_t tmp_stream; 1385 | 1386 | if (!authinfo_eof(stream) && authinfo_lookahead(stream) == '"') { 1387 | return authinfo_quoted_token(stream, token, error); 1388 | } 1389 | 1390 | tmp_stream = *stream; 1391 | while (!authinfo_eof(&tmp_stream)) { 1392 | int c = authinfo_lookahead(&tmp_stream); 1393 | if (c == ' ' || c == '\t' || c == '\n') { 1394 | break; 1395 | } 1396 | 1397 | (void) authinfo_next_char(&tmp_stream); 1398 | } 1399 | 1400 | span = tmp_stream.data - stream->data; 1401 | if (span >= TOKEN_SIZE_MAX) { 1402 | if (error != NULL) { 1403 | error->type = AUTHINFO_PET_VALUE_TOO_LONG; 1404 | error->line = stream->line; 1405 | error->column = stream->column; 1406 | } 1407 | ret = false; 1408 | } else { 1409 | memcpy(token, stream->data, span); 1410 | token[span] = '\0'; 1411 | } 1412 | 1413 | *stream = tmp_stream; 1414 | 1415 | return ret; 1416 | } 1417 | 1418 | static bool 1419 | authinfo_quoted_token(struct authinfo_stream_t *stream, char *token, 1420 | struct authinfo_parse_error_t *error) 1421 | { 1422 | enum { NORMAL, 1423 | SEEN_BACKSLASH, 1424 | DONE } state = NORMAL; 1425 | unsigned long token_column = stream->column; 1426 | size_t nwritten = 0; 1427 | bool error_occurred = false; 1428 | int c; 1429 | 1430 | c = authinfo_next_char(stream); 1431 | assert(c == '"'); 1432 | 1433 | while (state != DONE) { 1434 | if (authinfo_eol(stream)) { 1435 | error_occurred = true; 1436 | if (error != NULL) { 1437 | error->type = AUTHINFO_PET_UNTERMINATED_QUOTED_TOKEN; 1438 | error->line = stream->line; 1439 | error->column = stream->column; 1440 | } 1441 | break; 1442 | } 1443 | 1444 | /* this is safe because authinfo_eol above prevents us from hitting 1445 | * EOF */ 1446 | c = authinfo_lookahead(stream); 1447 | 1448 | switch (state) { 1449 | case NORMAL: 1450 | switch (c) { 1451 | case '"': 1452 | state = DONE; 1453 | break; 1454 | case '\\': 1455 | state = SEEN_BACKSLASH; 1456 | break; 1457 | default: 1458 | if (!error_occurred) { 1459 | token[nwritten++] = c; 1460 | } 1461 | } 1462 | break; 1463 | case SEEN_BACKSLASH: 1464 | switch (c) { 1465 | case '"': 1466 | if (!error_occurred) { 1467 | token[nwritten++] = '"'; 1468 | } 1469 | state = NORMAL; 1470 | break; 1471 | case '\\': 1472 | if (!error_occurred) { 1473 | token[nwritten++] = '\\'; 1474 | } 1475 | state = NORMAL; 1476 | break; 1477 | default: 1478 | if (!error_occurred) { 1479 | error_occurred = true; 1480 | if (error != NULL) { 1481 | error->type = AUTHINFO_PET_UNSUPPORTED_ESCAPE; 1482 | error->line = stream->line; 1483 | error->column = stream->column - 1; 1484 | } 1485 | } 1486 | state = NORMAL; 1487 | } 1488 | break; 1489 | default: 1490 | /* should not happen */ 1491 | assert(false); 1492 | } 1493 | 1494 | if (state != DONE && nwritten >= (TOKEN_SIZE_MAX - 1)) { 1495 | error_occurred = true; 1496 | if (error != NULL) { 1497 | error->type = AUTHINFO_PET_VALUE_TOO_LONG; 1498 | error->line = stream->line; 1499 | error->column = token_column; 1500 | } 1501 | } 1502 | 1503 | (void) authinfo_next_char(stream); 1504 | } 1505 | 1506 | token[nwritten] = '\0'; 1507 | 1508 | return !error_occurred; 1509 | } 1510 | 1511 | static bool 1512 | str_matches(const char *user_str, const char *str) 1513 | { 1514 | return (user_str == NULL) || (str == NULL) || (strcmp(user_str, str) == 0); 1515 | } 1516 | 1517 | static bool 1518 | authinfo_simple_query_entry(const struct authinfo_parse_entry_t *entry, 1519 | void *arg) 1520 | { 1521 | struct authinfo_simple_query_data_t *data = arg; 1522 | 1523 | if (str_matches(data->user, entry->user) && 1524 | str_matches(data->host, entry->host) && 1525 | str_matches(data->protocol, entry->protocol)) { 1526 | 1527 | data->entry->host = NULL; 1528 | data->entry->user = NULL; 1529 | data->entry->protocol = NULL; 1530 | data->entry->password = NULL; 1531 | data->entry->force = entry->force; 1532 | 1533 | if (entry->host != NULL) { 1534 | data->entry->host = strdup(entry->host); 1535 | if (data->entry->host == NULL) { 1536 | goto simple_query_entry_error; 1537 | } 1538 | } 1539 | 1540 | if (entry->protocol != NULL) { 1541 | data->entry->protocol = strdup(entry->protocol); 1542 | if (data->entry->protocol == NULL) { 1543 | goto simple_query_entry_error; 1544 | } 1545 | } 1546 | 1547 | if (entry->user != NULL) { 1548 | data->entry->user = strdup(entry->user); 1549 | if (data->entry->user == NULL) { 1550 | goto simple_query_entry_error; 1551 | } 1552 | } 1553 | 1554 | if (entry->password != NULL) { 1555 | enum authinfo_result_t ret; 1556 | struct authinfo_password_t *password; 1557 | struct authinfo_data_t *password_data; 1558 | 1559 | password = malloc(sizeof(*entry->password)); 1560 | if (password == NULL) { 1561 | goto simple_query_entry_error; 1562 | } 1563 | 1564 | ret = authinfo_data_copy(entry->password->data, &password_data); 1565 | if (ret != AUTHINFO_OK) { 1566 | assert(ret == AUTHINFO_ENOMEM); 1567 | free(password); 1568 | goto simple_query_entry_error; 1569 | } 1570 | 1571 | password->encrypted = entry->password->encrypted; 1572 | password->data = password_data; 1573 | 1574 | data->entry->password = password; 1575 | } 1576 | 1577 | data->status = AUTHINFO_OK; 1578 | return true; 1579 | 1580 | simple_query_entry_error: 1581 | authinfo_parse_entry_free(data->entry); 1582 | data->status = AUTHINFO_ENOMEM; 1583 | return true; 1584 | } 1585 | 1586 | return false; 1587 | } 1588 | 1589 | static bool 1590 | authinfo_simple_query_error(const struct authinfo_parse_error_t *error, 1591 | void *arg) 1592 | { 1593 | struct authinfo_simple_query_data_t *data = arg; 1594 | 1595 | data->status = AUTHINFO_EPARSE; 1596 | 1597 | if (data->error != NULL) { 1598 | *data->error = *error; 1599 | } 1600 | return true; 1601 | } 1602 | 1603 | static enum authinfo_result_t 1604 | authinfo_b64decode(const struct authinfo_data_t *b64data, 1605 | struct authinfo_data_t **data) 1606 | { 1607 | int ret; 1608 | 1609 | *data = malloc(sizeof(**data)); 1610 | if (*data == NULL) { 1611 | return AUTHINFO_ENOMEM; 1612 | } 1613 | 1614 | (*data)->buffer = malloc(b64data->size); 1615 | if ((*data)->buffer == NULL) { 1616 | free(*data); 1617 | return AUTHINFO_ENOMEM; 1618 | } 1619 | 1620 | (*data)->type = ALLOCATED; 1621 | (*data)->buffer_type = MALLOC; 1622 | (*data)->sensitive = false; 1623 | 1624 | ret = base64_decode((uint8_t *) (*data)->buffer, 1625 | b64data->buffer + strlen(GPG_PREFIX), b64data->size); 1626 | if (ret == -1) { 1627 | authinfo_data_free(*data); 1628 | return AUTHINFO_EGPG_BAD_BASE64; 1629 | } 1630 | 1631 | (*data)->size = ret; 1632 | return AUTHINFO_OK; 1633 | } 1634 | 1635 | static enum authinfo_result_t 1636 | authinfo_null_terminate(const struct authinfo_data_t *input, 1637 | struct authinfo_data_t **output) 1638 | { 1639 | char *buffer; 1640 | size_t size; 1641 | 1642 | *output = malloc(sizeof(**output)); 1643 | if (*output == NULL) { 1644 | return AUTHINFO_ENOMEM; 1645 | } 1646 | 1647 | size = input->size + 1; 1648 | buffer = malloc(size); 1649 | if (buffer == NULL) { 1650 | free(*output); 1651 | return AUTHINFO_ENOMEM; 1652 | } 1653 | 1654 | memcpy(buffer, input->buffer, input->size); 1655 | buffer[size - 1] = '\0'; 1656 | 1657 | (*output)->type = ALLOCATED; 1658 | (*output)->buffer_type = MALLOC; 1659 | (*output)->sensitive = input->sensitive; 1660 | (*output)->buffer = buffer; 1661 | (*output)->size = size; 1662 | 1663 | return AUTHINFO_OK; 1664 | } 1665 | 1666 | static enum authinfo_result_t 1667 | authinfo_data_copy(const struct authinfo_data_t *data, 1668 | struct authinfo_data_t **copy) 1669 | { 1670 | char *buffer; 1671 | 1672 | *copy = malloc(sizeof(**copy)); 1673 | if (*copy == NULL) { 1674 | return AUTHINFO_ENOMEM; 1675 | } 1676 | 1677 | buffer = malloc(data->size); 1678 | if (buffer == NULL) { 1679 | free(*copy); 1680 | return AUTHINFO_ENOMEM; 1681 | } 1682 | 1683 | memcpy(buffer, data->buffer, data->size); 1684 | 1685 | (*copy)->type = ALLOCATED; 1686 | (*copy)->buffer = buffer; 1687 | (*copy)->buffer_type = MALLOC; 1688 | (*copy)->sensitive = data->sensitive; 1689 | (*copy)->size = data->size; 1690 | 1691 | return AUTHINFO_OK; 1692 | } 1693 | 1694 | static void 1695 | authinfo_wipe(char *buffer, size_t size) 1696 | { 1697 | volatile char *p = buffer; 1698 | 1699 | while (size--) { 1700 | *p++ = 0; 1701 | } 1702 | } 1703 | 1704 | static void 1705 | authinfo_ctx_free(void) 1706 | { 1707 | if (ctx.name != NULL) { 1708 | free(ctx.name); 1709 | ctx.name = NULL; 1710 | } 1711 | 1712 | if (ctx.lc_ctype != NULL) { 1713 | free(ctx.lc_ctype); 1714 | ctx.lc_ctype = NULL; 1715 | } 1716 | 1717 | if (ctx.lc_messages != NULL) { 1718 | free(ctx.lc_messages); 1719 | ctx.lc_messages = NULL; 1720 | } 1721 | } 1722 | -------------------------------------------------------------------------------- /src/authinfo_data.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2015 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | #ifndef _AUTHINFO_DATA_H_ 23 | #define _AUTHINFO_DATA_H_ 24 | 25 | #include 26 | 27 | struct authinfo_data_t { 28 | enum { STATIC, ALLOCATED } type; 29 | enum { USER, MALLOC, GPGME } buffer_type; 30 | bool sensitive; 31 | 32 | const char *buffer; 33 | size_t size; 34 | }; 35 | 36 | 37 | #endif /* _AUTHINFO_DATA_H_ */ 38 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) 5 | * 6 | * This file is part of FFmpeg. 7 | * 8 | * FFmpeg is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * FFmpeg is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with FFmpeg; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | */ 22 | 23 | /** 24 | * @file 25 | * @brief Base64 encode/decode 26 | * @author Ryan Martell (with lots of Michael) 27 | */ 28 | 29 | #include 30 | #include 31 | #include "base64.h" 32 | 33 | #define SIZEOF_ARRAY(a) (sizeof((a)) / sizeof((a)[0])) 34 | 35 | /* ---------------- private code */ 36 | static const uint8_t map2[] = 37 | { 38 | 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, 39 | 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 40 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 41 | 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 42 | 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 43 | 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 44 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, 45 | 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 46 | 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 47 | 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33 48 | }; 49 | 50 | int base64_decode(uint8_t *out, const char *in, int out_size) 51 | { 52 | int i, v; 53 | uint8_t *dst = out; 54 | 55 | v = 0; 56 | for (i = 0; in[i] && in[i] != '='; i++) { 57 | unsigned int index= in[i]-43; 58 | if (index>=SIZEOF_ARRAY(map2) || map2[index] == 0xff) 59 | return -1; 60 | v = (v << 6) + map2[index]; 61 | if (i & 3) { 62 | if (dst - out < out_size) { 63 | *dst++ = v >> (6 - 2 * (i & 3)); 64 | } 65 | } 66 | } 67 | 68 | return dst - out; 69 | } 70 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) 5 | * 6 | * This file is part of FFmpeg. 7 | * 8 | * FFmpeg is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * FFmpeg is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with FFmpeg; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | */ 22 | 23 | #ifndef BASE64_H 24 | #define BASE64_H 25 | 26 | #include 27 | 28 | /** 29 | * Decode a base64-encoded string. 30 | * 31 | * @param out buffer for decoded data 32 | * @param in null-terminated input string 33 | * @param out_size size in bytes of the out buffer, must be at 34 | * least 3/4 of the length of in 35 | * @return number of bytes written, or a negative value in case of 36 | * invalid input 37 | */ 38 | int base64_decode(uint8_t *out, const char *in, int out_size); 39 | 40 | /** 41 | * Encode data to base64 and null-terminate. 42 | * 43 | * @param out buffer for encoded data 44 | * @param out_size size in bytes of the output buffer, must be at 45 | * least BASE64_SIZE(in_size) 46 | * @param in_size size in bytes of the 'in' buffer 47 | * @return 'out' or NULL in case of error 48 | */ 49 | char *base64_encode(char *out, int out_size, const uint8_t *in, int in_size); 50 | 51 | /** 52 | * Calculate the output size needed to base64-encode x bytes. 53 | */ 54 | #define BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) 55 | 56 | #endif /* BASE64_H */ 57 | -------------------------------------------------------------------------------- /src/cli.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2013 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "config.h" 33 | #include "authinfo.h" 34 | 35 | enum command_t { 36 | CMD_QUERY, 37 | CMD_VALIDATE, 38 | CMD_VERSION, 39 | CMD_HELP, 40 | }; 41 | 42 | enum option_t { 43 | OPT_USER = 1, 44 | OPT_HOST, 45 | OPT_PROTOCOL, 46 | OPT_PATH, 47 | }; 48 | 49 | static const char *program = NULL; 50 | 51 | static int command = CMD_HELP; 52 | 53 | static char *authinfo_path = NULL; 54 | 55 | static const char *user = NULL; 56 | static const char *host = NULL; 57 | static const char *protocol = NULL; 58 | 59 | static int error_count = 0; 60 | 61 | static void 62 | usage(void) 63 | { 64 | printf("Usage: %s [COMMAND] [OPTIONS]\n\n", program); 65 | printf("Supported commands:\n"); 66 | printf(" --query query authinfo file for matching entries\n"); 67 | printf(" --user match user name\n"); 68 | printf(" --host match host name\n"); 69 | printf(" --protocol match protocol\n"); 70 | printf(" --path use this authinfo file instead of autodiscovered\n"); 71 | printf(" --validate check authinfo file for syntax errors\n"); 72 | printf(" --path use this authinfo file instead of autodiscovered\n"); 73 | printf(" --version print version info\n"); 74 | printf(" --help print this help\n"); 75 | } 76 | 77 | static void 78 | version(void) 79 | { 80 | printf("authinfo version %s\n", PACKAGE_VERSION); 81 | } 82 | 83 | static void 84 | authinfo_error(enum authinfo_result_t error, const char *msg) 85 | { 86 | fprintf(stderr, "%s: %s (%s)\n", program, msg, authinfo_strerror(error)); 87 | exit(EXIT_FAILURE); 88 | } 89 | 90 | static void 91 | init_authinfo(void) 92 | { 93 | enum authinfo_result_t ret; 94 | 95 | ret = authinfo_init("authinfo"); 96 | if (ret != AUTHINFO_OK) { 97 | authinfo_error(ret, "failed to initialize authinfo library"); 98 | } 99 | } 100 | 101 | static void 102 | maybe_find_file(void) 103 | { 104 | enum authinfo_result_t ret; 105 | 106 | if (authinfo_path) { 107 | return; 108 | } 109 | 110 | ret = authinfo_find_file(&authinfo_path); 111 | if (ret != AUTHINFO_OK) { 112 | authinfo_error(ret, "failed to find authinfo file"); 113 | } 114 | } 115 | 116 | static void 117 | read_file(struct authinfo_data_t **data) 118 | { 119 | enum authinfo_result_t ret; 120 | 121 | assert(authinfo_path); 122 | 123 | ret = authinfo_data_from_file(authinfo_path, data); 124 | if (ret != AUTHINFO_OK) { 125 | authinfo_error(ret, "couldn't read authinfo file"); 126 | } 127 | } 128 | 129 | static void 130 | emit_env_var(const char *var, const char *value) 131 | { 132 | if (value == NULL) { 133 | value = ""; 134 | } 135 | 136 | size_t len = strlen(value); 137 | /* every original character will at most require 4 characters in the 138 | * escaped string; plus we need two bytes for starting and ending quotes */ 139 | char escaped_value[4 * len + 3]; 140 | 141 | const char *s = value; 142 | char *d = escaped_value; 143 | 144 | *d++ = '\''; 145 | 146 | while (*s) { 147 | if (*s == '\'') { 148 | /* there's no way to escape single quote in single-quoted string; 149 | * so we produce a closing single quote, then escaped single 150 | * quote and then an opening single quote again */ 151 | *d++ = '\''; 152 | *d++ = '\\'; 153 | *d++ = '\''; 154 | *d++ = '\''; 155 | } else { 156 | *d++ = *s; 157 | } 158 | 159 | s += 1; 160 | } 161 | 162 | *d++ = '\''; 163 | *d = '\0'; 164 | 165 | printf("%s=%s\n", var, escaped_value); 166 | printf("export %s\n", var); 167 | } 168 | 169 | static void 170 | query_process_entry(struct authinfo_parse_entry_t *entry) 171 | { 172 | const char *password = NULL; 173 | 174 | if (entry->password != NULL) { 175 | enum authinfo_result_t ret; 176 | 177 | ret = authinfo_password_extract(entry->password, &password); 178 | if (ret != AUTHINFO_OK) { 179 | authinfo_error(ret, "couldn't extract password"); 180 | } 181 | } 182 | 183 | emit_env_var("AUTHINFO_HOST", entry->host); 184 | emit_env_var("AUTHINFO_USER", entry->user); 185 | emit_env_var("AUTHINFO_PROTOCOL", entry->protocol); 186 | emit_env_var("AUTHINFO_PASSWORD", password); 187 | 188 | authinfo_parse_entry_free(entry); 189 | } 190 | 191 | static void 192 | query(void) 193 | { 194 | enum authinfo_result_t ret; 195 | struct authinfo_parse_entry_t entry; 196 | struct authinfo_parse_error_t error; 197 | struct authinfo_data_t *data; 198 | 199 | int status = EXIT_SUCCESS; 200 | 201 | init_authinfo(); 202 | maybe_find_file(); 203 | read_file(&data); 204 | 205 | ret = authinfo_simple_query(data, host, protocol, user, 206 | &entry, &error); 207 | authinfo_data_free(data); 208 | switch (ret) { 209 | case AUTHINFO_OK: 210 | query_process_entry(&entry); 211 | break; 212 | case AUTHINFO_ENOMATCH: 213 | fprintf(stderr, "%s: no matching entries found\n", program); 214 | status = EXIT_FAILURE; 215 | break; 216 | case AUTHINFO_EPARSE: 217 | fprintf(stderr, "%s: parse error at %s:%u:%u (%s)\n", 218 | program, authinfo_path, error.line, error.column, 219 | authinfo_parse_strerror(error.type)); 220 | status = EXIT_FAILURE; 221 | break; 222 | default: 223 | authinfo_error(ret, "couldn't find matching entry"); 224 | } 225 | 226 | exit(status); 227 | } 228 | 229 | static bool 230 | validate_error(const struct authinfo_parse_error_t *error, void *arg) 231 | { 232 | printf(" %u:%u: %s\n", 233 | error->line, error->column, authinfo_parse_strerror(error->type)); 234 | ++error_count; 235 | return false; 236 | } 237 | 238 | static bool 239 | validate_entry(const struct authinfo_parse_entry_t *entry, void *arg) 240 | { 241 | return false; 242 | } 243 | 244 | static void 245 | validate(void) 246 | { 247 | struct authinfo_data_t *data; 248 | 249 | init_authinfo(); 250 | maybe_find_file(); 251 | read_file(&data); 252 | 253 | printf("Parsing %s.\n", authinfo_path); 254 | authinfo_parse(data, NULL, validate_entry, validate_error); 255 | 256 | authinfo_data_free(data); 257 | 258 | if (error_count == 0) { 259 | printf(" No errors found\n"); 260 | } else { 261 | exit(EXIT_FAILURE); 262 | } 263 | } 264 | 265 | void 266 | disable_core_dumps() 267 | { 268 | struct rlimit limit; 269 | 270 | if (getrlimit(RLIMIT_CORE, &limit) != 0) { 271 | limit.rlim_max = 0; 272 | } 273 | 274 | limit.rlim_cur = 0; 275 | if (setrlimit(RLIMIT_CORE, &limit) != 0) { 276 | fprintf(stderr, "Couldn't disable core dumps: %s\n", strerror(errno)); 277 | } 278 | } 279 | 280 | int 281 | main(int argc, char *argv[]) 282 | { 283 | struct option options[] = { 284 | {"query", no_argument, &command, CMD_QUERY}, 285 | {"validate", no_argument, &command, CMD_VALIDATE}, 286 | {"version", no_argument, &command, CMD_VERSION}, 287 | {"help", no_argument, &command, CMD_HELP}, 288 | {"user", required_argument, NULL, OPT_USER}, 289 | {"host", required_argument, NULL, OPT_HOST}, 290 | {"protocol", required_argument, NULL, OPT_PROTOCOL}, 291 | {"path", required_argument, NULL, OPT_PATH}, 292 | {0, 0, 0, 0} 293 | }; 294 | 295 | program = argv[0]; 296 | 297 | setlocale(LC_ALL, ""); 298 | 299 | while (true) { 300 | int opt; 301 | 302 | opt = getopt_long(argc, argv, "", options, NULL); 303 | if (opt == '?') { 304 | return EXIT_FAILURE; 305 | } else if (opt == -1) { 306 | break; 307 | } 308 | 309 | switch (opt) { 310 | case 0: 311 | break; 312 | case OPT_USER: 313 | user = optarg; 314 | break; 315 | case OPT_HOST: 316 | host = optarg; 317 | break; 318 | case OPT_PROTOCOL: 319 | protocol = optarg; 320 | break; 321 | case OPT_PATH: 322 | authinfo_path = optarg; 323 | break; 324 | default: 325 | /* should not happen */ 326 | assert(false); 327 | } 328 | } 329 | 330 | disable_core_dumps(); 331 | 332 | switch (command) { 333 | case CMD_QUERY: 334 | query(); 335 | break; 336 | case CMD_VALIDATE: 337 | validate(); 338 | break; 339 | case CMD_VERSION: 340 | version(); 341 | break; 342 | case CMD_HELP: 343 | usage(); 344 | break; 345 | default: 346 | assert(false); 347 | } 348 | 349 | return EXIT_SUCCESS; 350 | } 351 | -------------------------------------------------------------------------------- /src/pinentry.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2015 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "authinfo_data.h" 27 | #include "pinentry.h" 28 | #include "utils.h" 29 | 30 | #ifdef __GNUC__ 31 | #define FORMAT_ATTR(type, fmt, args) \ 32 | __attribute__((format (type, fmt, args))) 33 | #else 34 | #define FORMAT_ATTR(type, fmt, args) 35 | #endif 36 | 37 | typedef gpg_error_t (*assuan_data_cb_t)(void *, const void *, size_t); 38 | 39 | static enum authinfo_result_t 40 | pinentry_command(struct pinentry_t *pinentry, 41 | assuan_data_cb_t data_cb, void *arg, 42 | const char *fmt, ...) 43 | FORMAT_ATTR(printf, 4, 5); 44 | 45 | enum authinfo_result_t 46 | pinentry_new(const struct pinentry_settings_t *settings, 47 | struct pinentry_t *pinentry) 48 | { 49 | gpg_error_t gpg_ret; 50 | enum authinfo_result_t ret; 51 | 52 | /* TODO: search for pinentry in PATH */ 53 | const char *argv[2] = {"/usr/bin/pinentry", NULL}; 54 | 55 | gpg_ret = assuan_new(&pinentry->ctx); 56 | if (gpg_ret != GPG_ERR_NO_ERROR) { 57 | TRACE_GPG_ERROR("Couldn't create assuan context", gpg_ret); 58 | return authinfo_gpg_error2result(gpg_ret); 59 | } 60 | 61 | gpg_ret = assuan_pipe_connect(pinentry->ctx, argv[0], argv, 62 | NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_DETACHED); 63 | if (gpg_ret != GPG_ERR_NO_ERROR) { 64 | TRACE_GPG_ERROR("Couldn't start pinentry", gpg_ret); 65 | ret = authinfo_gpg_error2result(gpg_ret); 66 | goto pinentry_start_release_context; 67 | } 68 | 69 | #define cmd(...) \ 70 | ret = pinentry_command(pinentry, NULL, NULL, __VA_ARGS__); \ 71 | if (ret != AUTHINFO_OK) { \ 72 | goto pinentry_start_release_context; \ 73 | } 74 | 75 | cmd("OPTION lc-ctype=%s", settings->lc_ctype); 76 | cmd("OPTION lc-messages=%s", settings->lc_messages); 77 | cmd("OPTION grab"); 78 | cmd("SETTIMEOUT %d", settings->timeout); 79 | cmd("SETTITLE %s", settings->title); 80 | cmd("SETDESC %s", settings->description); 81 | cmd("SETPROMPT %s", settings->prompt); 82 | 83 | #undef cmd 84 | 85 | return AUTHINFO_OK; 86 | 87 | pinentry_start_release_context: 88 | assuan_release(pinentry->ctx); 89 | 90 | return ret; 91 | } 92 | 93 | void 94 | pinentry_release(struct pinentry_t *pinentry) 95 | { 96 | assuan_release(pinentry->ctx); 97 | } 98 | 99 | enum authinfo_result_t 100 | pinentry_set_error(struct pinentry_t *pinentry, const char *error) 101 | { 102 | return pinentry_command(pinentry, NULL, NULL, "SETERROR %s", error); 103 | } 104 | 105 | static gpg_error_t 106 | pinentry_get_pin_cb(void *arg, const void *pin, size_t size) 107 | { 108 | struct authinfo_data_t *data = arg; 109 | 110 | data->size = size; 111 | data->buffer = malloc(size); 112 | if (data->buffer == NULL) { 113 | return gpg_error(GPG_ERR_ENOMEM); 114 | } 115 | 116 | memcpy((void *) data->buffer, pin, size); 117 | 118 | return GPG_ERR_NO_ERROR; 119 | } 120 | 121 | enum authinfo_result_t 122 | pinentry_get_pin(struct pinentry_t *pinentry, struct authinfo_data_t **data) 123 | { 124 | gpg_error_t ret; 125 | 126 | *data = malloc(sizeof(**data)); 127 | if (*data == NULL) { 128 | return AUTHINFO_ENOMEM; 129 | } 130 | 131 | (*data)->type = ALLOCATED; 132 | (*data)->buffer_type = MALLOC; 133 | (*data)->sensitive = true; 134 | 135 | ret = pinentry_command(pinentry, 136 | pinentry_get_pin_cb, *data, "GETPIN"); 137 | if (ret != AUTHINFO_OK) { 138 | free(*data); 139 | } 140 | 141 | return ret; 142 | } 143 | 144 | static enum authinfo_result_t 145 | pinentry_command(struct pinentry_t *pinentry, 146 | assuan_data_cb_t data_cb, void *arg, 147 | const char *fmt, ...) 148 | { 149 | va_list ap; 150 | char command[1024]; 151 | 152 | gpg_error_t ret; 153 | 154 | va_start(ap, fmt); 155 | vsnprintf(command, sizeof(command), fmt, ap); 156 | 157 | TRACE("Sending '%s' to pinentry\n", command); 158 | 159 | ret = assuan_transact(pinentry->ctx, command, 160 | data_cb, arg, NULL, NULL, NULL, NULL); 161 | if (ret != GPG_ERR_NO_ERROR) { 162 | TRACE_GPG_ERROR("Pinentry command failed", ret); 163 | return authinfo_gpg_error2result(ret); 164 | } 165 | 166 | return AUTHINFO_OK; 167 | } 168 | -------------------------------------------------------------------------------- /src/pinentry.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2015 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | #ifndef _PINENTRY_H_ 23 | #define _PINENTRY_H_ 24 | 25 | #include 26 | 27 | #include "authinfo.h" 28 | 29 | struct pinentry_settings_t { 30 | const char *lc_ctype; 31 | const char *lc_messages; 32 | 33 | int timeout; 34 | 35 | const char *title; 36 | const char *description; 37 | const char *prompt; 38 | }; 39 | 40 | struct pinentry_t { 41 | assuan_context_t ctx; 42 | }; 43 | 44 | enum authinfo_result_t 45 | pinentry_new(const struct pinentry_settings_t *settings, 46 | struct pinentry_t *pinentry); 47 | 48 | void 49 | pinentry_release(struct pinentry_t *pinentry); 50 | 51 | enum authinfo_result_t 52 | pinentry_set_error(struct pinentry_t *pinentry, const char *error); 53 | 54 | enum authinfo_result_t 55 | pinentry_get_pin(struct pinentry_t *pinentry, struct authinfo_data_t **pin); 56 | 57 | #endif /* _PINENTRY_H_ */ 58 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2015 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | #include "utils.h" 23 | 24 | enum authinfo_result_t 25 | authinfo_gpg_error2result(gpg_error_t error) 26 | { 27 | if (error == GPG_ERR_NO_ERROR) { 28 | return AUTHINFO_OK; 29 | } 30 | 31 | switch (gpg_err_code(error)) { 32 | case GPG_ERR_DECRYPT_FAILED: 33 | return AUTHINFO_EGPG_DECRYPT_FAILED; 34 | case GPG_ERR_BAD_PASSPHRASE: 35 | return AUTHINFO_EGPG_BAD_PASSPHRASE; 36 | case GPG_ERR_ENOMEM: 37 | return AUTHINFO_ENOMEM; 38 | case GPG_ERR_CANCELED: 39 | return AUTHINFO_EGPG_CANCELED; 40 | default: 41 | return AUTHINFO_EGPG; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | /* 4 | * Copyright (C) 2013 Aliaksey Artamonau 5 | * 6 | * This file is part of authinfo. 7 | * 8 | * Authinfo is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Authinfo is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with authinfo. If not, see . 20 | */ 21 | 22 | #ifndef _UTILS_H_ 23 | #define _UTILS_H_ 24 | 25 | #include 26 | #include 27 | 28 | #include "authinfo.h" 29 | 30 | #ifdef DEBUG 31 | # define STRINGIFY(exp) STRINGIFY_HELPER(exp) 32 | # define STRINGIFY_HELPER(exp) #exp 33 | # define TRACE(...) \ 34 | fprintf(stderr, \ 35 | "TRACE: " __FILE__ ":" STRINGIFY(__LINE__) ": " __VA_ARGS__) 36 | # define TRACE_GPG_ERROR(msg, error) \ 37 | do { \ 38 | char buf[128] = {0}; \ 39 | gpg_strerror_r(error, buf, sizeof(buf)); \ 40 | TRACE("%s: %s: %s\n", msg, gpg_strsource(error), buf); \ 41 | } while (0); 42 | #else 43 | # define TRACE(...) 44 | # define TRACE_GPG_ERROR(msg, error) 45 | #endif 46 | 47 | enum authinfo_result_t 48 | authinfo_gpg_error2result(gpg_error_t error); 49 | 50 | #endif /* _UTILS_H_ */ 51 | -------------------------------------------------------------------------------- /tests/authinfo_gpg_tests.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- */ 2 | 3 | #include "config.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "authinfo.h" 14 | 15 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 16 | 17 | #define GPG(body) \ 18 | do { \ 19 | gpgme_error_t __err = (body); \ 20 | if (gpg_err_code(__err) != GPG_ERR_NO_ERROR) { \ 21 | fprintf(stderr, "%s:%d: %s failed -> %s\n", \ 22 | __FILE__, __LINE__, #body, gpgme_strerror(__err)); \ 23 | ck_abort(); \ 24 | } \ 25 | } while(0) 26 | 27 | #define AUTHINFO(body) \ 28 | do { \ 29 | enum authinfo_result_t __err = (body); \ 30 | if (__err != AUTHINFO_OK) { \ 31 | fprintf(stderr, "%s:%d: %s failed -> %s\n", \ 32 | __FILE__, __LINE__, #body, authinfo_strerror(__err)); \ 33 | ck_abort(); \ 34 | } \ 35 | } while(0) 36 | 37 | static void 38 | setup(void) 39 | { 40 | AUTHINFO(authinfo_init(NULL)); 41 | 42 | gpgme_ctx_t ctx; 43 | gpgme_data_t data; 44 | 45 | GPG(gpgme_new(&ctx)); 46 | 47 | GPG(gpgme_data_new_from_file(&data, 48 | TOP_SRCDIR "/tests/files/gpg_tests/public.key", 49 | 1)); 50 | GPG(gpgme_op_import(ctx, data)); 51 | gpgme_data_release(data); 52 | 53 | GPG(gpgme_data_new_from_file(&data, 54 | TOP_SRCDIR "/tests/files/gpg_tests/private.key", 55 | 1)); 56 | GPG(gpgme_op_import(ctx, data)); 57 | gpgme_data_release(data); 58 | 59 | gpgme_release(ctx); 60 | } 61 | 62 | static void 63 | teardown(void) 64 | { 65 | /* since version 2.1 gpg prompts the confirmation dialog when secret key 66 | * is being removed; so I had to comment this out; 67 | * 68 | * TODO: figure out if I can do better than this 69 | */ 70 | 71 | 72 | /* gpgme_ctx_t ctx; */ 73 | /* gpgme_key_t test_key = NULL; */ 74 | /* gpgme_key_t key; */ 75 | 76 | /* GPG(gpgme_new(&ctx)); */ 77 | 78 | /* GPG(gpgme_op_keylist_start(ctx, NULL, 0)); */ 79 | /* while (true) { */ 80 | /* gpgme_error_t err; */ 81 | 82 | /* err = gpgme_op_keylist_next(ctx, &key); */ 83 | /* if (gpg_err_code(err) == GPG_ERR_EOF) { */ 84 | /* break; */ 85 | /* } */ 86 | /* GPG(err); */ 87 | 88 | /* if (key->uids && strcmp(key->uids->email, "authinfo@test.com") == 0) { */ 89 | /* ck_assert(test_key == NULL); */ 90 | /* test_key = key; */ 91 | /* } else { */ 92 | /* gpgme_key_release(key); */ 93 | /* } */ 94 | /* } */ 95 | 96 | /* if (test_key) { */ 97 | /* GPG(gpgme_op_delete(ctx, test_key, 1)); */ 98 | /* gpgme_key_release(test_key); */ 99 | /* } */ 100 | 101 | /* gpgme_release(ctx); */ 102 | } 103 | 104 | #define TEST(name) \ 105 | START_TEST(test_gpg_##name); \ 106 | fprintf(stderr, "==Test: %s=====================\n", #name); 107 | 108 | TEST(read_file) 109 | { 110 | struct authinfo_data_t *plain_data; 111 | struct authinfo_data_t *encrypted_data; 112 | 113 | const char *plain; 114 | const char *encrypted; 115 | 116 | size_t plain_size; 117 | size_t encrypted_size; 118 | 119 | AUTHINFO(authinfo_data_from_file( 120 | TOP_SRCDIR "/tests/files/gpg_tests/read_file.gpg", 121 | &encrypted_data)); 122 | AUTHINFO(authinfo_data_from_file( 123 | TOP_SRCDIR "/tests/files/gpg_tests/read_file", &plain_data)); 124 | 125 | authinfo_data_get_mem(encrypted_data, &encrypted, &encrypted_size); 126 | authinfo_data_get_mem(plain_data, &plain, &plain_size); 127 | 128 | ck_assert_uint_eq(plain_size, encrypted_size); 129 | ck_assert(memcmp(plain, encrypted, plain_size) == 0); 130 | 131 | authinfo_data_free(encrypted_data); 132 | authinfo_data_free(plain_data); 133 | } 134 | END_TEST 135 | 136 | TEST(gpged_password) 137 | { 138 | struct authinfo_data_t *data; 139 | 140 | struct authinfo_parse_entry_t entry; 141 | struct authinfo_parse_error_t error; 142 | const char *password; 143 | 144 | AUTHINFO(authinfo_data_from_file( 145 | TOP_SRCDIR "/tests/files/gpg_tests/gpged_password", &data)); 146 | AUTHINFO(authinfo_simple_query(data, NULL, NULL, NULL, &entry, &error)); 147 | AUTHINFO(authinfo_password_extract(entry.password, &password)); 148 | 149 | ck_assert_str_eq(entry.host, "host"); 150 | ck_assert_str_eq(entry.user, "user"); 151 | ck_assert_str_eq(entry.protocol, "protocol"); 152 | ck_assert_str_eq(password, "password"); 153 | 154 | authinfo_parse_entry_free(&entry); 155 | authinfo_data_free(data); 156 | } 157 | END_TEST 158 | 159 | TEST(gpged_password_bad) 160 | { 161 | struct authinfo_dat_t *data; 162 | 163 | struct authinfo_parse_entry_t entry; 164 | struct authinfo_parse_error_t error; 165 | const char *password; 166 | 167 | AUTHINFO(authinfo_data_from_file( 168 | TOP_SRCDIR "/tests/files/gpg_tests/gpged_password_bad", &data)); 169 | AUTHINFO(authinfo_simple_query(data, NULL, NULL, NULL, &entry, &error)); 170 | ck_assert_int_eq(authinfo_password_extract(entry.password, &password), 171 | AUTHINFO_EGPG_BAD_BASE64); 172 | 173 | authinfo_parse_entry_free(&entry); 174 | authinfo_data_free(data); 175 | } 176 | END_TEST 177 | 178 | Suite * 179 | gpg_suite(void) 180 | { 181 | Suite *s = suite_create("Parsing"); 182 | 183 | #define TEST_CASE(name, desc) \ 184 | TCase *tc_##name = tcase_create(desc); \ 185 | tcase_add_checked_fixture(tc_##name, setup, teardown); \ 186 | tcase_add_test(tc_##name, test_gpg_##name); \ 187 | suite_add_tcase(s, tc_##name); 188 | 189 | TEST_CASE(read_file, "Reading encrypted/unencrypted files"); 190 | TEST_CASE(gpged_password, "Parsing file with encrypted password"); 191 | TEST_CASE(gpged_password_bad, "Parsing file with badly encoded password"); 192 | 193 | return s; 194 | } 195 | 196 | int 197 | main(void) 198 | { 199 | int number_failed; 200 | Suite *s = gpg_suite(); 201 | SRunner *sr = srunner_create(s); 202 | 203 | srunner_run_all(sr, CK_VERBOSE); 204 | number_failed = srunner_ntests_failed(sr); 205 | srunner_free(sr); 206 | 207 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 208 | } 209 | -------------------------------------------------------------------------------- /tests/authinfo_parsing_tests.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; tab-width: 4; ; indent-tabs-mode: nil; -*- */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "authinfo.h" 8 | 9 | #define ITEMS_MAX 50 10 | 11 | struct entry_t { 12 | char *host; 13 | char *protocol; 14 | char *user; 15 | char *password; 16 | bool force; 17 | }; 18 | 19 | static int entries_start; 20 | static int entries_count; 21 | static struct entry_t entries[ITEMS_MAX]; 22 | 23 | static int errors_start; 24 | static int errors_count; 25 | static struct authinfo_parse_error_t errors[ITEMS_MAX]; 26 | 27 | static char * 28 | xstrdup(const char *str) 29 | { 30 | char *ret = NULL; 31 | 32 | if (str) { 33 | ret = strdup(str); 34 | assert(ret); 35 | } 36 | 37 | return ret; 38 | } 39 | 40 | static void 41 | xfree(void *p) 42 | { 43 | if (p) { 44 | free(p); 45 | } 46 | } 47 | 48 | static void 49 | copy_entry(struct entry_t *dst, 50 | const struct authinfo_parse_entry_t *src) 51 | { 52 | dst->host = xstrdup(src->host); 53 | dst->protocol = xstrdup(src->protocol); 54 | dst->user = xstrdup(src->user); 55 | dst->force = src->force; 56 | 57 | if (src->password) { 58 | enum authinfo_result_t ret; 59 | const char *pwdata; 60 | 61 | ret = authinfo_password_extract(src->password, &pwdata); 62 | assert(ret == AUTHINFO_OK); 63 | 64 | dst->password = strdup(pwdata); 65 | } else { 66 | dst->password = NULL; 67 | } 68 | } 69 | 70 | static void 71 | free_entry(struct entry_t *entry) 72 | { 73 | xfree((void *) entry->host); 74 | xfree((void *) entry->protocol); 75 | xfree((void *) entry->user); 76 | xfree((void *) entry->password); 77 | } 78 | 79 | static bool 80 | parse_all_entry_cb(const struct authinfo_parse_entry_t *entry, void *arg) 81 | { 82 | assert(entries_count < ITEMS_MAX); 83 | copy_entry(&entries[entries_count++], entry); 84 | 85 | return false; 86 | } 87 | 88 | static bool 89 | parse_all_error_cb(const struct authinfo_parse_error_t *error, void *arg) 90 | { 91 | assert(errors_count < ITEMS_MAX); 92 | errors[errors_count++] = *error; 93 | 94 | return false; 95 | } 96 | 97 | static void 98 | parse_all(const char *raw_data) 99 | { 100 | enum authinfo_result_t ret; 101 | struct authinfo_data_t *data; 102 | 103 | entries_start = entries_count; 104 | errors_start = errors_count; 105 | 106 | ret = authinfo_data_from_mem(raw_data, strlen(raw_data), &data); 107 | assert(ret == AUTHINFO_OK); 108 | 109 | authinfo_parse(data, NULL, parse_all_entry_cb, parse_all_error_cb); 110 | 111 | authinfo_data_free(data); 112 | } 113 | 114 | static bool 115 | parse_one_entry_cb(const struct authinfo_parse_entry_t *entry, void *arg) 116 | { 117 | assert(entries_count < ITEMS_MAX); 118 | copy_entry(&entries[entries_count++], entry); 119 | 120 | return true; 121 | } 122 | 123 | static bool 124 | parse_one_error_cb(const struct authinfo_parse_error_t *error, void *arg) 125 | { 126 | assert(errors_count < ITEMS_MAX); 127 | errors[errors_count++] = *error; 128 | 129 | return true; 130 | } 131 | 132 | static void 133 | parse_one(const char *raw_data) 134 | { 135 | enum authinfo_result_t ret; 136 | struct authinfo_data_t *data; 137 | 138 | entries_start = entries_count; 139 | errors_start = errors_count; 140 | 141 | ret = authinfo_data_from_mem(raw_data, strlen(raw_data), &data); 142 | assert(ret == AUTHINFO_OK); 143 | 144 | authinfo_parse(data, NULL, parse_one_entry_cb, parse_one_error_cb); 145 | 146 | authinfo_data_free(data); 147 | } 148 | 149 | 150 | static void 151 | dump_entries(void) 152 | { 153 | if (entries_count) { 154 | fprintf(stderr, "Parsed entries:\n"); 155 | for (int i = entries_start; i < entries_count; ++i) { 156 | struct entry_t *e = &entries[i]; 157 | 158 | fprintf(stderr, 159 | "\thost -> '%s', protocol -> '%s', " 160 | "user -> '%s', password -> '%s', force -> %s\n", 161 | e->host, e->protocol, e->user, e->password, 162 | (e->force ? "true" : "false")); 163 | } 164 | } else { 165 | fprintf(stderr, "No parsed entries\n"); 166 | } 167 | } 168 | 169 | static void 170 | dump_errors(void) 171 | { 172 | if (errors_count) { 173 | fprintf(stderr, "Parsing errors:\n"); 174 | for (int i = errors_start; i < errors_count; ++i) { 175 | struct authinfo_parse_error_t *e = &errors[i]; 176 | 177 | fprintf(stderr, "\t%u:%u: %s\n", 178 | e->line, e->column, authinfo_parse_strerror(e->type)); 179 | } 180 | } else { 181 | fprintf(stderr, "No parsing errors\n"); 182 | } 183 | } 184 | 185 | static void 186 | setup(void) 187 | { 188 | entries_start = 0; 189 | entries_count = 0; 190 | errors_start = 0; 191 | errors_count = 0; 192 | 193 | assert(authinfo_init(NULL) == AUTHINFO_OK); 194 | } 195 | 196 | static void 197 | teardown(void) 198 | { 199 | for (int i = 0; i < entries_count; ++i) { 200 | free_entry(&entries[i]); 201 | } 202 | } 203 | 204 | #define NOTE_FAILURE() \ 205 | fprintf(stderr, "FAILURE: %s:%d\n", __func__, __LINE__); 206 | 207 | #define ASSERT_EMPTY() \ 208 | if ((entries_count != entries_start) || (errors_count != errors_start)) { \ 209 | NOTE_FAILURE(); \ 210 | dump_entries(); \ 211 | dump_errors(); \ 212 | ck_abort_msg("Non-empty set of parsed entries or errors"); \ 213 | } 214 | 215 | #define ASSERT_PARSES_COUNT(count) \ 216 | if ((entries_count - entries_start != (count))) { \ 217 | NOTE_FAILURE(); \ 218 | dump_entries(); \ 219 | dump_errors(); \ 220 | ck_abort_msg("Number of parsed entries differs from expected (%d)", (count)); \ 221 | } 222 | 223 | #define ASSERT_STREQ(x, y) \ 224 | if ((x) != NULL && (y) != NULL) { \ 225 | ck_assert_str_eq((x), (y)); \ 226 | } else { \ 227 | ck_assert_ptr_eq((void *)(x), (void *)(y)); \ 228 | } 229 | 230 | #define ASSERT_ENTRY(entry, host_, user_, password_, protocol_, force_) \ 231 | ASSERT_STREQ((entry).host, host_); \ 232 | ASSERT_STREQ((entry).user, user_); \ 233 | ASSERT_STREQ((entry).password, password_); \ 234 | ASSERT_STREQ((entry).protocol, protocol_); \ 235 | ck_assert_int_eq((entry).force, force_); 236 | 237 | #define ASSERT_NTH_ENTRY(number, host, user, password, protocol, force) \ 238 | ASSERT_ENTRY(entries[entries_start + (number)], \ 239 | host, user, password, protocol, force); 240 | 241 | #define ASSERT_SINGLE_ENTRY(host, user, password, protocol, force) \ 242 | ASSERT_PARSES_COUNT(1); \ 243 | ASSERT_ERRORS_COUNT(0); \ 244 | ASSERT_NTH_ENTRY(0, host, user, password, protocol, force); 245 | 246 | #define ASSERT_ERRORS_COUNT(count) \ 247 | if (errors_count - errors_start != (count)) { \ 248 | NOTE_FAILURE(); \ 249 | dump_entries(); \ 250 | dump_errors(); \ 251 | ck_abort_msg("Number of parsing errors differs from expected (%d)", (count)); \ 252 | } 253 | 254 | #define ASSERT_NTH_ERROR(number, type_) \ 255 | if (errors[errors_start + (number)].type != (type_)) { \ 256 | ck_abort_msg("Error type (%s) differs from expected (%s)", \ 257 | authinfo_parse_strerror(errors[errors_start + (number)].type), \ 258 | authinfo_parse_strerror((type_))); \ 259 | } 260 | 261 | #define ASSERT_SINGLE_ERROR(type) \ 262 | ASSERT_ERRORS_COUNT(1); \ 263 | ASSERT_NTH_ERROR(0, type); 264 | 265 | #define TEST(name) \ 266 | START_TEST(test_parse_##name); \ 267 | fprintf(stderr, "==Test: %s=====================\n", #name); 268 | 269 | TEST(empty) 270 | { 271 | parse_all(""); 272 | ASSERT_EMPTY(); 273 | 274 | parse_all(" "); 275 | ASSERT_EMPTY(); 276 | 277 | parse_all("\t"); 278 | ASSERT_EMPTY(); 279 | 280 | parse_all("\n"); 281 | ASSERT_EMPTY(); 282 | 283 | parse_all(" \n"); 284 | ASSERT_EMPTY(); 285 | 286 | parse_all("\t\n"); 287 | ASSERT_EMPTY(); 288 | 289 | parse_all("\n\n"); 290 | ASSERT_EMPTY(); 291 | 292 | parse_all("\t\n \n\t "); 293 | ASSERT_EMPTY(); 294 | } 295 | END_TEST 296 | 297 | TEST(comment_basic) 298 | { 299 | parse_all("# commented line only"); 300 | ASSERT_EMPTY(); 301 | 302 | parse_all("# commented line only\n"); 303 | ASSERT_EMPTY(); 304 | 305 | parse_all(" # commented line only"); 306 | ASSERT_EMPTY(); 307 | 308 | parse_all(" # commented line only\n"); 309 | ASSERT_EMPTY(); 310 | } 311 | END_TEST 312 | 313 | TEST(macdef_basic) 314 | { 315 | parse_all( 316 | "macdef test\n" 317 | "def"); 318 | ASSERT_EMPTY(); 319 | 320 | parse_all( 321 | "macdef test\n" 322 | "def\n"); 323 | ASSERT_EMPTY(); 324 | 325 | parse_all( 326 | "macdef test\n" 327 | "def\n\n"); 328 | ASSERT_EMPTY(); 329 | } 330 | END_TEST 331 | 332 | TEST(basic) 333 | { 334 | parse_all("host hostname user username " 335 | "password password protocol protocol force yes"); 336 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", "protocol", true); 337 | 338 | /* test synonymous keywords */ 339 | parse_all("machine hostname login username " 340 | "password password port protocol force yes"); 341 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", "protocol", true); 342 | 343 | parse_all("machine hostname account username " 344 | "password password protocol protocol force yes"); 345 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", "protocol", true); 346 | 347 | parse_all("user username " 348 | "password password protocol protocol force yes"); 349 | ASSERT_SINGLE_ENTRY(NULL, "username", "password", "protocol", true); 350 | 351 | parse_all("default user username " 352 | "password password protocol protocol force yes"); 353 | ASSERT_SINGLE_ENTRY(NULL, "username", "password", "protocol", true); 354 | 355 | /* everything except host can be omitted */ 356 | parse_all("host hostname user username " 357 | "password password protocol protocol"); 358 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", "protocol", false); 359 | 360 | parse_all("host hostname user username " 361 | "password password force yes"); 362 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", NULL, true); 363 | 364 | parse_all("host hostname user username " 365 | "protocol protocol force yes"); 366 | ASSERT_SINGLE_ENTRY("hostname", "username", NULL, "protocol", true); 367 | 368 | parse_all("host hostname " 369 | "password password protocol protocol force yes"); 370 | ASSERT_SINGLE_ENTRY("hostname", NULL, "password", "protocol", true); 371 | 372 | parse_all("host hostname user username " 373 | "password password"); 374 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", NULL, false); 375 | 376 | parse_all("host hostname user username"); 377 | ASSERT_SINGLE_ENTRY("hostname", "username", NULL, NULL, false); 378 | 379 | parse_all("host hostname"); 380 | ASSERT_SINGLE_ENTRY("hostname", NULL, NULL, NULL, false); 381 | } 382 | END_TEST 383 | 384 | TEST(quoted) 385 | { 386 | parse_all("host hostname user username password \"password\""); 387 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", NULL, false); 388 | 389 | parse_all("host hostname user username password \"pass word\""); 390 | ASSERT_SINGLE_ENTRY("hostname", "username", "pass word", NULL, false); 391 | 392 | parse_all("host hostname user username password \"pass \\\"word\""); 393 | ASSERT_SINGLE_ENTRY("hostname", "username", "pass \"word", NULL, false); 394 | 395 | parse_all("host hostname user username password \"pass \\\"\\\\word\""); 396 | ASSERT_SINGLE_ENTRY("hostname", "username", "pass \"\\word", NULL, false); 397 | 398 | parse_all("host hostname user username password \"pass \\\"\\\\\""); 399 | ASSERT_SINGLE_ENTRY("hostname", "username", "pass \"\\", NULL, false); 400 | 401 | parse_all("host hostname user username password \" \\\"\\\\\""); 402 | ASSERT_SINGLE_ENTRY("hostname", "username", " \"\\", NULL, false); 403 | 404 | parse_all("host hostname user username password \"\\\"\\\\\""); 405 | ASSERT_SINGLE_ENTRY("hostname", "username", "\"\\", NULL, false); 406 | } 407 | END_TEST 408 | 409 | TEST(multi_entry) 410 | { 411 | parse_all( 412 | "host hostname user username " 413 | "password password protocol protocol force yes\n" 414 | 415 | "machine hostname login username " 416 | "password password port protocol force yes\n" 417 | 418 | "machine hostname account username " 419 | "password password protocol protocol force yes\n" 420 | 421 | "#comment\n" 422 | 423 | "user username " 424 | "password password protocol protocol force yes\n" 425 | 426 | "default user username " 427 | "password password protocol protocol force yes\n" 428 | 429 | "host hostname user username " 430 | "password password protocol protocol\n" 431 | 432 | " #comment\n" 433 | 434 | "host hostname user username " 435 | "password password force yes\n" 436 | 437 | "host hostname user username " 438 | "protocol protocol force yes\n" 439 | 440 | "host hostname " 441 | "password password protocol protocol force yes\n" 442 | 443 | "macdef test\n" 444 | "def\n" 445 | "\n" 446 | 447 | "host hostname user username " 448 | "password password\n" 449 | 450 | "host hostname user username\n" 451 | 452 | "host hostname\n" 453 | 454 | "host hostname user username password \"password\" \n" 455 | 456 | "host hostname user username password \"pass word\" \n" 457 | 458 | "host hostname user username password \"pass \\\"word\" \n" 459 | 460 | "host hostname user username password \"pass \\\"\\\\word\" \n" 461 | 462 | "host hostname user username password \"pass \\\"\\\\\" \n" 463 | 464 | "host hostname user username password \" \\\"\\\\\" \n" 465 | 466 | "host hostname user username password \"\\\"\\\\\" \n" 467 | 468 | " # find a cool macdef on the next line\n" 469 | "macdef cool\n" 470 | "really cool"); 471 | 472 | ASSERT_PARSES_COUNT(19); 473 | ASSERT_ERRORS_COUNT(0); 474 | 475 | ASSERT_NTH_ENTRY(0, "hostname", "username", "password", "protocol", true); 476 | ASSERT_NTH_ENTRY(1, "hostname", "username", "password", "protocol", true); 477 | ASSERT_NTH_ENTRY(2, "hostname", "username", "password", "protocol", true); 478 | ASSERT_NTH_ENTRY(3, NULL, "username", "password", "protocol", true); 479 | ASSERT_NTH_ENTRY(4, NULL, "username", "password", "protocol", true); 480 | ASSERT_NTH_ENTRY(5, "hostname", "username", "password", "protocol", false); 481 | ASSERT_NTH_ENTRY(6, "hostname", "username", "password", NULL, true); 482 | ASSERT_NTH_ENTRY(7, "hostname", "username", NULL, "protocol", true); 483 | ASSERT_NTH_ENTRY(8, "hostname", NULL, "password", "protocol", true); 484 | ASSERT_NTH_ENTRY(9, "hostname", "username", "password", NULL, false); 485 | ASSERT_NTH_ENTRY(10, "hostname", "username", NULL, NULL, false); 486 | ASSERT_NTH_ENTRY(11, "hostname", NULL, NULL, NULL, false); 487 | ASSERT_NTH_ENTRY(12, "hostname", "username", "password", NULL, false); 488 | ASSERT_NTH_ENTRY(13, "hostname", "username", "pass word", NULL, false); 489 | ASSERT_NTH_ENTRY(14, "hostname", "username", "pass \"word", NULL, false); 490 | ASSERT_NTH_ENTRY(15, "hostname", "username", "pass \"\\word", NULL, false); 491 | ASSERT_NTH_ENTRY(16, "hostname", "username", "pass \"\\", NULL, false); 492 | ASSERT_NTH_ENTRY(17, "hostname", "username", " \"\\", NULL, false); 493 | ASSERT_NTH_ENTRY(18, "hostname", "username", "\"\\", NULL, false); 494 | } 495 | END_TEST 496 | 497 | TEST(errors) 498 | { 499 | parse_all("host hostname user username " 500 | "password password protocol"); 501 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_MISSING_VALUE); 502 | 503 | parse_all("host hostname user username " 504 | "password password protocol protocol force true"); 505 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_BAD_VALUE); 506 | 507 | parse_all("host hostname userr user username " 508 | "password password protocol protocol force yes"); 509 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_BAD_KEYWORD); 510 | 511 | parse_all("host hostname user username account username " 512 | "password password protocol protocol force yes"); 513 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_DUPLICATED_KEYWORD); 514 | 515 | parse_all("host hostname user username " 516 | "password \"password protocol protocol force yes"); 517 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_UNTERMINATED_QUOTED_TOKEN); 518 | 519 | parse_all("host hostname user username " 520 | "password \"pass\\word\" protocol protocol force yes"); 521 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_UNSUPPORTED_ESCAPE); 522 | } 523 | END_TEST 524 | 525 | TEST(first_only) 526 | { 527 | parse_one( 528 | "# comment\n" 529 | 530 | "macdef test\n" 531 | "test\n" 532 | "\n" 533 | 534 | "host hostname user username " 535 | "password password protocol protocol force yes\n" 536 | 537 | "machine hostname login username " 538 | "password password port protocol force yes\n" 539 | 540 | "machine hostname account username " 541 | "password password protocol protocol force yes\n"); 542 | 543 | ASSERT_SINGLE_ENTRY("hostname", "username", "password", "protocol", true); 544 | 545 | parse_one( 546 | "# comment\n" 547 | 548 | "macdef test\n" 549 | "test\n" 550 | "\n" 551 | 552 | "host hostname user username " 553 | "password password protocol protocol force true"); 554 | 555 | ASSERT_PARSES_COUNT(0); 556 | ASSERT_SINGLE_ERROR(AUTHINFO_PET_BAD_VALUE); 557 | 558 | } 559 | END_TEST 560 | 561 | Suite * 562 | parsing_suite(void) 563 | { 564 | Suite *s = suite_create("Parsing"); 565 | 566 | #define TEST_CASE(name, desc) \ 567 | TCase *tc_##name = tcase_create(desc); \ 568 | tcase_add_checked_fixture(tc_##name, setup, teardown); \ 569 | tcase_add_test(tc_##name, test_parse_##name); \ 570 | suite_add_tcase(s, tc_##name); 571 | 572 | TEST_CASE(empty, "Parsing empty file"); 573 | TEST_CASE(comment_basic, "Basic comment parsing"); 574 | TEST_CASE(macdef_basic, "Basic macdef parsing"); 575 | TEST_CASE(basic, "Basic entry parsing"); 576 | TEST_CASE(quoted, "Parsing quoted tokens"); 577 | TEST_CASE(multi_entry, "Parsing several entries"); 578 | TEST_CASE(errors, "Parsing errors test"); 579 | TEST_CASE(first_only, "Getting only first parse or error"); 580 | 581 | #undef TEST_CASE 582 | 583 | return s; 584 | } 585 | 586 | int 587 | main(void) 588 | { 589 | int number_failed; 590 | Suite *s = parsing_suite(); 591 | SRunner *sr = srunner_create(s); 592 | 593 | srunner_run_all(sr, CK_VERBOSE); 594 | number_failed = srunner_ntests_failed(sr); 595 | srunner_free(sr); 596 | 597 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 598 | } 599 | -------------------------------------------------------------------------------- /tests/files/gpg_tests/gpged_password: -------------------------------------------------------------------------------- 1 | host host protocol protocol user user password gpg:hQEMA3caoW+PjyXcAQf+PLT99tAmj9PkePr6a3zsawlYZ84a8e+Q377BHROdgVCi8h9FI2+IoGVpms5ezQvjEhjs+KQUJBKJNQIxs0LErzVQyIKbGNwmnIDpIk40fZMW+SWEo6jWlnVtKPPWzDTZVfa1xAIk2S4cYxmX8VqeWKcQLb9YS8Hj7VFddwR/ao6V69W9+IFmzqpKzX3R9Oz3sgN2K4Kh7EaOyQ2yvvEMYeMYe5zI3KYT3SSrVuOf9LT/N7MSWfC/0FYFrmcJhDensdLVxKuJ+CcxNL5K1lSuqdus0DLWdR9H2iPnuifTpWnSb91gPS+eAHZoMnZtqZ5aDcQZkT4jyRmZbNjKbaSlCNJDAYujSB4C7aqrKwPz2M0RoY47wLSGfHDVgvUNaXn4eNUEjRik8hdKxMSVy/ioL0/H6PS98Q7awNPDMR0PQdPSDDqasQ== 2 | -------------------------------------------------------------------------------- /tests/files/gpg_tests/gpged_password_bad: -------------------------------------------------------------------------------- 1 | host host protocol protocol user user password gpg:???MA3caoW+PjyXcAQf+PLT99tAmj9PkePr6a3zsawlYZ84a8e+Q377BHROdgVCi8h9FI2+IoGVpms5ezQvjEhjs+KQUJBKJNQIxs0LErzVQyIKbGNwmnIDpIk40fZMW+SWEo6jWlnVtKPPWzDTZVfa1xAIk2S4cYxmX8VqeWKcQLb9YS8Hj7VFddwR/ao6V69W9+IFmzqpKzX3R9Oz3sgN2K4Kh7EaOyQ2yvvEMYeMYe5zI3KYT3SSrVuOf9LT/N7MSWfC/0FYFrmcJhDensdLVxKuJ+CcxNL5K1lSuqdus0DLWdR9H2iPnuifTpWnSb91gPS+eAHZoMnZtqZ5aDcQZkT4jyRmZbNjKbaSlCNJDAYujSB4C7aqrKwPz2M0RoY47wLSGfHDVgvUNaXn4eNUEjRik8hdKxMSVy/ioL0/H6PS98Q7awNPDMR0PQdPSDDqasQ== 2 | -------------------------------------------------------------------------------- /tests/files/gpg_tests/private.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aartamonau/authinfo/0098545479fa81234ebc1c2951610cb8c70f5c44/tests/files/gpg_tests/private.key -------------------------------------------------------------------------------- /tests/files/gpg_tests/public.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aartamonau/authinfo/0098545479fa81234ebc1c2951610cb8c70f5c44/tests/files/gpg_tests/public.key -------------------------------------------------------------------------------- /tests/files/gpg_tests/read_file: -------------------------------------------------------------------------------- 1 | read_file succeeded 2 | -------------------------------------------------------------------------------- /tests/files/gpg_tests/read_file.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aartamonau/authinfo/0098545479fa81234ebc1c2951610cb8c70f5c44/tests/files/gpg_tests/read_file.gpg --------------------------------------------------------------------------------