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