├── .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
--------------------------------------------------------------------------------