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