Line data Source code
1 : /* engine-gpgconf.c - gpg-conf engine.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
4 : 2013 g10 Code GmbH
5 :
6 : This file is part of GPGME.
7 :
8 : GPGME is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as
10 : published by the Free Software Foundation; either version 2.1 of
11 : the License, or (at your option) any later version.
12 :
13 : GPGME is distributed in the hope that it will be useful, but
14 : 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 this program; if not, see <https://www.gnu.org/licenses/>.
20 : */
21 :
22 : #if HAVE_CONFIG_H
23 : #include <config.h>
24 : #endif
25 :
26 : #include <stdlib.h>
27 : #include <string.h>
28 : #ifdef HAVE_SYS_TYPES_H
29 : # include <sys/types.h>
30 : #endif
31 : #include <assert.h>
32 : #ifdef HAVE_UNISTD_H
33 : # include <unistd.h>
34 : #endif
35 : #include <fcntl.h> /* FIXME */
36 : #include <errno.h>
37 :
38 : #include "gpgme.h"
39 : #include "util.h"
40 : #include "ops.h"
41 : #include "wait.h"
42 : #include "priv-io.h"
43 : #include "sema.h"
44 :
45 : #include "assuan.h"
46 : #include "debug.h"
47 :
48 : #include "engine-backend.h"
49 :
50 :
51 :
52 : struct engine_gpgconf
53 : {
54 : char *file_name;
55 : char *home_dir;
56 : char *version;
57 : };
58 :
59 : typedef struct engine_gpgconf *engine_gpgconf_t;
60 :
61 :
62 : /* Return true if the engine's version is at least VERSION. */
63 : static int
64 1 : have_gpgconf_version (engine_gpgconf_t gpgconf, const char *version)
65 : {
66 1 : return _gpgme_compare_versions (gpgconf->version, version);
67 : }
68 :
69 :
70 : static char *
71 119 : gpgconf_get_version (const char *file_name)
72 : {
73 119 : return _gpgme_get_program_version (file_name ? file_name
74 : : _gpgme_get_default_gpgconf_name ());
75 : }
76 :
77 :
78 : static const char *
79 117 : gpgconf_get_req_version (void)
80 : {
81 117 : return "2.0.4";
82 : }
83 :
84 :
85 : static void
86 198 : gpgconf_release (void *engine)
87 : {
88 198 : engine_gpgconf_t gpgconf = engine;
89 :
90 198 : if (!gpgconf)
91 0 : return;
92 :
93 198 : if (gpgconf->file_name)
94 198 : free (gpgconf->file_name);
95 198 : if (gpgconf->home_dir)
96 1 : free (gpgconf->home_dir);
97 198 : if (gpgconf->version)
98 198 : free (gpgconf->version);
99 :
100 198 : free (gpgconf);
101 : }
102 :
103 :
104 : static gpgme_error_t
105 200 : gpgconf_new (void **engine, const char *file_name, const char *home_dir,
106 : const char *version)
107 : {
108 200 : gpgme_error_t err = 0;
109 : engine_gpgconf_t gpgconf;
110 :
111 200 : gpgconf = calloc (1, sizeof *gpgconf);
112 200 : if (!gpgconf)
113 0 : return gpg_error_from_syserror ();
114 :
115 200 : gpgconf->file_name = strdup (file_name ? file_name
116 : : _gpgme_get_default_gpgconf_name ());
117 200 : if (!gpgconf->file_name)
118 0 : err = gpg_error_from_syserror ();
119 :
120 200 : if (!err && home_dir)
121 : {
122 1 : gpgconf->home_dir = strdup (home_dir);
123 1 : if (!gpgconf->home_dir)
124 0 : err = gpg_error_from_syserror ();
125 : }
126 :
127 200 : if (!err && version)
128 : {
129 200 : gpgconf->version = strdup (version);
130 200 : if (!gpgconf->version)
131 0 : err = gpg_error_from_syserror ();
132 : }
133 :
134 200 : if (err)
135 0 : gpgconf_release (gpgconf);
136 : else
137 200 : *engine = gpgconf;
138 :
139 200 : return err;
140 : }
141 :
142 :
143 : static void
144 37793 : release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
145 : {
146 78091 : while (arg)
147 : {
148 2505 : gpgme_conf_arg_t next = arg->next;
149 :
150 2505 : if (alt_type == GPGME_CONF_STRING)
151 1269 : free (arg->value.string);
152 2505 : free (arg);
153 2505 : arg = next;
154 : }
155 37793 : }
156 :
157 :
158 : static void
159 5760 : release_opt (gpgme_conf_opt_t opt)
160 : {
161 5760 : if (opt->name)
162 5760 : free (opt->name);
163 5760 : if (opt->description)
164 4995 : free (opt->description);
165 5760 : if (opt->argname)
166 1890 : free (opt->argname);
167 :
168 5760 : release_arg (opt->default_value, opt->alt_type);
169 5760 : if (opt->default_description)
170 0 : free (opt->default_description);
171 :
172 5760 : release_arg (opt->no_arg_value, opt->alt_type);
173 5760 : release_arg (opt->value, opt->alt_type);
174 5760 : release_arg (opt->new_value, opt->alt_type);
175 :
176 5760 : free (opt);
177 5760 : }
178 :
179 :
180 : static void
181 270 : release_comp (gpgme_conf_comp_t comp)
182 : {
183 : gpgme_conf_opt_t opt;
184 :
185 270 : if (comp->name)
186 270 : free (comp->name);
187 270 : if (comp->description)
188 270 : free (comp->description);
189 270 : if (comp->program_name)
190 270 : free (comp->program_name);
191 :
192 270 : opt = comp->options;
193 6300 : while (opt)
194 : {
195 5760 : gpgme_conf_opt_t next = opt->next;
196 5760 : release_opt (opt);
197 5760 : opt = next;
198 : }
199 :
200 270 : free (comp);
201 270 : }
202 :
203 :
204 : static void
205 155 : gpgconf_config_release (gpgme_conf_comp_t conf)
206 : {
207 580 : while (conf)
208 : {
209 270 : gpgme_conf_comp_t next = conf->next;
210 270 : release_comp (conf);
211 270 : conf = next;
212 : }
213 155 : }
214 :
215 : /* Read from gpgconf and pass line after line to the hook function.
216 : We put a limit of 64 k on the maximum size for a line. This should
217 : allow for quite a long "group" line, which is usually the longest
218 : line (mine is currently ~3k). */
219 : static gpgme_error_t
220 324 : gpgconf_read (void *engine, const char *arg1, char *arg2,
221 : gpgme_error_t (*cb) (void *hook, char *line),
222 : void *hook)
223 : {
224 324 : struct engine_gpgconf *gpgconf = engine;
225 324 : gpgme_error_t err = 0;
226 : char *linebuf;
227 : size_t linebufsize;
228 : int linelen;
229 : char *argv[6];
230 324 : int argc = 0;
231 : int rp[2];
232 324 : struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
233 : {-1, -1} };
234 : int status;
235 : int nread;
236 324 : char *mark = NULL;
237 :
238 : /* _gpgme_engine_new guarantees that this is not NULL. */
239 324 : argv[argc++] = gpgconf->file_name;
240 :
241 324 : if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
242 : {
243 1 : argv[argc++] = (char*)"--homedir";
244 1 : argv[argc++] = gpgconf->home_dir;
245 : }
246 :
247 324 : argv[argc++] = (char*)arg1;
248 324 : argv[argc++] = arg2;
249 324 : argv[argc] = NULL;
250 324 : assert (argc < DIM (argv));
251 :
252 324 : if (_gpgme_io_pipe (rp, 1) < 0)
253 0 : return gpg_error_from_syserror ();
254 :
255 324 : cfd[0].fd = rp[1];
256 :
257 324 : status = _gpgme_io_spawn (gpgconf->file_name, argv,
258 : IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
259 324 : if (status < 0)
260 : {
261 0 : _gpgme_io_close (rp[0]);
262 0 : _gpgme_io_close (rp[1]);
263 0 : return gpg_error_from_syserror ();
264 : }
265 :
266 324 : linebufsize = 1024; /* Usually enough for conf lines. */
267 324 : linebuf = malloc (linebufsize);
268 324 : if (!linebuf)
269 : {
270 0 : err = gpg_error_from_syserror ();
271 0 : goto leave;
272 : }
273 324 : linelen = 0;
274 :
275 1154 : while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
276 830 : linebufsize - linelen - 1)))
277 : {
278 : char *line;
279 508 : const char *lastmark = NULL;
280 : size_t nused;
281 :
282 508 : if (nread < 0)
283 : {
284 0 : err = gpg_error_from_syserror ();
285 0 : goto leave;
286 : }
287 :
288 508 : linelen += nread;
289 508 : linebuf[linelen] = '\0';
290 :
291 6694 : for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
292 : {
293 6188 : lastmark = mark;
294 6188 : if (mark > line && mark[-1] == '\r')
295 0 : mark[-1] = '\0';
296 : else
297 6188 : mark[0] = '\0';
298 :
299 : /* Got a full line. Due to the CR removal code (which
300 : occurs only on Windows) we might be one-off and thus
301 : would see empty lines. Don't pass them to the
302 : callback. */
303 6188 : err = *line? (*cb) (hook, line) : 0;
304 6188 : if (err)
305 2 : goto leave;
306 : }
307 :
308 506 : nused = lastmark? (lastmark + 1 - linebuf) : 0;
309 506 : memmove (linebuf, linebuf + nused, linelen - nused);
310 506 : linelen -= nused;
311 :
312 506 : if (!(linelen < linebufsize - 1))
313 : {
314 : char *newlinebuf;
315 :
316 0 : if (linelen < 8 * 1024 - 1)
317 0 : linebufsize = 8 * 1024;
318 0 : else if (linelen < 64 * 1024 - 1)
319 0 : linebufsize = 64 * 1024;
320 : else
321 : {
322 : /* We reached our limit - give up. */
323 0 : err = gpg_error (GPG_ERR_LINE_TOO_LONG);
324 0 : goto leave;
325 : }
326 :
327 0 : newlinebuf = realloc (linebuf, linebufsize);
328 0 : if (!newlinebuf)
329 : {
330 0 : err = gpg_error_from_syserror ();
331 0 : goto leave;
332 : }
333 0 : linebuf = newlinebuf;
334 : }
335 : }
336 :
337 : leave:
338 324 : free (linebuf);
339 324 : _gpgme_io_close (rp[0]);
340 324 : return err;
341 : }
342 :
343 :
344 : static gpgme_error_t
345 276 : gpgconf_config_load_cb (void *hook, char *line)
346 : {
347 276 : gpgme_conf_comp_t *comp_p = hook;
348 276 : gpgme_conf_comp_t comp = *comp_p;
349 : #define NR_FIELDS 16
350 : char *field[NR_FIELDS];
351 276 : int fields = 0;
352 :
353 1380 : while (line && fields < NR_FIELDS)
354 : {
355 828 : field[fields++] = line;
356 828 : line = strchr (line, ':');
357 828 : if (line)
358 552 : *(line++) = '\0';
359 : }
360 :
361 : /* We require at least the first 3 fields. */
362 276 : if (fields < 2)
363 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
364 :
365 : /* Find the pointer to the new component in the list. */
366 1012 : while (comp && comp->next)
367 460 : comp = comp->next;
368 276 : if (comp)
369 230 : comp_p = &comp->next;
370 :
371 276 : comp = calloc (1, sizeof (*comp));
372 276 : if (!comp)
373 0 : return gpg_error_from_syserror ();
374 : /* Prepare return value. */
375 276 : comp->_last_opt_p = &comp->options;
376 276 : *comp_p = comp;
377 :
378 276 : comp->name = strdup (field[0]);
379 276 : if (!comp->name)
380 0 : return gpg_error_from_syserror ();
381 :
382 276 : comp->description = strdup (field[1]);
383 276 : if (!comp->description)
384 0 : return gpg_error_from_syserror ();
385 :
386 276 : if (fields >= 3)
387 : {
388 276 : comp->program_name = strdup (field[2]);
389 276 : if (!comp->program_name)
390 0 : return gpg_error_from_syserror ();
391 : }
392 :
393 276 : return 0;
394 : }
395 :
396 :
397 : static gpgme_error_t
398 13156 : gpgconf_parse_option (gpgme_conf_opt_t opt,
399 : gpgme_conf_arg_t *arg_p, char *line)
400 : {
401 : gpgme_error_t err;
402 13156 : char *mark = NULL;
403 :
404 13156 : if (!line[0])
405 11896 : return 0;
406 :
407 3780 : while (line)
408 : {
409 : gpgme_conf_arg_t arg;
410 :
411 1260 : if (opt->type != GPGME_CONF_STRING)
412 641 : mark = strchr (line, ',');
413 1260 : if (mark)
414 0 : *mark = '\0';
415 :
416 1260 : arg = calloc (1, sizeof (*arg));
417 1260 : if (!arg)
418 0 : return gpg_error_from_syserror ();
419 1260 : *arg_p = arg;
420 1260 : arg_p = &arg->next;
421 :
422 1260 : if (*line == '\0')
423 0 : arg->no_arg = 1;
424 : else
425 : {
426 1260 : switch (opt->alt_type)
427 : {
428 : /* arg->value.count is an alias for arg->value.uint32. */
429 : case GPGME_CONF_NONE:
430 : case GPGME_CONF_UINT32:
431 549 : arg->value.uint32 = strtoul (line, NULL, 0);
432 549 : break;
433 :
434 : case GPGME_CONF_INT32:
435 92 : arg->value.uint32 = strtol (line, NULL, 0);
436 92 : break;
437 :
438 : case GPGME_CONF_STRING:
439 : /* The complex types below are only here to silent the
440 : compiler warning. */
441 : case GPGME_CONF_FILENAME:
442 : case GPGME_CONF_LDAP_SERVER:
443 : case GPGME_CONF_KEY_FPR:
444 : case GPGME_CONF_PUB_KEY:
445 : case GPGME_CONF_SEC_KEY:
446 : case GPGME_CONF_ALIAS_LIST:
447 : /* Skip quote character. */
448 619 : line++;
449 :
450 619 : err = _gpgme_decode_percent_string (line, &arg->value.string,
451 : 0, 0);
452 619 : if (err)
453 0 : return err;
454 619 : break;
455 : }
456 : }
457 :
458 : /* Find beginning of next value. */
459 1260 : if (mark++ && *mark)
460 0 : line = mark;
461 : else
462 1260 : line = NULL;
463 : }
464 :
465 1260 : return 0;
466 : }
467 :
468 :
469 : static gpgme_error_t
470 5888 : gpgconf_config_load_cb2 (void *hook, char *line)
471 : {
472 : gpgme_error_t err;
473 5888 : gpgme_conf_comp_t comp = hook;
474 5888 : gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
475 : gpgme_conf_opt_t opt;
476 : #define NR_FIELDS 16
477 : char *field[NR_FIELDS];
478 5888 : int fields = 0;
479 :
480 70656 : while (line && fields < NR_FIELDS)
481 : {
482 58880 : field[fields++] = line;
483 58880 : line = strchr (line, ':');
484 58880 : if (line)
485 52992 : *(line++) = '\0';
486 : }
487 :
488 : /* We require at least the first 10 fields. */
489 5888 : if (fields < 10)
490 0 : return trace_gpg_error (GPG_ERR_INV_ENGINE);
491 :
492 5888 : opt = calloc (1, sizeof (*opt));
493 5888 : if (!opt)
494 0 : return gpg_error_from_syserror ();
495 :
496 5888 : comp->_last_opt_p = &opt->next;
497 5888 : *opt_p = opt;
498 :
499 5888 : if (field[0][0])
500 : {
501 5888 : opt->name = strdup (field[0]);
502 5888 : if (!opt->name)
503 0 : return gpg_error_from_syserror ();
504 : }
505 :
506 5888 : opt->flags = strtoul (field[1], NULL, 0);
507 :
508 5888 : opt->level = strtoul (field[2], NULL, 0);
509 :
510 5888 : if (field[3][0])
511 : {
512 5106 : opt->description = strdup (field[3]);
513 5106 : if (!opt->description)
514 0 : return gpg_error_from_syserror ();
515 : }
516 :
517 5888 : opt->type = strtoul (field[4], NULL, 0);
518 :
519 5888 : opt->alt_type = strtoul (field[5], NULL, 0);
520 :
521 5888 : if (field[6][0])
522 : {
523 1932 : opt->argname = strdup (field[6]);
524 1932 : if (!opt->argname)
525 0 : return gpg_error_from_syserror ();
526 : }
527 :
528 5888 : if (opt->flags & GPGME_CONF_DEFAULT)
529 : {
530 1380 : err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
531 1380 : if (err)
532 0 : return err;
533 : }
534 4508 : else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
535 : {
536 0 : opt->default_description = strdup (field[7]);
537 0 : if (!opt->default_description)
538 0 : return gpg_error_from_syserror ();
539 : }
540 :
541 5888 : if (opt->flags & GPGME_CONF_NO_ARG_DESC)
542 : {
543 0 : opt->no_arg_description = strdup (field[8]);
544 0 : if (!opt->no_arg_description)
545 0 : return gpg_error_from_syserror ();
546 : }
547 : else
548 : {
549 5888 : err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
550 5888 : if (err)
551 0 : return err;
552 : }
553 :
554 5888 : err = gpgconf_parse_option (opt, &opt->value, field[9]);
555 5888 : if (err)
556 0 : return err;
557 :
558 5888 : return 0;
559 : }
560 :
561 :
562 : static gpgme_error_t
563 46 : gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
564 : {
565 : gpgme_error_t err;
566 46 : gpgme_conf_comp_t comp = NULL;
567 : gpgme_conf_comp_t cur_comp;
568 :
569 46 : *comp_p = NULL;
570 :
571 46 : err = gpgconf_read (engine, "--list-components", NULL,
572 : gpgconf_config_load_cb, &comp);
573 46 : if (err)
574 : {
575 0 : gpgconf_release (comp);
576 0 : return err;
577 : }
578 :
579 46 : cur_comp = comp;
580 368 : while (!err && cur_comp)
581 : {
582 276 : err = gpgconf_read (engine, "--list-options", cur_comp->name,
583 : gpgconf_config_load_cb2, cur_comp);
584 276 : cur_comp = cur_comp->next;
585 : }
586 :
587 46 : if (err)
588 : {
589 0 : gpgconf_release (comp);
590 0 : return err;
591 : }
592 :
593 46 : *comp_p = comp;
594 46 : return 0;
595 : }
596 :
597 :
598 :
599 : gpgme_error_t
600 1271 : _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
601 : gpgme_conf_type_t type, const void *value)
602 : {
603 : gpgme_conf_arg_t arg;
604 :
605 1271 : arg = calloc (1, sizeof (*arg));
606 1271 : if (!arg)
607 0 : return gpg_error_from_syserror ();
608 :
609 1271 : if (!value)
610 0 : arg->no_arg = 1;
611 : else
612 : {
613 : /* We need to switch on type here because the alt-type is not
614 : yet known. */
615 1271 : switch (type)
616 : {
617 : case GPGME_CONF_NONE:
618 : case GPGME_CONF_UINT32:
619 516 : arg->value.uint32 = *((unsigned int *) value);
620 516 : break;
621 :
622 : case GPGME_CONF_INT32:
623 92 : arg->value.int32 = *((int *) value);
624 92 : break;
625 :
626 : case GPGME_CONF_STRING:
627 : case GPGME_CONF_FILENAME:
628 : case GPGME_CONF_LDAP_SERVER:
629 : case GPGME_CONF_KEY_FPR:
630 : case GPGME_CONF_PUB_KEY:
631 : case GPGME_CONF_SEC_KEY:
632 : case GPGME_CONF_ALIAS_LIST:
633 663 : arg->value.string = strdup (value);
634 663 : if (!arg->value.string)
635 : {
636 0 : free (arg);
637 0 : return gpg_error_from_syserror ();
638 : }
639 663 : break;
640 :
641 : default:
642 0 : free (arg);
643 0 : return gpg_error (GPG_ERR_INV_VALUE);
644 : }
645 : }
646 :
647 1271 : *arg_p = arg;
648 1271 : return 0;
649 : }
650 :
651 :
652 : void
653 14753 : _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
654 : {
655 : /* Lacking the alt_type we need to switch on type here. */
656 14753 : switch (type)
657 : {
658 : case GPGME_CONF_NONE:
659 : case GPGME_CONF_UINT32:
660 : case GPGME_CONF_INT32:
661 : case GPGME_CONF_STRING:
662 : default:
663 14753 : break;
664 :
665 : case GPGME_CONF_FILENAME:
666 : case GPGME_CONF_LDAP_SERVER:
667 : case GPGME_CONF_KEY_FPR:
668 : case GPGME_CONF_PUB_KEY:
669 : case GPGME_CONF_SEC_KEY:
670 : case GPGME_CONF_ALIAS_LIST:
671 0 : type = GPGME_CONF_STRING;
672 0 : break;
673 : }
674 :
675 14753 : release_arg (arg, type);
676 14753 : }
677 :
678 :
679 : gpgme_error_t
680 42 : _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
681 : {
682 42 : if (reset)
683 : {
684 0 : if (opt->new_value)
685 0 : release_arg (opt->new_value, opt->alt_type);
686 0 : opt->new_value = NULL;
687 0 : opt->change_value = 0;
688 : }
689 : else
690 : {
691 : /* Support self-assignment, for example for adding an item to an
692 : existing list. */
693 42 : if (opt->new_value && arg != opt->new_value)
694 0 : release_arg (opt->new_value, opt->alt_type);
695 42 : opt->new_value = arg;
696 42 : opt->change_value = 1;
697 : }
698 42 : return 0;
699 : }
700 :
701 :
702 : /* FIXME: Major problem: We don't get errors from gpgconf. */
703 :
704 : static gpgme_error_t
705 42 : gpgconf_write (void *engine, const char *arg1, char *arg2, gpgme_data_t conf)
706 : {
707 42 : struct engine_gpgconf *gpgconf = engine;
708 42 : gpgme_error_t err = 0;
709 : #define BUFLEN 1024
710 : char buf[BUFLEN];
711 42 : int buflen = 0;
712 : char *argv[7];
713 42 : int argc = 0;
714 42 : int rp[2] = { -1, -1 };
715 42 : int errp[2] = { -1, -1 };
716 42 : struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */},
717 : {-1, 2 /* STDERR_FILENO */, -1},
718 : {-1, -1} };
719 : int status;
720 : int nwrite;
721 :
722 : /* _gpgme_engine_new guarantees that this is not NULL. */
723 42 : argv[argc++] = gpgconf->file_name;
724 :
725 42 : if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
726 : {
727 0 : argv[argc++] = (char*)"--homedir";
728 0 : argv[argc++] = gpgconf->home_dir;
729 : }
730 :
731 42 : argv[argc++] = (char*)"--runtime";
732 42 : argv[argc++] = (char*)arg1;
733 42 : argv[argc++] = arg2;
734 42 : argv[argc] = NULL;
735 42 : assert (argc < DIM (argv));
736 :
737 42 : if (_gpgme_io_pipe (rp, 0) < 0)
738 : {
739 0 : err = gpg_error_from_syserror ();
740 0 : goto leave;
741 : }
742 :
743 42 : if (_gpgme_io_pipe (errp, 1) < 0)
744 : {
745 0 : err = gpg_error_from_syserror ();
746 0 : goto leave;
747 : }
748 :
749 42 : cfd[0].fd = rp[0];
750 42 : cfd[1].fd = errp[1];
751 :
752 42 : status = _gpgme_io_spawn (gpgconf->file_name, argv,
753 : IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
754 42 : if (status < 0)
755 : {
756 0 : err = gpg_error_from_syserror ();
757 0 : goto leave;
758 : }
759 :
760 42 : rp[0] = -1;
761 42 : errp[1] = -1;
762 :
763 : for (;;)
764 : {
765 126 : if (buflen == 0)
766 : {
767 : do
768 : {
769 84 : buflen = gpgme_data_read (conf, buf, BUFLEN);
770 : }
771 84 : while (buflen < 0 && errno == EAGAIN);
772 :
773 84 : if (buflen < 0)
774 : {
775 0 : err = gpg_error_from_syserror ();
776 0 : goto leave;
777 : }
778 84 : else if (buflen == 0)
779 : {
780 : /* All is written. */
781 42 : _gpgme_io_close (rp[1]);
782 42 : rp[1] = -1;
783 :
784 10 : for (;;)
785 : {
786 : do
787 : {
788 52 : buflen = _gpgme_io_read (errp[0], buf, BUFLEN);
789 : }
790 52 : while (buflen < 0 && errno == EAGAIN);
791 :
792 52 : if (buflen == 0)
793 : {
794 42 : err = 0;
795 42 : goto leave;
796 : }
797 : /* XXX: Do something useful with BUF. */
798 : }
799 : }
800 : }
801 :
802 : do
803 : {
804 42 : nwrite = _gpgme_io_write (rp[1], buf, buflen);
805 : }
806 42 : while (nwrite < 0 && errno == EAGAIN);
807 :
808 42 : if (nwrite > 0)
809 : {
810 42 : buflen -= nwrite;
811 42 : if (buflen > 0)
812 0 : memmove (&buf[0], &buf[nwrite], buflen);
813 : }
814 0 : else if (nwrite < 0)
815 : {
816 0 : err = gpg_error_from_syserror ();
817 0 : goto leave;
818 : }
819 : }
820 :
821 : assert (! "reached");
822 :
823 : leave:
824 42 : if (rp[0] != -1)
825 0 : _gpgme_io_close (rp[0]);
826 42 : if (rp[1] != -1)
827 0 : _gpgme_io_close (rp[1]);
828 42 : if (errp[0] != -1)
829 42 : _gpgme_io_close (errp[0]);
830 42 : if (errp[1] != -1)
831 0 : _gpgme_io_close (errp[1]);
832 :
833 42 : return err;
834 : }
835 :
836 :
837 : static gpgme_error_t
838 31 : arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
839 : {
840 31 : gpgme_error_t err = 0;
841 31 : int amt = 0;
842 : char buf[16];
843 :
844 93 : while (amt >= 0 && arg)
845 : {
846 31 : switch (option->alt_type)
847 : {
848 : case GPGME_CONF_NONE:
849 : case GPGME_CONF_UINT32:
850 : default:
851 10 : snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
852 10 : buf[sizeof (buf) - 1] = '\0';
853 10 : amt = gpgme_data_write (conf, buf, strlen (buf));
854 10 : break;
855 :
856 : case GPGME_CONF_INT32:
857 0 : snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
858 0 : buf[sizeof (buf) - 1] = '\0';
859 0 : amt = gpgme_data_write (conf, buf, strlen (buf));
860 0 : break;
861 :
862 :
863 : case GPGME_CONF_STRING:
864 : /* The complex types below are only here to silent the
865 : compiler warning. */
866 : case GPGME_CONF_FILENAME:
867 : case GPGME_CONF_LDAP_SERVER:
868 : case GPGME_CONF_KEY_FPR:
869 : case GPGME_CONF_PUB_KEY:
870 : case GPGME_CONF_SEC_KEY:
871 : case GPGME_CONF_ALIAS_LIST:
872 21 : if (arg->value.string)
873 : {
874 : /* One quote character, and three times to allow for
875 : percent escaping. */
876 21 : char *ptr = arg->value.string;
877 21 : amt = gpgme_data_write (conf, "\"", 1);
878 21 : if (amt < 0)
879 0 : break;
880 :
881 352 : while (!err && *ptr)
882 : {
883 310 : switch (*ptr)
884 : {
885 : case '%':
886 0 : amt = gpgme_data_write (conf, "%25", 3);
887 0 : break;
888 :
889 : case ':':
890 20 : amt = gpgme_data_write (conf, "%3a", 3);
891 20 : break;
892 :
893 : case ',':
894 0 : amt = gpgme_data_write (conf, "%2c", 3);
895 0 : break;
896 :
897 : default:
898 290 : amt = gpgme_data_write (conf, ptr, 1);
899 : }
900 310 : ptr++;
901 : }
902 : }
903 21 : break;
904 : }
905 :
906 31 : if (amt < 0)
907 0 : break;
908 :
909 31 : arg = arg->next;
910 : /* Comma separator. */
911 31 : if (arg)
912 0 : amt = gpgme_data_write (conf, ",", 1);
913 : }
914 :
915 31 : if (amt < 0)
916 0 : return gpg_error_from_syserror ();
917 :
918 31 : return 0;
919 : }
920 :
921 :
922 : static gpgme_error_t
923 152 : gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
924 : {
925 : gpgme_error_t err;
926 152 : int amt = 0;
927 : /* We use a data object to store the new configuration. */
928 : gpgme_data_t conf;
929 : gpgme_conf_opt_t option;
930 152 : int something_changed = 0;
931 :
932 152 : err = gpgme_data_new (&conf);
933 152 : if (err)
934 0 : return err;
935 :
936 152 : option = comp->options;
937 3710 : while (!err && amt >= 0 && option)
938 : {
939 3406 : if (option->change_value)
940 : {
941 42 : unsigned int flags = 0;
942 : char buf[16];
943 :
944 42 : something_changed = 1;
945 :
946 42 : amt = gpgme_data_write (conf, option->name, strlen (option->name));
947 42 : if (amt >= 0)
948 42 : amt = gpgme_data_write (conf, ":", 1);
949 42 : if (amt < 0)
950 0 : break;
951 :
952 42 : if (!option->new_value)
953 11 : flags |= GPGME_CONF_DEFAULT;
954 42 : snprintf (buf, sizeof (buf), "%u", flags);
955 42 : buf[sizeof (buf) - 1] = '\0';
956 :
957 42 : amt = gpgme_data_write (conf, buf, strlen (buf));
958 42 : if (amt >= 0)
959 42 : amt = gpgme_data_write (conf, ":", 1);
960 42 : if (amt < 0)
961 0 : break;
962 :
963 42 : if (option->new_value)
964 : {
965 31 : err = arg_to_data (conf, option, option->new_value);
966 31 : if (err)
967 0 : break;
968 : }
969 42 : amt = gpgme_data_write (conf, "\n", 1);
970 : }
971 3406 : option = option->next;
972 : }
973 152 : if (!err && amt < 0)
974 0 : err = gpg_error_from_syserror ();
975 152 : if (err || !something_changed)
976 : goto bail;
977 :
978 42 : err = gpgme_data_seek (conf, 0, SEEK_SET);
979 42 : if (err)
980 0 : goto bail;
981 :
982 42 : err = gpgconf_write (engine, "--change-options", comp->name, conf);
983 : bail:
984 152 : gpgme_data_release (conf);
985 152 : return err;
986 : }
987 :
988 :
989 : struct gpgconf_config_dir_s
990 : {
991 : const char *what;
992 : char *result;
993 : };
994 :
995 : /* Called for each line in the gpgconf --list-dirs output. Searches
996 : for the desired line and returns the result, indicating success by
997 : a special error value GPG_ERR_USER_1 (which terminates the
998 : operation immediately). */
999 : static gpgme_error_t
1000 24 : gpgconf_config_dir_cb (void *hook, char *line)
1001 : {
1002 : /* This is an input- and output-parameter. */
1003 24 : struct gpgconf_config_dir_s *data = (struct gpgconf_config_dir_s *) hook;
1004 24 : int len = strlen(data->what);
1005 :
1006 24 : if (!strncmp(line, data->what, len) && line[len] == ':')
1007 : {
1008 2 : char *result = strdup(&line[len + 1]);
1009 2 : if (!result)
1010 0 : return gpg_error_from_syserror ();
1011 2 : data->result = result;
1012 2 : return gpg_error(GPG_ERR_USER_1);
1013 : }
1014 22 : return 0;
1015 : }
1016 :
1017 :
1018 : /* Like gpgme_get_dirinfo, but uses the home directory of ENGINE and
1019 : does not cache the result. */
1020 : static gpgme_error_t
1021 2 : gpgconf_conf_dir (void *engine, const char *what, char **result)
1022 : {
1023 : gpgme_error_t err;
1024 : struct gpgconf_config_dir_s data;
1025 :
1026 2 : data.what = what;
1027 2 : data.result = NULL;
1028 2 : err = gpgconf_read (engine, "--list-dirs", NULL,
1029 : gpgconf_config_dir_cb, &data);
1030 2 : if (gpg_err_code (err) == GPG_ERR_USER_1)
1031 : {
1032 : /* This signals to us that a result was found. */
1033 2 : *result = data.result;
1034 2 : return 0;
1035 : }
1036 :
1037 0 : if (!err)
1038 0 : err = gpg_error(GPG_ERR_NOT_FOUND);
1039 0 : return 0;
1040 : }
1041 :
1042 :
1043 : /* Parse a line received from gpgconf --query-swdb. This function may
1044 : * modify LINE. The result is stored at RESULT. */
1045 : static gpg_error_t
1046 0 : parse_swdb_line (char *line, gpgme_query_swdb_result_t result)
1047 : {
1048 : char *field[9];
1049 0 : int fields = 0;
1050 : gpg_err_code_t ec;
1051 :
1052 0 : while (line && fields < DIM (field))
1053 : {
1054 0 : field[fields++] = line;
1055 0 : line = strchr (line, ':');
1056 0 : if (line)
1057 0 : *line++ = 0;
1058 : }
1059 : /* We require that all fields exists - gpgme emits all these fields
1060 : * even on error. They might be empty, though. */
1061 0 : if (fields < 9)
1062 0 : return gpg_error (GPG_ERR_INV_ENGINE);
1063 :
1064 0 : free (result->name);
1065 0 : result->name = strdup (field[0]);
1066 0 : if (!result->name)
1067 0 : return gpg_error_from_syserror ();
1068 :
1069 0 : free (result->iversion);
1070 0 : result->iversion = strdup (field[1]);
1071 0 : if (!result->iversion)
1072 0 : return gpg_error_from_syserror ();
1073 :
1074 0 : result->urgent = (strtol (field[3], NULL, 10) > 0);
1075 :
1076 0 : ec = gpg_err_code (strtoul (field[4], NULL, 10));
1077 :
1078 0 : result->created = _gpgme_parse_timestamp (field[5], NULL);
1079 0 : result->retrieved= _gpgme_parse_timestamp (field[6], NULL);
1080 :
1081 0 : free (result->version);
1082 0 : result->version = strdup (field[7]);
1083 0 : if (!result->version)
1084 0 : return gpg_error_from_syserror ();
1085 :
1086 0 : result->reldate = _gpgme_parse_timestamp (field[8], NULL);
1087 :
1088 : /* Set other flags. */
1089 0 : result->warning = !!ec;
1090 0 : result->update = 0;
1091 0 : result->noinfo = 0;
1092 0 : result->unknown = 0;
1093 0 : result->tooold = 0;
1094 0 : result->error = 0;
1095 :
1096 0 : switch (*field[2])
1097 : {
1098 0 : case '-': result->warning = 1; break;
1099 0 : case '?': result->unknown = result->warning = 1; break;
1100 0 : case 'u': result->update = 1; break;
1101 0 : case 'c': break;
1102 0 : case 'n': break;
1103 : default:
1104 0 : result->warning = 1;
1105 0 : if (!ec)
1106 0 : ec = GPG_ERR_INV_ENGINE;
1107 0 : break;
1108 : }
1109 :
1110 0 : if (ec == GPG_ERR_TOO_OLD)
1111 0 : result->tooold = 1;
1112 0 : else if (ec == GPG_ERR_ENOENT)
1113 0 : result->noinfo = 1;
1114 0 : else if (ec)
1115 0 : result->error = 1;
1116 :
1117 :
1118 0 : return 0;
1119 : }
1120 :
1121 :
1122 : static gpgme_error_t
1123 0 : gpgconf_query_swdb (void *engine,
1124 : const char *name, const char *iversion,
1125 : gpgme_query_swdb_result_t result)
1126 : {
1127 0 : struct engine_gpgconf *gpgconf = engine;
1128 0 : gpgme_error_t err = 0;
1129 : char *linebuf;
1130 : size_t linebufsize;
1131 : int linelen;
1132 : char *argv[7];
1133 0 : int argc = 0;
1134 : int rp[2];
1135 0 : struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
1136 : {-1, -1} };
1137 : int status;
1138 : int nread;
1139 0 : char *mark = NULL;
1140 :
1141 0 : if (!have_gpgconf_version (gpgconf, "2.1.16"))
1142 0 : return gpg_error (GPG_ERR_ENGINE_TOO_OLD);
1143 :
1144 : /* _gpgme_engine_new guarantees that this is not NULL. */
1145 0 : argv[argc++] = gpgconf->file_name;
1146 :
1147 0 : if (gpgconf->home_dir)
1148 : {
1149 0 : argv[argc++] = (char*)"--homedir";
1150 0 : argv[argc++] = gpgconf->home_dir;
1151 : }
1152 :
1153 0 : argv[argc++] = (char*)"--query-swdb";
1154 0 : argv[argc++] = (char*)name;
1155 0 : argv[argc++] = (char*)iversion;
1156 0 : argv[argc] = NULL;
1157 0 : assert (argc < DIM (argv));
1158 :
1159 0 : if (_gpgme_io_pipe (rp, 1) < 0)
1160 0 : return gpg_error_from_syserror ();
1161 :
1162 0 : cfd[0].fd = rp[1];
1163 :
1164 0 : status = _gpgme_io_spawn (gpgconf->file_name, argv,
1165 : IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
1166 0 : if (status < 0)
1167 : {
1168 0 : _gpgme_io_close (rp[0]);
1169 0 : _gpgme_io_close (rp[1]);
1170 0 : return gpg_error_from_syserror ();
1171 : }
1172 :
1173 0 : linebufsize = 2048; /* Same as used by gpgconf. */
1174 0 : linebuf = malloc (linebufsize);
1175 0 : if (!linebuf)
1176 : {
1177 0 : err = gpg_error_from_syserror ();
1178 0 : goto leave;
1179 : }
1180 0 : linelen = 0;
1181 :
1182 0 : while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
1183 0 : linebufsize - linelen - 1)))
1184 : {
1185 : char *line;
1186 0 : const char *lastmark = NULL;
1187 : size_t nused;
1188 :
1189 0 : if (nread < 0)
1190 : {
1191 0 : err = gpg_error_from_syserror ();
1192 0 : goto leave;
1193 : }
1194 :
1195 0 : linelen += nread;
1196 0 : linebuf[linelen] = '\0';
1197 :
1198 0 : for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
1199 : {
1200 0 : lastmark = mark;
1201 0 : if (mark > line && mark[-1] == '\r')
1202 0 : mark[-1] = '\0';
1203 : else
1204 0 : mark[0] = '\0';
1205 :
1206 : /* Got a full line. Due to the CR removal code (which
1207 : occurs only on Windows) we might be one-off and thus
1208 : would see empty lines. */
1209 0 : if (*line)
1210 : {
1211 0 : err = parse_swdb_line (line, result);
1212 0 : goto leave; /* Ready. */
1213 : }
1214 : else /* empty line. */
1215 0 : err = 0;
1216 : }
1217 :
1218 0 : nused = lastmark? (lastmark + 1 - linebuf) : 0;
1219 0 : memmove (linebuf, linebuf + nused, linelen - nused);
1220 0 : linelen -= nused;
1221 :
1222 0 : if (!(linelen < linebufsize - 1))
1223 : {
1224 : char *newlinebuf;
1225 :
1226 0 : if (linelen < 8 * 1024 - 1)
1227 0 : linebufsize = 8 * 1024;
1228 0 : else if (linelen < 64 * 1024 - 1)
1229 0 : linebufsize = 64 * 1024;
1230 : else
1231 : {
1232 : /* We reached our limit - give up. */
1233 0 : err = gpg_error (GPG_ERR_LINE_TOO_LONG);
1234 0 : goto leave;
1235 : }
1236 :
1237 0 : newlinebuf = realloc (linebuf, linebufsize);
1238 0 : if (!newlinebuf)
1239 : {
1240 0 : err = gpg_error_from_syserror ();
1241 0 : goto leave;
1242 : }
1243 0 : linebuf = newlinebuf;
1244 : }
1245 : }
1246 :
1247 : leave:
1248 0 : free (linebuf);
1249 0 : _gpgme_io_close (rp[0]);
1250 0 : return err;
1251 : }
1252 :
1253 :
1254 : static void
1255 200 : gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1256 : {
1257 : (void)engine;
1258 : (void)io_cbs;
1259 : /* Nothing to do. */
1260 200 : }
1261 :
1262 :
1263 : /* Currently, we do not use the engine interface for the various
1264 : operations. */
1265 : void
1266 155 : _gpgme_conf_release (gpgme_conf_comp_t conf)
1267 : {
1268 155 : gpgconf_config_release (conf);
1269 155 : }
1270 :
1271 :
1272 : struct engine_ops _gpgme_engine_ops_gpgconf =
1273 : {
1274 : /* Static functions. */
1275 : _gpgme_get_default_gpgconf_name,
1276 : NULL,
1277 : gpgconf_get_version,
1278 : gpgconf_get_req_version,
1279 : gpgconf_new,
1280 :
1281 : /* Member functions. */
1282 : gpgconf_release,
1283 : NULL, /* reset */
1284 : NULL, /* set_status_cb */
1285 : NULL, /* set_status_handler */
1286 : NULL, /* set_command_handler */
1287 : NULL, /* set_colon_line_handler */
1288 : NULL, /* set_locale */
1289 : NULL, /* set_protocol */
1290 : NULL, /* set_engine_flags */
1291 : NULL, /* decrypt */
1292 : NULL, /* delete */
1293 : NULL, /* edit */
1294 : NULL, /* encrypt */
1295 : NULL, /* encrypt_sign */
1296 : NULL, /* export */
1297 : NULL, /* export_ext */
1298 : NULL, /* genkey */
1299 : NULL, /* import */
1300 : NULL, /* keylist */
1301 : NULL, /* keylist_ext */
1302 : NULL, /* keylist_data */
1303 : NULL, /* keysign */
1304 : NULL, /* tofu_policy */
1305 : NULL, /* sign */
1306 : NULL, /* trustlist */
1307 : NULL, /* verify */
1308 : NULL, /* getauditlog */
1309 : NULL, /* opassuan_transact */
1310 : gpgconf_conf_load,
1311 : gpgconf_conf_save,
1312 : gpgconf_conf_dir,
1313 : gpgconf_query_swdb,
1314 : gpgconf_set_io_cbs,
1315 : NULL, /* io_event */
1316 : NULL, /* cancel */
1317 : NULL, /* cancel_op */
1318 : NULL, /* passwd */
1319 : NULL, /* set_pinentry_mode */
1320 : NULL /* opspawn */
1321 : };
|