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