Line data Source code
1 : /* engine-gpgsm.c - GpgSM engine.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
4 : 2010 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, write to the Free Software
20 : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 : 02111-1307, USA. */
22 :
23 : #if HAVE_CONFIG_H
24 : #include <config.h>
25 : #endif
26 :
27 : #include <stdlib.h>
28 : #include <string.h>
29 : #ifdef HAVE_SYS_TYPES_H
30 : # include <sys/types.h>
31 : #endif
32 : #include <assert.h>
33 : #ifdef HAVE_UNISTD_H
34 : # include <unistd.h>
35 : #endif
36 : #ifdef HAVE_LOCALE_H
37 : #include <locale.h>
38 : #endif
39 : #include <fcntl.h> /* FIXME */
40 : #include <errno.h>
41 :
42 : #include "gpgme.h"
43 : #include "util.h"
44 : #include "ops.h"
45 : #include "wait.h"
46 : #include "priv-io.h"
47 : #include "sema.h"
48 : #include "data.h"
49 :
50 : #include "assuan.h"
51 : #include "debug.h"
52 :
53 : #include "engine-backend.h"
54 :
55 :
56 : typedef struct
57 : {
58 : int fd; /* FD we talk about. */
59 : int server_fd;/* Server FD for this connection. */
60 : int dir; /* Inbound/Outbound, maybe given implicit? */
61 : void *data; /* Handler-specific data. */
62 : void *tag; /* ID from the user for gpgme_remove_io_callback. */
63 : char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
64 : need this because _gpgme_io_fd2str can't
65 : be used on a closed descriptor. */
66 : } iocb_data_t;
67 :
68 :
69 : struct engine_gpgsm
70 : {
71 : assuan_context_t assuan_ctx;
72 :
73 : int lc_ctype_set;
74 : int lc_messages_set;
75 :
76 : iocb_data_t status_cb;
77 :
78 : /* Input, output etc are from the servers perspective. */
79 : iocb_data_t input_cb;
80 : gpgme_data_t input_helper_data; /* Input helper data object. */
81 : void *input_helper_memory; /* Input helper memory block. */
82 :
83 : iocb_data_t output_cb;
84 :
85 : iocb_data_t message_cb;
86 :
87 : struct
88 : {
89 : engine_status_handler_t fnc;
90 : void *fnc_value;
91 : gpgme_status_cb_t mon_cb;
92 : void *mon_cb_value;
93 : } status;
94 :
95 : struct
96 : {
97 : engine_colon_line_handler_t fnc;
98 : void *fnc_value;
99 : struct
100 : {
101 : char *line;
102 : int linesize;
103 : int linelen;
104 : } attic;
105 : int any; /* any data line seen */
106 : } colon;
107 :
108 : gpgme_data_t inline_data; /* Used to collect D lines. */
109 :
110 : struct gpgme_io_cbs io_cbs;
111 : };
112 :
113 : typedef struct engine_gpgsm *engine_gpgsm_t;
114 :
115 :
116 : static void gpgsm_io_event (void *engine,
117 : gpgme_event_io_t type, void *type_data);
118 :
119 :
120 :
121 : static char *
122 57 : gpgsm_get_version (const char *file_name)
123 : {
124 57 : return _gpgme_get_program_version (file_name ? file_name
125 : : _gpgme_get_default_gpgsm_name ());
126 : }
127 :
128 :
129 : static const char *
130 56 : gpgsm_get_req_version (void)
131 : {
132 56 : return "2.0.4";
133 : }
134 :
135 :
136 : static void
137 32 : close_notify_handler (int fd, void *opaque)
138 : {
139 32 : engine_gpgsm_t gpgsm = opaque;
140 :
141 32 : assert (fd != -1);
142 32 : if (gpgsm->status_cb.fd == fd)
143 : {
144 14 : if (gpgsm->status_cb.tag)
145 14 : (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
146 14 : gpgsm->status_cb.fd = -1;
147 14 : gpgsm->status_cb.tag = NULL;
148 : }
149 18 : else if (gpgsm->input_cb.fd == fd)
150 : {
151 8 : if (gpgsm->input_cb.tag)
152 8 : (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
153 8 : gpgsm->input_cb.fd = -1;
154 8 : gpgsm->input_cb.tag = NULL;
155 8 : if (gpgsm->input_helper_data)
156 : {
157 0 : gpgme_data_release (gpgsm->input_helper_data);
158 0 : gpgsm->input_helper_data = NULL;
159 : }
160 8 : if (gpgsm->input_helper_memory)
161 : {
162 0 : free (gpgsm->input_helper_memory);
163 0 : gpgsm->input_helper_memory = NULL;
164 : }
165 : }
166 10 : else if (gpgsm->output_cb.fd == fd)
167 : {
168 8 : if (gpgsm->output_cb.tag)
169 8 : (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
170 8 : gpgsm->output_cb.fd = -1;
171 8 : gpgsm->output_cb.tag = NULL;
172 : }
173 2 : else if (gpgsm->message_cb.fd == fd)
174 : {
175 2 : if (gpgsm->message_cb.tag)
176 2 : (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
177 2 : gpgsm->message_cb.fd = -1;
178 2 : gpgsm->message_cb.tag = NULL;
179 : }
180 32 : }
181 :
182 :
183 : /* This is the default inquiry callback. We use it to handle the
184 : Pinentry notifications. */
185 : static gpgme_error_t
186 0 : default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
187 : {
188 0 : if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
189 : {
190 0 : _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
191 : }
192 :
193 0 : return 0;
194 : }
195 :
196 :
197 : static gpgme_error_t
198 8 : gpgsm_cancel (void *engine)
199 : {
200 8 : engine_gpgsm_t gpgsm = engine;
201 :
202 8 : if (!gpgsm)
203 0 : return gpg_error (GPG_ERR_INV_VALUE);
204 :
205 8 : if (gpgsm->status_cb.fd != -1)
206 0 : _gpgme_io_close (gpgsm->status_cb.fd);
207 8 : if (gpgsm->input_cb.fd != -1)
208 0 : _gpgme_io_close (gpgsm->input_cb.fd);
209 8 : if (gpgsm->output_cb.fd != -1)
210 0 : _gpgme_io_close (gpgsm->output_cb.fd);
211 8 : if (gpgsm->message_cb.fd != -1)
212 0 : _gpgme_io_close (gpgsm->message_cb.fd);
213 :
214 8 : if (gpgsm->assuan_ctx)
215 : {
216 8 : assuan_release (gpgsm->assuan_ctx);
217 8 : gpgsm->assuan_ctx = NULL;
218 : }
219 :
220 8 : return 0;
221 : }
222 :
223 :
224 : static void
225 8 : gpgsm_release (void *engine)
226 : {
227 8 : engine_gpgsm_t gpgsm = engine;
228 :
229 8 : if (!gpgsm)
230 8 : return;
231 :
232 8 : gpgsm_cancel (engine);
233 :
234 8 : free (gpgsm->colon.attic.line);
235 8 : free (gpgsm);
236 : }
237 :
238 :
239 : static gpgme_error_t
240 8 : gpgsm_new (void **engine, const char *file_name, const char *home_dir,
241 : const char *version)
242 : {
243 8 : gpgme_error_t err = 0;
244 : engine_gpgsm_t gpgsm;
245 : const char *pgmname;
246 : const char *argv[5];
247 : int argc;
248 : #if !USE_DESCRIPTOR_PASSING
249 : int fds[2];
250 : int child_fds[4];
251 : #endif
252 8 : char *dft_display = NULL;
253 : char dft_ttyname[64];
254 8 : char *dft_ttytype = NULL;
255 : char *optstr;
256 :
257 : (void)version; /* Not yet used. */
258 :
259 8 : gpgsm = calloc (1, sizeof *gpgsm);
260 8 : if (!gpgsm)
261 0 : return gpg_error_from_syserror ();
262 :
263 8 : gpgsm->status_cb.fd = -1;
264 8 : gpgsm->status_cb.dir = 1;
265 8 : gpgsm->status_cb.tag = 0;
266 8 : gpgsm->status_cb.data = gpgsm;
267 :
268 8 : gpgsm->input_cb.fd = -1;
269 8 : gpgsm->input_cb.dir = 0;
270 8 : gpgsm->input_cb.tag = 0;
271 8 : gpgsm->input_cb.server_fd = -1;
272 8 : *gpgsm->input_cb.server_fd_str = 0;
273 8 : gpgsm->output_cb.fd = -1;
274 8 : gpgsm->output_cb.dir = 1;
275 8 : gpgsm->output_cb.tag = 0;
276 8 : gpgsm->output_cb.server_fd = -1;
277 8 : *gpgsm->output_cb.server_fd_str = 0;
278 8 : gpgsm->message_cb.fd = -1;
279 8 : gpgsm->message_cb.dir = 0;
280 8 : gpgsm->message_cb.tag = 0;
281 8 : gpgsm->message_cb.server_fd = -1;
282 8 : *gpgsm->message_cb.server_fd_str = 0;
283 :
284 8 : gpgsm->status.fnc = 0;
285 8 : gpgsm->colon.fnc = 0;
286 8 : gpgsm->colon.attic.line = 0;
287 8 : gpgsm->colon.attic.linesize = 0;
288 8 : gpgsm->colon.attic.linelen = 0;
289 8 : gpgsm->colon.any = 0;
290 :
291 8 : gpgsm->inline_data = NULL;
292 :
293 8 : gpgsm->io_cbs.add = NULL;
294 8 : gpgsm->io_cbs.add_priv = NULL;
295 8 : gpgsm->io_cbs.remove = NULL;
296 8 : gpgsm->io_cbs.event = NULL;
297 8 : gpgsm->io_cbs.event_priv = NULL;
298 :
299 : #if !USE_DESCRIPTOR_PASSING
300 : if (_gpgme_io_pipe (fds, 0) < 0)
301 : {
302 : err = gpg_error_from_syserror ();
303 : goto leave;
304 : }
305 : gpgsm->input_cb.fd = fds[1];
306 : gpgsm->input_cb.server_fd = fds[0];
307 :
308 : if (_gpgme_io_pipe (fds, 1) < 0)
309 : {
310 : err = gpg_error_from_syserror ();
311 : goto leave;
312 : }
313 : gpgsm->output_cb.fd = fds[0];
314 : gpgsm->output_cb.server_fd = fds[1];
315 :
316 : if (_gpgme_io_pipe (fds, 0) < 0)
317 : {
318 : err = gpg_error_from_syserror ();
319 : goto leave;
320 : }
321 : gpgsm->message_cb.fd = fds[1];
322 : gpgsm->message_cb.server_fd = fds[0];
323 :
324 : child_fds[0] = gpgsm->input_cb.server_fd;
325 : child_fds[1] = gpgsm->output_cb.server_fd;
326 : child_fds[2] = gpgsm->message_cb.server_fd;
327 : child_fds[3] = -1;
328 : #endif
329 :
330 8 : pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
331 :
332 8 : argc = 0;
333 8 : argv[argc++] = _gpgme_get_basename (pgmname);
334 8 : if (home_dir)
335 : {
336 0 : argv[argc++] = "--homedir";
337 0 : argv[argc++] = home_dir;
338 : }
339 8 : argv[argc++] = "--server";
340 8 : argv[argc++] = NULL;
341 :
342 8 : err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
343 : &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
344 : NULL);
345 8 : if (err)
346 0 : goto leave;
347 8 : assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
348 :
349 : #if USE_DESCRIPTOR_PASSING
350 8 : err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
351 : NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
352 : #else
353 : {
354 : assuan_fd_t achild_fds[4];
355 : int i;
356 :
357 : /* For now... */
358 : for (i = 0; i < 4; i++)
359 : achild_fds[i] = (assuan_fd_t) child_fds[i];
360 :
361 : err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
362 : achild_fds, NULL, NULL, 0);
363 :
364 : /* For now... */
365 : for (i = 0; i < 4; i++)
366 : child_fds[i] = (int) achild_fds[i];
367 : }
368 :
369 : /* On Windows, handles are inserted in the spawned process with
370 : DuplicateHandle, and child_fds contains the server-local names
371 : for the inserted handles when assuan_pipe_connect returns. */
372 : if (!err)
373 : {
374 : /* Note: We don't use _gpgme_io_fd2str here. On W32 the
375 : returned handles are real W32 system handles, not whatever
376 : GPGME uses internally (which may be a system handle, a C
377 : library handle or a GLib/Qt channel. Confusing, yes, but
378 : remember these are server-local names, so they are not part
379 : of GPGME at all. */
380 : snprintf (gpgsm->input_cb.server_fd_str,
381 : sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
382 : snprintf (gpgsm->output_cb.server_fd_str,
383 : sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
384 : snprintf (gpgsm->message_cb.server_fd_str,
385 : sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
386 : }
387 : #endif
388 8 : if (err)
389 0 : goto leave;
390 :
391 8 : err = _gpgme_getenv ("DISPLAY", &dft_display);
392 8 : if (err)
393 0 : goto leave;
394 8 : if (dft_display)
395 : {
396 8 : if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
397 : {
398 0 : free (dft_display);
399 0 : err = gpg_error_from_syserror ();
400 0 : goto leave;
401 : }
402 8 : free (dft_display);
403 :
404 8 : err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
405 : NULL, NULL, NULL);
406 8 : free (optstr);
407 8 : if (err)
408 0 : goto leave;
409 : }
410 :
411 8 : if (isatty (1))
412 : {
413 : int rc;
414 :
415 8 : rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
416 :
417 : /* Even though isatty() returns 1, ttyname_r() may fail in many
418 : ways, e.g., when /dev/pts is not accessible under chroot. */
419 8 : if (!rc)
420 : {
421 8 : if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
422 : {
423 0 : err = gpg_error_from_syserror ();
424 0 : goto leave;
425 : }
426 8 : err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
427 : NULL, NULL, NULL);
428 8 : free (optstr);
429 8 : if (err)
430 0 : goto leave;
431 :
432 8 : err = _gpgme_getenv ("TERM", &dft_ttytype);
433 8 : if (err)
434 0 : goto leave;
435 8 : if (dft_ttytype)
436 : {
437 8 : if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
438 : {
439 0 : free (dft_ttytype);
440 0 : err = gpg_error_from_syserror ();
441 0 : goto leave;
442 : }
443 8 : free (dft_ttytype);
444 :
445 8 : err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
446 : NULL, NULL, NULL, NULL);
447 8 : free (optstr);
448 8 : if (err)
449 0 : goto leave;
450 : }
451 : }
452 : }
453 :
454 : /* Ask gpgsm to enable the audit log support. */
455 8 : if (!err)
456 : {
457 8 : err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
458 : NULL, NULL, NULL, NULL, NULL, NULL);
459 8 : if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
460 0 : err = 0; /* This is an optional feature of gpgsm. */
461 : }
462 :
463 :
464 : #ifdef HAVE_W32_SYSTEM
465 : /* Under Windows we need to use AllowSetForegroundWindow. Tell
466 : gpgsm to tell us when it needs it. */
467 : if (!err)
468 : {
469 : err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
470 : NULL, NULL, NULL, NULL, NULL, NULL);
471 : if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
472 : err = 0; /* This is a new feature of gpgsm. */
473 : }
474 : #endif /*HAVE_W32_SYSTEM*/
475 :
476 : #if !USE_DESCRIPTOR_PASSING
477 : if (!err
478 : && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
479 : close_notify_handler, gpgsm)
480 : || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
481 : close_notify_handler, gpgsm)
482 : || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
483 : close_notify_handler, gpgsm)))
484 : {
485 : err = gpg_error (GPG_ERR_GENERAL);
486 : goto leave;
487 : }
488 : #endif
489 :
490 : leave:
491 : /* Close the server ends of the pipes (because of this, we must use
492 : the stored server_fd_str in the function start). Our ends are
493 : closed in gpgsm_release(). */
494 : #if !USE_DESCRIPTOR_PASSING
495 : if (gpgsm->input_cb.server_fd != -1)
496 : _gpgme_io_close (gpgsm->input_cb.server_fd);
497 : if (gpgsm->output_cb.server_fd != -1)
498 : _gpgme_io_close (gpgsm->output_cb.server_fd);
499 : if (gpgsm->message_cb.server_fd != -1)
500 : _gpgme_io_close (gpgsm->message_cb.server_fd);
501 : #endif
502 :
503 8 : if (err)
504 0 : gpgsm_release (gpgsm);
505 : else
506 8 : *engine = gpgsm;
507 :
508 8 : return err;
509 : }
510 :
511 :
512 : static gpgme_error_t
513 24 : gpgsm_set_locale (void *engine, int category, const char *value)
514 : {
515 24 : engine_gpgsm_t gpgsm = engine;
516 : gpgme_error_t err;
517 : char *optstr;
518 : char *catstr;
519 :
520 : /* FIXME: If value is NULL, we need to reset the option to default.
521 : But we can't do this. So we error out here. GPGSM needs support
522 : for this. */
523 : if (0)
524 : ;
525 : #ifdef LC_CTYPE
526 24 : else if (category == LC_CTYPE)
527 : {
528 12 : catstr = "lc-ctype";
529 12 : if (!value && gpgsm->lc_ctype_set)
530 0 : return gpg_error (GPG_ERR_INV_VALUE);
531 12 : if (value)
532 12 : gpgsm->lc_ctype_set = 1;
533 : }
534 : #endif
535 : #ifdef LC_MESSAGES
536 12 : else if (category == LC_MESSAGES)
537 : {
538 12 : catstr = "lc-messages";
539 12 : if (!value && gpgsm->lc_messages_set)
540 0 : return gpg_error (GPG_ERR_INV_VALUE);
541 12 : if (value)
542 12 : gpgsm->lc_messages_set = 1;
543 : }
544 : #endif /* LC_MESSAGES */
545 : else
546 0 : return gpg_error (GPG_ERR_INV_VALUE);
547 :
548 : /* FIXME: Reset value to default. */
549 24 : if (!value)
550 0 : return 0;
551 :
552 24 : if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
553 0 : err = gpg_error_from_syserror ();
554 : else
555 : {
556 24 : err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
557 : NULL, NULL, NULL, NULL);
558 24 : free (optstr);
559 : }
560 :
561 24 : return err;
562 : }
563 :
564 :
565 : static gpgme_error_t
566 33 : gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, char *cmd,
567 : engine_status_handler_t status_fnc,
568 : void *status_fnc_value)
569 : {
570 33 : assuan_context_t ctx = gpgsm->assuan_ctx;
571 : gpg_error_t err, cb_err;
572 : char *line;
573 : size_t linelen;
574 :
575 33 : err = assuan_write_line (ctx, cmd);
576 33 : if (err)
577 0 : return err;
578 :
579 33 : cb_err = 0;
580 : do
581 : {
582 33 : err = assuan_read_line (ctx, &line, &linelen);
583 33 : if (err)
584 0 : return err;
585 :
586 33 : if (*line == '#' || !linelen)
587 0 : continue;
588 :
589 33 : if (linelen >= 2
590 33 : && line[0] == 'O' && line[1] == 'K'
591 33 : && (line[2] == '\0' || line[2] == ' '))
592 33 : return cb_err;
593 0 : else if (linelen >= 4
594 0 : && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
595 0 : && line[3] == ' ')
596 : {
597 : /* We prefer a callback generated error because that one is
598 : more related to gpgme and thus probably more important
599 : than the error returned by the engine. */
600 0 : err = cb_err? cb_err : atoi (&line[4]);
601 : }
602 0 : else if (linelen >= 2
603 0 : && line[0] == 'S' && line[1] == ' ')
604 : {
605 : /* After an error from a status callback we skip all further
606 : status lines. */
607 0 : if (!cb_err)
608 : {
609 : char *rest;
610 : gpgme_status_code_t r;
611 :
612 0 : rest = strchr (line + 2, ' ');
613 0 : if (!rest)
614 0 : rest = line + linelen; /* set to an empty string */
615 : else
616 0 : *(rest++) = 0;
617 :
618 0 : r = _gpgme_parse_status (line + 2);
619 0 : if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
620 : {
621 : /* Note that we call the monitor even if we do
622 : * not know the status code (r < 0). */
623 0 : cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
624 0 : line + 2, rest);
625 : }
626 :
627 0 : if (r >= 0 && status_fnc && !cb_err)
628 0 : cb_err = status_fnc (status_fnc_value, r, rest);
629 : }
630 : }
631 : else
632 : {
633 : /* Invalid line or INQUIRY. We can't do anything else than
634 : to stop. As with ERR we prefer a status callback
635 : generated error code, though. */
636 0 : err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
637 : }
638 : }
639 0 : while (!err);
640 :
641 0 : return err;
642 : }
643 :
644 :
645 : typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
646 :
647 : static void
648 24 : gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
649 : {
650 : #if !USE_DESCRIPTOR_PASSING
651 : switch (fd_type)
652 : {
653 : case INPUT_FD:
654 : _gpgme_io_close (gpgsm->input_cb.fd);
655 : break;
656 : case OUTPUT_FD:
657 : _gpgme_io_close (gpgsm->output_cb.fd);
658 : break;
659 : case MESSAGE_FD:
660 : _gpgme_io_close (gpgsm->message_cb.fd);
661 : break;
662 : }
663 : #endif
664 24 : }
665 :
666 : #define COMMANDLINELEN 40
667 : static gpgme_error_t
668 18 : gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
669 : {
670 18 : gpg_error_t err = 0;
671 : char line[COMMANDLINELEN];
672 : char *which;
673 : iocb_data_t *iocb_data;
674 : #if USE_DESCRIPTOR_PASSING
675 : int dir;
676 : #endif
677 :
678 18 : switch (fd_type)
679 : {
680 : case INPUT_FD:
681 8 : which = "INPUT";
682 8 : iocb_data = &gpgsm->input_cb;
683 8 : break;
684 :
685 : case OUTPUT_FD:
686 8 : which = "OUTPUT";
687 8 : iocb_data = &gpgsm->output_cb;
688 8 : break;
689 :
690 : case MESSAGE_FD:
691 2 : which = "MESSAGE";
692 2 : iocb_data = &gpgsm->message_cb;
693 2 : break;
694 :
695 : default:
696 0 : return gpg_error (GPG_ERR_INV_VALUE);
697 : }
698 :
699 : #if USE_DESCRIPTOR_PASSING
700 18 : dir = iocb_data->dir;
701 : /* We try to short-cut the communication by giving GPGSM direct
702 : access to the file descriptor, rather than using a pipe. */
703 18 : iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
704 18 : if (iocb_data->server_fd < 0)
705 : {
706 : int fds[2];
707 :
708 18 : if (_gpgme_io_pipe (fds, dir) < 0)
709 0 : return gpg_error_from_syserror ();
710 :
711 18 : iocb_data->fd = dir ? fds[0] : fds[1];
712 18 : iocb_data->server_fd = dir ? fds[1] : fds[0];
713 :
714 18 : if (_gpgme_io_set_close_notify (iocb_data->fd,
715 : close_notify_handler, gpgsm))
716 : {
717 0 : err = gpg_error (GPG_ERR_GENERAL);
718 0 : goto leave_set_fd;
719 : }
720 : }
721 :
722 18 : err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
723 18 : if (err)
724 0 : goto leave_set_fd;
725 :
726 18 : _gpgme_io_close (iocb_data->server_fd);
727 18 : iocb_data->server_fd = -1;
728 :
729 18 : if (opt)
730 5 : snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
731 : else
732 13 : snprintf (line, COMMANDLINELEN, "%s FD", which);
733 : #else
734 : if (opt)
735 : snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
736 : which, iocb_data->server_fd_str, opt);
737 : else
738 : snprintf (line, COMMANDLINELEN, "%s FD=%s",
739 : which, iocb_data->server_fd_str);
740 : #endif
741 :
742 18 : err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
743 :
744 : #if USE_DESCRIPTOR_PASSING
745 : leave_set_fd:
746 18 : if (err)
747 : {
748 0 : _gpgme_io_close (iocb_data->fd);
749 0 : iocb_data->fd = -1;
750 0 : if (iocb_data->server_fd != -1)
751 : {
752 0 : _gpgme_io_close (iocb_data->server_fd);
753 0 : iocb_data->server_fd = -1;
754 : }
755 : }
756 : #endif
757 :
758 18 : return err;
759 : }
760 :
761 :
762 : static const char *
763 8 : map_data_enc (gpgme_data_t d)
764 : {
765 8 : switch (gpgme_data_get_encoding (d))
766 : {
767 : case GPGME_DATA_ENCODING_NONE:
768 8 : break;
769 : case GPGME_DATA_ENCODING_BINARY:
770 0 : return "--binary";
771 : case GPGME_DATA_ENCODING_BASE64:
772 0 : return "--base64";
773 : case GPGME_DATA_ENCODING_ARMOR:
774 0 : return "--armor";
775 : default:
776 0 : break;
777 : }
778 8 : return NULL;
779 : }
780 :
781 :
782 : static gpgme_error_t
783 28 : status_handler (void *opaque, int fd)
784 : {
785 28 : struct io_cb_data *data = (struct io_cb_data *) opaque;
786 28 : engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
787 28 : gpgme_error_t err = 0;
788 : char *line;
789 : size_t linelen;
790 :
791 : do
792 : {
793 35 : err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
794 35 : if (err)
795 : {
796 : /* Try our best to terminate the connection friendly. */
797 : /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
798 0 : TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
799 : "fd 0x%x: error from assuan (%d) getting status line : %s",
800 : fd, err, gpg_strerror (err));
801 : }
802 35 : else if (linelen >= 3
803 21 : && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
804 0 : && (line[3] == '\0' || line[3] == ' '))
805 : {
806 0 : if (line[3] == ' ')
807 0 : err = atoi (&line[4]);
808 0 : if (! err)
809 0 : err = gpg_error (GPG_ERR_GENERAL);
810 0 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
811 : "fd 0x%x: ERR line - mapped to: %s",
812 : fd, err ? gpg_strerror (err) : "ok");
813 : /* Try our best to terminate the connection friendly. */
814 : /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
815 : }
816 35 : else if (linelen >= 2
817 35 : && line[0] == 'O' && line[1] == 'K'
818 14 : && (line[2] == '\0' || line[2] == ' '))
819 : {
820 14 : if (gpgsm->status.fnc)
821 14 : err = gpgsm->status.fnc (gpgsm->status.fnc_value,
822 : GPGME_STATUS_EOF, "");
823 :
824 14 : if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
825 : {
826 : /* We must tell a colon function about the EOF. We do
827 : this only when we have seen any data lines. Note
828 : that this inlined use of colon data lines will
829 : eventually be changed into using a regular data
830 : channel. */
831 2 : gpgsm->colon.any = 0;
832 2 : err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
833 : }
834 14 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
835 : "fd 0x%x: OK line - final status: %s",
836 : fd, err ? gpg_strerror (err) : "ok");
837 14 : _gpgme_io_close (gpgsm->status_cb.fd);
838 14 : return err;
839 : }
840 21 : else if (linelen > 2
841 21 : && line[0] == 'D' && line[1] == ' '
842 4 : && gpgsm->colon.fnc)
843 4 : {
844 : /* We are using the colon handler even for plain inline data
845 : - strange name for that function but for historic reasons
846 : we keep it. */
847 : /* FIXME We can't use this for binary data because we
848 : assume this is a string. For the current usage of colon
849 : output it is correct. */
850 4 : char *src = line + 2;
851 4 : char *end = line + linelen;
852 : char *dst;
853 4 : char **aline = &gpgsm->colon.attic.line;
854 4 : int *alinelen = &gpgsm->colon.attic.linelen;
855 :
856 4 : if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
857 : {
858 3 : char *newline = realloc (*aline, *alinelen + linelen + 1);
859 3 : if (!newline)
860 0 : err = gpg_error_from_syserror ();
861 : else
862 : {
863 3 : *aline = newline;
864 3 : gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
865 : }
866 : }
867 4 : if (!err)
868 : {
869 4 : dst = *aline + *alinelen;
870 :
871 2401 : while (!err && src < end)
872 : {
873 2393 : if (*src == '%' && src + 2 < end)
874 : {
875 : /* Handle escaped characters. */
876 21 : ++src;
877 21 : *dst = _gpgme_hextobyte (src);
878 21 : (*alinelen)++;
879 21 : src += 2;
880 : }
881 : else
882 : {
883 2372 : *dst = *src++;
884 2372 : (*alinelen)++;
885 : }
886 :
887 2393 : if (*dst == '\n')
888 : {
889 : /* Terminate the pending line, pass it to the colon
890 : handler and reset it. */
891 :
892 21 : gpgsm->colon.any = 1;
893 21 : if (*alinelen > 1 && *(dst - 1) == '\r')
894 0 : dst--;
895 21 : *dst = '\0';
896 :
897 : /* FIXME How should we handle the return code? */
898 21 : err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
899 21 : if (!err)
900 : {
901 21 : dst = *aline;
902 21 : *alinelen = 0;
903 : }
904 : }
905 : else
906 2372 : dst++;
907 : }
908 : }
909 4 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
910 : "fd 0x%x: D line; final status: %s",
911 : fd, err? gpg_strerror (err):"ok");
912 : }
913 17 : else if (linelen > 2
914 17 : && line[0] == 'D' && line[1] == ' '
915 0 : && gpgsm->inline_data)
916 0 : {
917 0 : char *src = line + 2;
918 0 : char *end = line + linelen;
919 0 : char *dst = src;
920 : gpgme_ssize_t nwritten;
921 :
922 0 : linelen = 0;
923 0 : while (src < end)
924 : {
925 0 : if (*src == '%' && src + 2 < end)
926 : {
927 : /* Handle escaped characters. */
928 0 : ++src;
929 0 : *dst++ = _gpgme_hextobyte (src);
930 0 : src += 2;
931 : }
932 : else
933 0 : *dst++ = *src++;
934 :
935 0 : linelen++;
936 : }
937 :
938 0 : src = line + 2;
939 0 : while (linelen > 0)
940 : {
941 0 : nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
942 0 : if (!nwritten || (nwritten < 0 && errno != EINTR)
943 0 : || nwritten > linelen)
944 : {
945 0 : err = gpg_error_from_syserror ();
946 0 : break;
947 : }
948 0 : src += nwritten;
949 0 : linelen -= nwritten;
950 : }
951 :
952 0 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
953 : "fd 0x%x: D inlinedata; final status: %s",
954 : fd, err? gpg_strerror (err):"ok");
955 : }
956 17 : else if (linelen > 2
957 17 : && line[0] == 'S' && line[1] == ' ')
958 17 : {
959 : char *rest;
960 : gpgme_status_code_t r;
961 :
962 17 : rest = strchr (line + 2, ' ');
963 17 : if (!rest)
964 3 : rest = line + linelen; /* set to an empty string */
965 : else
966 14 : *(rest++) = 0;
967 :
968 17 : r = _gpgme_parse_status (line + 2);
969 :
970 : if (r >= 0)
971 : {
972 17 : if (gpgsm->status.fnc)
973 17 : err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
974 : }
975 : else
976 : fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
977 17 : TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
978 : "fd 0x%x: S line (%s) - final status: %s",
979 : fd, line+2, err? gpg_strerror (err):"ok");
980 : }
981 0 : else if (linelen >= 7
982 0 : && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
983 0 : && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
984 0 : && line[6] == 'E'
985 0 : && (line[7] == '\0' || line[7] == ' '))
986 : {
987 0 : char *keyword = line+7;
988 :
989 0 : while (*keyword == ' ')
990 0 : keyword++;;
991 0 : default_inq_cb (gpgsm, keyword);
992 0 : assuan_write_line (gpgsm->assuan_ctx, "END");
993 : }
994 :
995 : }
996 21 : while (!err && assuan_pending_line (gpgsm->assuan_ctx));
997 :
998 14 : return err;
999 : }
1000 :
1001 :
1002 : static gpgme_error_t
1003 32 : add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
1004 : {
1005 : gpgme_error_t err;
1006 :
1007 32 : TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
1008 : "fd %d, dir %d", iocbd->fd, iocbd->dir);
1009 32 : err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1010 : iocbd->fd, iocbd->dir,
1011 : handler, iocbd->data, &iocbd->tag);
1012 32 : if (err)
1013 0 : return TRACE_ERR (err);
1014 32 : if (!iocbd->dir)
1015 : /* FIXME Kludge around poll() problem. */
1016 10 : err = _gpgme_io_set_nonblocking (iocbd->fd);
1017 32 : return TRACE_ERR (err);
1018 : }
1019 :
1020 :
1021 : static gpgme_error_t
1022 14 : start (engine_gpgsm_t gpgsm, const char *command)
1023 : {
1024 : gpgme_error_t err;
1025 : assuan_fd_t afdlist[5];
1026 : int fdlist[5];
1027 : int nfds;
1028 : int i;
1029 :
1030 : /* We need to know the fd used by assuan for reads. We do this by
1031 : using the assumption that the first returned fd from
1032 : assuan_get_active_fds() is always this one. */
1033 14 : nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1034 : afdlist, DIM (afdlist));
1035 14 : if (nfds < 1)
1036 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1037 : /* For now... */
1038 28 : for (i = 0; i < nfds; i++)
1039 14 : fdlist[i] = (int) afdlist[i];
1040 :
1041 : /* We "duplicate" the file descriptor, so we can close it here (we
1042 : can't close fdlist[0], as that is closed by libassuan, and
1043 : closing it here might cause libassuan to close some unrelated FD
1044 : later). Alternatively, we could special case status_fd and
1045 : register/unregister it manually as needed, but this increases
1046 : code duplication and is more complicated as we can not use the
1047 : close notifications etc. A third alternative would be to let
1048 : Assuan know that we closed the FD, but that complicates the
1049 : Assuan interface. */
1050 :
1051 14 : gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1052 14 : if (gpgsm->status_cb.fd < 0)
1053 0 : return gpg_error_from_syserror ();
1054 :
1055 14 : if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1056 : close_notify_handler, gpgsm))
1057 : {
1058 0 : _gpgme_io_close (gpgsm->status_cb.fd);
1059 0 : gpgsm->status_cb.fd = -1;
1060 0 : return gpg_error (GPG_ERR_GENERAL);
1061 : }
1062 :
1063 14 : err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1064 14 : if (!err && gpgsm->input_cb.fd != -1)
1065 8 : err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1066 14 : if (!err && gpgsm->output_cb.fd != -1)
1067 8 : err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1068 14 : if (!err && gpgsm->message_cb.fd != -1)
1069 2 : err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1070 :
1071 14 : if (!err)
1072 14 : err = assuan_write_line (gpgsm->assuan_ctx, command);
1073 :
1074 14 : if (!err)
1075 14 : gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1076 :
1077 14 : return err;
1078 : }
1079 :
1080 :
1081 : #if USE_DESCRIPTOR_PASSING
1082 : static gpgme_error_t
1083 4 : gpgsm_reset (void *engine)
1084 : {
1085 4 : engine_gpgsm_t gpgsm = engine;
1086 :
1087 : /* IF we have an active connection we must send a reset because we
1088 : need to reset the list of signers. Note that RESET does not
1089 : reset OPTION commands. */
1090 8 : return (gpgsm->assuan_ctx
1091 : ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
1092 4 : : 0);
1093 : }
1094 : #endif
1095 :
1096 :
1097 :
1098 : static gpgme_error_t
1099 1 : gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1100 : {
1101 1 : engine_gpgsm_t gpgsm = engine;
1102 : gpgme_error_t err;
1103 :
1104 1 : if (!gpgsm)
1105 0 : return gpg_error (GPG_ERR_INV_VALUE);
1106 :
1107 1 : gpgsm->input_cb.data = ciph;
1108 1 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1109 1 : if (err)
1110 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1111 1 : gpgsm->output_cb.data = plain;
1112 1 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1113 1 : if (err)
1114 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1115 1 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1116 1 : gpgsm->inline_data = NULL;
1117 :
1118 1 : err = start (engine, "DECRYPT");
1119 1 : return err;
1120 : }
1121 :
1122 :
1123 : static gpgme_error_t
1124 0 : gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1125 : {
1126 0 : engine_gpgsm_t gpgsm = engine;
1127 : gpgme_error_t err;
1128 0 : char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1129 0 : char *linep = fpr;
1130 : char *line;
1131 0 : int length = 8; /* "DELKEYS " */
1132 :
1133 0 : if (!fpr)
1134 0 : return gpg_error (GPG_ERR_INV_VALUE);
1135 :
1136 0 : while (*linep)
1137 : {
1138 0 : length++;
1139 0 : if (*linep == '%' || *linep == ' ' || *linep == '+')
1140 0 : length += 2;
1141 0 : linep++;
1142 : }
1143 0 : length++;
1144 :
1145 0 : line = malloc (length);
1146 0 : if (!line)
1147 0 : return gpg_error_from_syserror ();
1148 :
1149 0 : strcpy (line, "DELKEYS ");
1150 0 : linep = &line[8];
1151 :
1152 0 : while (*fpr)
1153 : {
1154 0 : switch (*fpr)
1155 : {
1156 : case '%':
1157 0 : *(linep++) = '%';
1158 0 : *(linep++) = '2';
1159 0 : *(linep++) = '5';
1160 0 : break;
1161 : case ' ':
1162 0 : *(linep++) = '%';
1163 0 : *(linep++) = '2';
1164 0 : *(linep++) = '0';
1165 0 : break;
1166 : case '+':
1167 0 : *(linep++) = '%';
1168 0 : *(linep++) = '2';
1169 0 : *(linep++) = 'B';
1170 0 : break;
1171 : default:
1172 0 : *(linep++) = *fpr;
1173 0 : break;
1174 : }
1175 0 : fpr++;
1176 : }
1177 0 : *linep = '\0';
1178 :
1179 0 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1180 0 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1181 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1182 0 : gpgsm->inline_data = NULL;
1183 :
1184 0 : err = start (gpgsm, line);
1185 0 : free (line);
1186 :
1187 0 : return err;
1188 : }
1189 :
1190 :
1191 : static gpgme_error_t
1192 1 : set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1193 : {
1194 1 : gpgme_error_t err = 0;
1195 : char *line;
1196 : int linelen;
1197 1 : int invalid_recipients = 0;
1198 : int i;
1199 :
1200 1 : linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
1201 1 : line = malloc (10 + 40 + 1);
1202 1 : if (!line)
1203 0 : return gpg_error_from_syserror ();
1204 1 : strcpy (line, "RECIPIENT ");
1205 2 : for (i =0; !err && recp[i]; i++)
1206 : {
1207 : char *fpr;
1208 : int newlen;
1209 :
1210 1 : if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1211 : {
1212 0 : invalid_recipients++;
1213 0 : continue;
1214 : }
1215 1 : fpr = recp[i]->subkeys->fpr;
1216 :
1217 1 : newlen = 11 + strlen (fpr);
1218 1 : if (linelen < newlen)
1219 : {
1220 0 : char *newline = realloc (line, newlen);
1221 0 : if (! newline)
1222 : {
1223 0 : int saved_err = gpg_error_from_syserror ();
1224 0 : free (line);
1225 0 : return saved_err;
1226 : }
1227 0 : line = newline;
1228 0 : linelen = newlen;
1229 : }
1230 1 : strcpy (&line[10], fpr);
1231 :
1232 1 : err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
1233 : gpgsm->status.fnc_value);
1234 : /* FIXME: This requires more work. */
1235 1 : if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1236 0 : invalid_recipients++;
1237 1 : else if (err)
1238 : {
1239 0 : free (line);
1240 0 : return err;
1241 : }
1242 : }
1243 1 : free (line);
1244 1 : return gpg_error (invalid_recipients
1245 : ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1246 : }
1247 :
1248 :
1249 : static gpgme_error_t
1250 1 : gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1251 : gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1252 : {
1253 1 : engine_gpgsm_t gpgsm = engine;
1254 : gpgme_error_t err;
1255 :
1256 1 : if (!gpgsm)
1257 0 : return gpg_error (GPG_ERR_INV_VALUE);
1258 1 : if (!recp)
1259 0 : return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1260 :
1261 1 : if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
1262 : {
1263 0 : err = gpgsm_assuan_simple_command (gpgsm,
1264 : "OPTION no-encrypt-to", NULL, NULL);
1265 0 : if (err)
1266 0 : return err;
1267 : }
1268 :
1269 1 : gpgsm->input_cb.data = plain;
1270 1 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1271 1 : if (err)
1272 0 : return err;
1273 1 : gpgsm->output_cb.data = ciph;
1274 1 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1275 0 : : map_data_enc (gpgsm->output_cb.data));
1276 1 : if (err)
1277 0 : return err;
1278 1 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1279 1 : gpgsm->inline_data = NULL;
1280 :
1281 1 : err = set_recipients (gpgsm, recp);
1282 :
1283 1 : if (!err)
1284 1 : err = start (gpgsm, "ENCRYPT");
1285 :
1286 1 : return err;
1287 : }
1288 :
1289 :
1290 : static gpgme_error_t
1291 0 : gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
1292 : gpgme_data_t keydata, int use_armor)
1293 : {
1294 0 : engine_gpgsm_t gpgsm = engine;
1295 0 : gpgme_error_t err = 0;
1296 : char *cmd;
1297 :
1298 0 : if (!gpgsm)
1299 0 : return gpg_error (GPG_ERR_INV_VALUE);
1300 :
1301 0 : if (!pattern)
1302 0 : pattern = "";
1303 :
1304 0 : cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
1305 0 : if (!cmd)
1306 0 : return gpg_error_from_syserror ();
1307 :
1308 0 : strcpy (cmd, "EXPORT ");
1309 0 : if ((mode & GPGME_EXPORT_MODE_SECRET))
1310 : {
1311 0 : strcat (cmd, "--secret ");
1312 0 : if ((mode & GPGME_EXPORT_MODE_RAW))
1313 0 : strcat (cmd, "--raw ");
1314 0 : else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1315 0 : strcat (cmd, "--pkcs12 ");
1316 : }
1317 0 : strcat (cmd, pattern);
1318 :
1319 0 : gpgsm->output_cb.data = keydata;
1320 0 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1321 0 : : map_data_enc (gpgsm->output_cb.data));
1322 0 : if (err)
1323 0 : return err;
1324 0 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1325 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1326 0 : gpgsm->inline_data = NULL;
1327 :
1328 0 : err = start (gpgsm, cmd);
1329 0 : free (cmd);
1330 0 : return err;
1331 : }
1332 :
1333 :
1334 : static gpgme_error_t
1335 2 : gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
1336 : gpgme_data_t keydata, int use_armor)
1337 : {
1338 2 : engine_gpgsm_t gpgsm = engine;
1339 2 : gpgme_error_t err = 0;
1340 : char *line;
1341 : /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'. */
1342 2 : int length = 7 + 9 + 9 + 1;
1343 : char *linep;
1344 :
1345 2 : if (!gpgsm)
1346 0 : return gpg_error (GPG_ERR_INV_VALUE);
1347 :
1348 2 : if (pattern && *pattern)
1349 : {
1350 2 : const char **pat = pattern;
1351 :
1352 7 : while (*pat)
1353 : {
1354 3 : const char *patlet = *pat;
1355 :
1356 117 : while (*patlet)
1357 : {
1358 111 : length++;
1359 111 : if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1360 7 : length += 2;
1361 111 : patlet++;
1362 : }
1363 3 : pat++;
1364 3 : length++;
1365 : }
1366 : }
1367 2 : line = malloc (length);
1368 2 : if (!line)
1369 0 : return gpg_error_from_syserror ();
1370 :
1371 2 : strcpy (line, "EXPORT ");
1372 2 : if ((mode & GPGME_EXPORT_MODE_SECRET))
1373 : {
1374 0 : strcat (line, "--secret ");
1375 0 : if ((mode & GPGME_EXPORT_MODE_RAW))
1376 0 : strcat (line, "--raw ");
1377 0 : else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1378 0 : strcat (line, "--pkcs12 ");
1379 : }
1380 2 : linep = &line[strlen (line)];
1381 :
1382 2 : if (pattern && *pattern)
1383 : {
1384 7 : while (*pattern)
1385 : {
1386 3 : const char *patlet = *pattern;
1387 :
1388 117 : while (*patlet)
1389 : {
1390 111 : switch (*patlet)
1391 : {
1392 : case '%':
1393 0 : *(linep++) = '%';
1394 0 : *(linep++) = '2';
1395 0 : *(linep++) = '5';
1396 0 : break;
1397 : case ' ':
1398 7 : *(linep++) = '%';
1399 7 : *(linep++) = '2';
1400 7 : *(linep++) = '0';
1401 7 : break;
1402 : case '+':
1403 0 : *(linep++) = '%';
1404 0 : *(linep++) = '2';
1405 0 : *(linep++) = 'B';
1406 0 : break;
1407 : default:
1408 104 : *(linep++) = *patlet;
1409 104 : break;
1410 : }
1411 111 : patlet++;
1412 : }
1413 3 : pattern++;
1414 3 : if (*pattern)
1415 1 : *linep++ = ' ';
1416 : }
1417 : }
1418 2 : *linep = '\0';
1419 :
1420 2 : gpgsm->output_cb.data = keydata;
1421 2 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1422 0 : : map_data_enc (gpgsm->output_cb.data));
1423 2 : if (err)
1424 0 : return err;
1425 2 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1426 2 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1427 2 : gpgsm->inline_data = NULL;
1428 :
1429 2 : err = start (gpgsm, line);
1430 2 : free (line);
1431 2 : return err;
1432 : }
1433 :
1434 :
1435 : static gpgme_error_t
1436 0 : gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1437 : gpgme_data_t pubkey, gpgme_data_t seckey)
1438 : {
1439 0 : engine_gpgsm_t gpgsm = engine;
1440 : gpgme_error_t err;
1441 :
1442 0 : if (!gpgsm || !pubkey || seckey)
1443 0 : return gpg_error (GPG_ERR_INV_VALUE);
1444 :
1445 0 : gpgsm->input_cb.data = help_data;
1446 0 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1447 0 : if (err)
1448 0 : return err;
1449 0 : gpgsm->output_cb.data = pubkey;
1450 0 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1451 0 : : map_data_enc (gpgsm->output_cb.data));
1452 0 : if (err)
1453 0 : return err;
1454 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1455 0 : gpgsm->inline_data = NULL;
1456 :
1457 0 : err = start (gpgsm, "GENKEY");
1458 0 : return err;
1459 : }
1460 :
1461 :
1462 : static gpgme_error_t
1463 2 : gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
1464 : {
1465 2 : engine_gpgsm_t gpgsm = engine;
1466 : gpgme_error_t err;
1467 : gpgme_data_encoding_t dataenc;
1468 : int idx;
1469 :
1470 2 : if (!gpgsm)
1471 0 : return gpg_error (GPG_ERR_INV_VALUE);
1472 :
1473 2 : if (keydata && keyarray)
1474 0 : return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */
1475 :
1476 2 : dataenc = gpgme_data_get_encoding (keydata);
1477 :
1478 2 : if (keyarray)
1479 : {
1480 : size_t buflen;
1481 : char *buffer, *p;
1482 :
1483 : /* Fist check whether the engine already features the
1484 : --re-import option. */
1485 0 : err = gpgsm_assuan_simple_command
1486 : (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
1487 0 : if (err)
1488 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
1489 :
1490 : /* Create an internal data object with a list of all
1491 : fingerprints. The data object and its memory (to avoid an
1492 : extra copy by gpgme_data_new_from_mem) are stored in two
1493 : variables which are released by the close_notify_handler. */
1494 0 : for (idx=0, buflen=0; keyarray[idx]; idx++)
1495 : {
1496 0 : if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1497 0 : && keyarray[idx]->subkeys
1498 0 : && keyarray[idx]->subkeys->fpr
1499 0 : && *keyarray[idx]->subkeys->fpr)
1500 0 : buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
1501 : }
1502 : /* Allocate a bufer with extra space for the trailing Nul
1503 : introduced by the use of stpcpy. */
1504 0 : buffer = malloc (buflen+1);
1505 0 : if (!buffer)
1506 0 : return gpg_error_from_syserror ();
1507 0 : for (idx=0, p = buffer; keyarray[idx]; idx++)
1508 : {
1509 0 : if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1510 0 : && keyarray[idx]->subkeys
1511 0 : && keyarray[idx]->subkeys->fpr
1512 0 : && *keyarray[idx]->subkeys->fpr)
1513 0 : p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
1514 : }
1515 :
1516 0 : err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
1517 : buffer, buflen, 0);
1518 0 : if (err)
1519 : {
1520 0 : free (buffer);
1521 0 : return err;
1522 : }
1523 0 : gpgsm->input_helper_memory = buffer;
1524 :
1525 0 : gpgsm->input_cb.data = gpgsm->input_helper_data;
1526 0 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1527 0 : if (err)
1528 : {
1529 0 : gpgme_data_release (gpgsm->input_helper_data);
1530 0 : gpgsm->input_helper_data = NULL;
1531 0 : free (gpgsm->input_helper_memory);
1532 0 : gpgsm->input_helper_memory = NULL;
1533 0 : return err;
1534 : }
1535 0 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1536 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1537 0 : gpgsm->inline_data = NULL;
1538 :
1539 0 : return start (gpgsm, "IMPORT --re-import");
1540 : }
1541 2 : else if (dataenc == GPGME_DATA_ENCODING_URL
1542 2 : || dataenc == GPGME_DATA_ENCODING_URL0
1543 2 : || dataenc == GPGME_DATA_ENCODING_URLESC)
1544 : {
1545 0 : return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1546 : }
1547 : else
1548 : {
1549 2 : gpgsm->input_cb.data = keydata;
1550 2 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1551 2 : if (err)
1552 0 : return err;
1553 2 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1554 2 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1555 2 : gpgsm->inline_data = NULL;
1556 :
1557 2 : return start (gpgsm, "IMPORT");
1558 : }
1559 : }
1560 :
1561 :
1562 : static gpgme_error_t
1563 2 : gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1564 : gpgme_keylist_mode_t mode, int engine_flags)
1565 : {
1566 2 : engine_gpgsm_t gpgsm = engine;
1567 : char *line;
1568 : gpgme_error_t err;
1569 2 : int list_mode = 0;
1570 :
1571 2 : if (mode & GPGME_KEYLIST_MODE_LOCAL)
1572 2 : list_mode |= 1;
1573 2 : if (mode & GPGME_KEYLIST_MODE_EXTERN)
1574 0 : list_mode |= 2;
1575 :
1576 2 : if (!pattern)
1577 1 : pattern = "";
1578 :
1579 : /* Hack to make sure that the agent is started. Only if the agent
1580 : has been started an application may connect to the agent via
1581 : GPGME_PROTOCOL_ASSUAN - for example to look for smartcards. We
1582 : do this only if a secret key listing has been requested. In
1583 : general this is not needed because a secret key listing starts
1584 : the agent. However on a fresh installation no public keys are
1585 : available and thus there is no need for gpgsm to ask the agent
1586 : whether a secret key exists for the public key. */
1587 2 : if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
1588 0 : gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL);
1589 :
1590 : /* Always send list-mode option because RESET does not reset it. */
1591 2 : if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1592 0 : return gpg_error_from_syserror ();
1593 2 : err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1594 2 : free (line);
1595 2 : if (err)
1596 0 : return err;
1597 :
1598 :
1599 : /* Always send key validation because RESET does not reset it. */
1600 :
1601 : /* Use the validation mode if requested. We don't check for an error
1602 : yet because this is a pretty fresh gpgsm features. */
1603 2 : gpgsm_assuan_simple_command (gpgsm,
1604 2 : (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1605 : "OPTION with-validation=1":
1606 : "OPTION with-validation=0" ,
1607 : NULL, NULL);
1608 : /* Include the ephemeral keys if requested. We don't check for an error
1609 : yet because this is a pretty fresh gpgsm features. */
1610 2 : gpgsm_assuan_simple_command (gpgsm,
1611 2 : (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1612 : "OPTION with-ephemeral-keys=1":
1613 : "OPTION with-ephemeral-keys=0" ,
1614 : NULL, NULL);
1615 2 : gpgsm_assuan_simple_command (gpgsm,
1616 2 : (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1617 : "OPTION with-secret=1":
1618 : "OPTION with-secret=0" ,
1619 : NULL, NULL);
1620 2 : gpgsm_assuan_simple_command (gpgsm,
1621 2 : (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1622 : "OPTION offline=1":
1623 : "OPTION offline=0" ,
1624 : NULL, NULL);
1625 :
1626 :
1627 : /* Length is "LISTSECRETKEYS " + p + '\0'. */
1628 2 : line = malloc (15 + strlen (pattern) + 1);
1629 2 : if (!line)
1630 0 : return gpg_error_from_syserror ();
1631 2 : if (secret_only)
1632 : {
1633 0 : strcpy (line, "LISTSECRETKEYS ");
1634 0 : strcpy (&line[15], pattern);
1635 : }
1636 : else
1637 : {
1638 2 : strcpy (line, "LISTKEYS ");
1639 2 : strcpy (&line[9], pattern);
1640 : }
1641 :
1642 2 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1643 2 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1644 2 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1645 2 : gpgsm->inline_data = NULL;
1646 :
1647 2 : err = start (gpgsm, line);
1648 2 : free (line);
1649 2 : return err;
1650 : }
1651 :
1652 :
1653 : static gpgme_error_t
1654 0 : gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1655 : int reserved, gpgme_keylist_mode_t mode, int engine_flags)
1656 : {
1657 0 : engine_gpgsm_t gpgsm = engine;
1658 : char *line;
1659 : gpgme_error_t err;
1660 : /* Length is "LISTSECRETKEYS " + p + '\0'. */
1661 0 : int length = 15 + 1;
1662 : char *linep;
1663 0 : int any_pattern = 0;
1664 0 : int list_mode = 0;
1665 :
1666 0 : if (reserved)
1667 0 : return gpg_error (GPG_ERR_INV_VALUE);
1668 :
1669 0 : if (mode & GPGME_KEYLIST_MODE_LOCAL)
1670 0 : list_mode |= 1;
1671 0 : if (mode & GPGME_KEYLIST_MODE_EXTERN)
1672 0 : list_mode |= 2;
1673 :
1674 : /* Always send list-mode option because RESET does not reset it. */
1675 0 : if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1676 0 : return gpg_error_from_syserror ();
1677 0 : err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1678 0 : free (line);
1679 0 : if (err)
1680 0 : return err;
1681 :
1682 : /* Always send key validation because RESET does not reset it. */
1683 : /* Use the validation mode if required. We don't check for an error
1684 : yet because this is a pretty fresh gpgsm features. */
1685 0 : gpgsm_assuan_simple_command (gpgsm,
1686 0 : (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1687 : "OPTION with-validation=1":
1688 : "OPTION with-validation=0" ,
1689 : NULL, NULL);
1690 0 : gpgsm_assuan_simple_command (gpgsm,
1691 0 : (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1692 : "OPTION with-secret=1":
1693 : "OPTION with-secret=0" ,
1694 : NULL, NULL);
1695 0 : gpgsm_assuan_simple_command (gpgsm,
1696 0 : (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1697 : "OPTION offline=1":
1698 : "OPTION offline=0" ,
1699 : NULL, NULL);
1700 :
1701 0 : if (pattern && *pattern)
1702 : {
1703 0 : const char **pat = pattern;
1704 :
1705 0 : while (*pat)
1706 : {
1707 0 : const char *patlet = *pat;
1708 :
1709 0 : while (*patlet)
1710 : {
1711 0 : length++;
1712 0 : if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1713 0 : length += 2;
1714 0 : patlet++;
1715 : }
1716 0 : pat++;
1717 0 : length++;
1718 : }
1719 : }
1720 0 : line = malloc (length);
1721 0 : if (!line)
1722 0 : return gpg_error_from_syserror ();
1723 0 : if (secret_only)
1724 : {
1725 0 : strcpy (line, "LISTSECRETKEYS ");
1726 0 : linep = &line[15];
1727 : }
1728 : else
1729 : {
1730 0 : strcpy (line, "LISTKEYS ");
1731 0 : linep = &line[9];
1732 : }
1733 :
1734 0 : if (pattern && *pattern)
1735 : {
1736 0 : while (*pattern)
1737 : {
1738 0 : const char *patlet = *pattern;
1739 :
1740 0 : while (*patlet)
1741 : {
1742 0 : switch (*patlet)
1743 : {
1744 : case '%':
1745 0 : *(linep++) = '%';
1746 0 : *(linep++) = '2';
1747 0 : *(linep++) = '5';
1748 0 : break;
1749 : case ' ':
1750 0 : *(linep++) = '%';
1751 0 : *(linep++) = '2';
1752 0 : *(linep++) = '0';
1753 0 : break;
1754 : case '+':
1755 0 : *(linep++) = '%';
1756 0 : *(linep++) = '2';
1757 0 : *(linep++) = 'B';
1758 0 : break;
1759 : default:
1760 0 : *(linep++) = *patlet;
1761 0 : break;
1762 : }
1763 0 : patlet++;
1764 : }
1765 0 : any_pattern = 1;
1766 0 : *linep++ = ' ';
1767 0 : pattern++;
1768 : }
1769 : }
1770 0 : if (any_pattern)
1771 0 : linep--;
1772 0 : *linep = '\0';
1773 :
1774 0 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1775 0 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1776 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1777 0 : gpgsm->inline_data = NULL;
1778 :
1779 0 : err = start (gpgsm, line);
1780 0 : free (line);
1781 0 : return err;
1782 : }
1783 :
1784 :
1785 : static gpgme_error_t
1786 2 : gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1787 : gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1788 : int include_certs, gpgme_ctx_t ctx /* FIXME */)
1789 : {
1790 2 : engine_gpgsm_t gpgsm = engine;
1791 : gpgme_error_t err;
1792 : char *assuan_cmd;
1793 : int i;
1794 : gpgme_key_t key;
1795 :
1796 2 : if (!gpgsm)
1797 0 : return gpg_error (GPG_ERR_INV_VALUE);
1798 :
1799 : /* FIXME: This does not work as RESET does not reset it so we can't
1800 : revert back to default. */
1801 2 : if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1802 : {
1803 : /* FIXME: Make sure that if we run multiple operations, that we
1804 : can reset any previously set value in case the default is
1805 : requested. */
1806 :
1807 0 : if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1808 0 : return gpg_error_from_syserror ();
1809 0 : err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
1810 0 : free (assuan_cmd);
1811 0 : if (err)
1812 0 : return err;
1813 : }
1814 :
1815 2 : for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1816 : {
1817 0 : const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1818 0 : if (s && strlen (s) < 80)
1819 0 : {
1820 : char buf[100];
1821 :
1822 0 : strcpy (stpcpy (buf, "SIGNER "), s);
1823 0 : err = gpgsm_assuan_simple_command (gpgsm, buf,
1824 : gpgsm->status.fnc,
1825 : gpgsm->status.fnc_value);
1826 : }
1827 : else
1828 0 : err = gpg_error (GPG_ERR_INV_VALUE);
1829 0 : gpgme_key_unref (key);
1830 0 : if (err)
1831 0 : return err;
1832 : }
1833 :
1834 2 : gpgsm->input_cb.data = in;
1835 2 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1836 2 : if (err)
1837 0 : return err;
1838 2 : gpgsm->output_cb.data = out;
1839 2 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1840 0 : : map_data_enc (gpgsm->output_cb.data));
1841 2 : if (err)
1842 0 : return err;
1843 2 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1844 2 : gpgsm->inline_data = NULL;
1845 :
1846 2 : err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1847 : ? "SIGN --detached" : "SIGN");
1848 2 : return err;
1849 : }
1850 :
1851 :
1852 : static gpgme_error_t
1853 2 : gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1854 : gpgme_data_t plaintext)
1855 : {
1856 2 : engine_gpgsm_t gpgsm = engine;
1857 : gpgme_error_t err;
1858 :
1859 2 : if (!gpgsm)
1860 0 : return gpg_error (GPG_ERR_INV_VALUE);
1861 :
1862 2 : gpgsm->input_cb.data = sig;
1863 2 : err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1864 2 : if (err)
1865 0 : return err;
1866 2 : if (plaintext)
1867 : {
1868 : /* Normal or cleartext signature. */
1869 0 : gpgsm->output_cb.data = plaintext;
1870 0 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1871 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1872 : }
1873 : else
1874 : {
1875 : /* Detached signature. */
1876 2 : gpgsm->message_cb.data = signed_text;
1877 2 : err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1878 2 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1879 : }
1880 2 : gpgsm->inline_data = NULL;
1881 :
1882 2 : if (!err)
1883 2 : err = start (gpgsm, "VERIFY");
1884 :
1885 2 : return err;
1886 : }
1887 :
1888 :
1889 : /* Send the GETAUDITLOG command. The result is saved to a gpgme data
1890 : object. */
1891 : static gpgme_error_t
1892 2 : gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1893 : {
1894 2 : engine_gpgsm_t gpgsm = engine;
1895 2 : gpgme_error_t err = 0;
1896 :
1897 2 : if (!gpgsm || !output)
1898 0 : return gpg_error (GPG_ERR_INV_VALUE);
1899 :
1900 : #if USE_DESCRIPTOR_PASSING
1901 2 : gpgsm->output_cb.data = output;
1902 2 : err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1903 2 : if (err)
1904 0 : return err;
1905 :
1906 2 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1907 2 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1908 2 : gpgsm->inline_data = NULL;
1909 : # define CMD "GETAUDITLOG"
1910 : #else
1911 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1912 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1913 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1914 : gpgsm->inline_data = output;
1915 : # define CMD "GETAUDITLOG --data"
1916 : #endif
1917 :
1918 2 : err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1919 :
1920 2 : return err;
1921 : }
1922 :
1923 :
1924 : /* This sets a status callback for monitoring status lines before they
1925 : * are passed to a caller set handler. */
1926 : static void
1927 0 : gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
1928 : {
1929 0 : engine_gpgsm_t gpgsm = engine;
1930 :
1931 0 : gpgsm->status.mon_cb = cb;
1932 0 : gpgsm->status.mon_cb_value = cb_value;
1933 0 : }
1934 :
1935 :
1936 : static void
1937 14 : gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1938 : void *fnc_value)
1939 : {
1940 14 : engine_gpgsm_t gpgsm = engine;
1941 :
1942 14 : gpgsm->status.fnc = fnc;
1943 14 : gpgsm->status.fnc_value = fnc_value;
1944 14 : }
1945 :
1946 :
1947 : static gpgme_error_t
1948 2 : gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1949 : void *fnc_value)
1950 : {
1951 2 : engine_gpgsm_t gpgsm = engine;
1952 :
1953 2 : gpgsm->colon.fnc = fnc;
1954 2 : gpgsm->colon.fnc_value = fnc_value;
1955 2 : gpgsm->colon.any = 0;
1956 2 : return 0;
1957 : }
1958 :
1959 :
1960 : static void
1961 14 : gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1962 : {
1963 14 : engine_gpgsm_t gpgsm = engine;
1964 14 : gpgsm->io_cbs = *io_cbs;
1965 14 : }
1966 :
1967 :
1968 : static void
1969 36 : gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1970 : {
1971 36 : engine_gpgsm_t gpgsm = engine;
1972 :
1973 36 : TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
1974 : "event %p, type %d, type_data %p",
1975 : gpgsm->io_cbs.event, type, type_data);
1976 36 : if (gpgsm->io_cbs.event)
1977 36 : (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1978 36 : }
1979 :
1980 :
1981 : static gpgme_error_t
1982 0 : gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
1983 : {
1984 0 : engine_gpgsm_t gpgsm = engine;
1985 : gpgme_error_t err;
1986 : char *line;
1987 :
1988 0 : if (!key || !key->subkeys || !key->subkeys->fpr)
1989 0 : return gpg_error (GPG_ERR_INV_CERT_OBJ);
1990 :
1991 0 : if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
1992 0 : return gpg_error_from_syserror ();
1993 :
1994 0 : gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1995 0 : gpgsm_clear_fd (gpgsm, INPUT_FD);
1996 0 : gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1997 0 : gpgsm->inline_data = NULL;
1998 :
1999 0 : err = start (gpgsm, line);
2000 0 : free (line);
2001 :
2002 0 : return err;
2003 : }
2004 :
2005 :
2006 :
2007 : struct engine_ops _gpgme_engine_ops_gpgsm =
2008 : {
2009 : /* Static functions. */
2010 : _gpgme_get_default_gpgsm_name,
2011 : NULL,
2012 : gpgsm_get_version,
2013 : gpgsm_get_req_version,
2014 : gpgsm_new,
2015 :
2016 : /* Member functions. */
2017 : gpgsm_release,
2018 : #if USE_DESCRIPTOR_PASSING
2019 : gpgsm_reset,
2020 : #else
2021 : NULL, /* reset */
2022 : #endif
2023 : gpgsm_set_status_cb,
2024 : gpgsm_set_status_handler,
2025 : NULL, /* set_command_handler */
2026 : gpgsm_set_colon_line_handler,
2027 : gpgsm_set_locale,
2028 : NULL, /* set_protocol */
2029 : gpgsm_decrypt,
2030 : gpgsm_decrypt,
2031 : gpgsm_delete, /* decrypt_verify */
2032 : NULL, /* edit */
2033 : gpgsm_encrypt,
2034 : NULL, /* encrypt_sign */
2035 : gpgsm_export,
2036 : gpgsm_export_ext,
2037 : gpgsm_genkey,
2038 : gpgsm_import,
2039 : gpgsm_keylist,
2040 : gpgsm_keylist_ext,
2041 : gpgsm_sign,
2042 : NULL, /* trustlist */
2043 : gpgsm_verify,
2044 : gpgsm_getauditlog,
2045 : NULL, /* opassuan_transact */
2046 : NULL, /* conf_load */
2047 : NULL, /* conf_save */
2048 : gpgsm_set_io_cbs,
2049 : gpgsm_io_event,
2050 : gpgsm_cancel,
2051 : NULL, /* cancel_op */
2052 : gpgsm_passwd,
2053 : NULL, /* set_pinentry_mode */
2054 : NULL /* opspawn */
2055 : };
|