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