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