1 /* engine-assuan.c - Low-level Assuan protocol engine
2 * Copyright (C) 2009 g10 Code GmbH
4 * This file is part of GPGME.
6 * GPGME is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
11 * GPGME is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
21 Note: This engine requires a modern Assuan server which uses
22 gpg-error codes. In particular there is no backward compatible
23 mapping of old Assuan error codes implemented.
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
55 #include "engine-backend.h"
60 int fd; /* FD we talk about. */
61 int server_fd;/* Server FD for this connection. */
62 int dir; /* Inbound/Outbound, maybe given implicit? */
63 void *data; /* Handler-specific data. */
64 void *tag; /* ID from the user for gpgme_remove_io_callback. */
67 /* Engine instance data. */
70 assuan_context_t assuan_ctx;
75 iocb_data_t status_cb;
77 struct gpgme_io_cbs io_cbs;
79 /* Hack for old opassuan.c interface, see there the result struct. */
80 gpg_error_t last_op_err;
82 /* User provided callbacks. */
84 gpgme_assuan_data_cb_t data_cb;
87 gpgme_assuan_inquire_cb_t inq_cb;
90 gpgme_assuan_status_cb_t status_cb;
91 void *status_cb_value;
96 int gpg_agent:1; /* Assume this is a gpg-agent connection. */
100 typedef struct engine_llass *engine_llass_t;
103 gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
105 engine_llass_t llass = engine;
106 return llass->last_op_err;
111 static void llass_io_event (void *engine,
112 gpgme_event_io_t type, void *type_data);
118 /* return the default home directory. */
120 llass_get_home_dir (void)
122 /* For this engine the home directory is not a filename but a string
123 used to convey options. The exclamation mark is a marker to show
124 that this is not a directory name. Options are strings delimited
125 by a space. The only option defined for now is GPG_AGENT to
126 enable GPG_AGENT specific commands to send to the server at
127 connection startup. */
132 llass_get_version (const char *file_name)
134 return strdup ("1.0");
139 llass_get_req_version (void)
146 close_notify_handler (int fd, void *opaque)
148 engine_llass_t llass = opaque;
151 if (llass->status_cb.fd == fd)
153 if (llass->status_cb.tag)
154 llass->io_cbs.remove (llass->status_cb.tag);
155 llass->status_cb.fd = -1;
156 llass->status_cb.tag = NULL;
163 llass_cancel (void *engine)
165 engine_llass_t llass = engine;
168 return gpg_error (GPG_ERR_INV_VALUE);
170 if (llass->status_cb.fd != -1)
171 _gpgme_io_close (llass->status_cb.fd);
173 if (llass->assuan_ctx)
175 assuan_release (llass->assuan_ctx);
176 llass->assuan_ctx = NULL;
184 llass_cancel_op (void *engine)
186 engine_llass_t llass = engine;
189 return gpg_error (GPG_ERR_INV_VALUE);
191 if (llass->status_cb.fd != -1)
192 _gpgme_io_close (llass->status_cb.fd);
199 llass_release (void *engine)
201 engine_llass_t llass = engine;
206 llass_cancel (engine);
212 /* Create a new instance. If HOME_DIR is NULL standard options for use
213 with gpg-agent are issued. */
215 llass_new (void **engine, const char *file_name, const char *home_dir)
217 gpgme_error_t err = 0;
218 engine_llass_t llass;
221 llass = calloc (1, sizeof *llass);
223 return gpg_error_from_syserror ();
225 llass->status_cb.fd = -1;
226 llass->status_cb.dir = 1;
227 llass->status_cb.tag = 0;
228 llass->status_cb.data = llass;
231 if (home_dir && *home_dir == '!')
234 /* Very simple parser only working for the one option we support. */
235 /* Note that wk promised to write a regression test if this
236 parser will be extended. */
237 if (!strncmp (home_dir, "GPG_AGENT", 9)
238 && (!home_dir[9] || home_dir[9] == ' '))
239 llass->opt.gpg_agent = 1;
242 err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
243 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
247 assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
249 err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
253 if (llass->opt.gpg_agent)
255 char *dft_display = NULL;
257 err = _gpgme_getenv ("DISPLAY", &dft_display);
262 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
264 err = gpg_error_from_syserror ();
270 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
278 if (llass->opt.gpg_agent && isatty (1))
281 char dft_ttyname[64];
282 char *dft_ttytype = NULL;
284 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
287 err = gpg_error_from_errno (rc);
292 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
294 err = gpg_error_from_syserror ();
297 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
303 err = _gpgme_getenv ("TERM", &dft_ttytype);
308 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
310 err = gpg_error_from_syserror ();
316 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
317 NULL, NULL, NULL, NULL);
326 #ifdef HAVE_W32_SYSTEM
327 /* Under Windows we need to use AllowSetForegroundWindow. Tell
328 llass to tell us when it needs it. */
329 if (!err && llass->opt.gpg_agent)
331 err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
332 NULL, NULL, NULL, NULL, NULL, NULL);
333 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
334 err = 0; /* This work only with recent gpg-agents. */
336 #endif /*HAVE_W32_SYSTEM*/
340 /* Close the server ends of the pipes (because of this, we must use
341 the stored server_fd_str in the function start). Our ends are
342 closed in llass_release(). */
345 llass_release (llass);
354 llass_set_locale (void *engine, int category, const char *value)
357 engine_llass_t llass = engine;
361 if (!llass->opt.gpg_agent)
364 /* FIXME: If value is NULL, we need to reset the option to default.
365 But we can't do this. So we error out here. gpg-agent needs
370 else if (category == LC_CTYPE)
373 if (!value && llass->lc_ctype_set)
374 return gpg_error (GPG_ERR_INV_VALUE);
376 llass->lc_ctype_set = 1;
380 else if (category == LC_MESSAGES)
382 catstr = "lc-messages";
383 if (!value && llass->lc_messages_set)
384 return gpg_error (GPG_ERR_INV_VALUE);
386 llass->lc_messages_set = 1;
388 #endif /* LC_MESSAGES */
390 return gpg_error (GPG_ERR_INV_VALUE);
392 /* FIXME: Reset value to default. */
396 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
397 err = gpg_error_from_errno (errno);
400 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
401 NULL, NULL, NULL, NULL);
408 /* This is the inquiry callback. It handles stuff which ee need to
409 handle here and passes everything on to the user callback. */
411 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
415 if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
417 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
420 if (llass->user.inq_cb)
422 gpgme_data_t data = NULL;
424 err = llass->user.inq_cb (llass->user.inq_cb_value,
425 keyword, args, &data);
428 /* FIXME: Returning data is not yet implemented. However we
429 need to allow the caller to cleanup his data object.
430 Thus we run the callback in finish mode immediately. */
431 err = llass->user.inq_cb (llass->user.inq_cb_value,
443 llass_status_handler (void *opaque, int fd)
445 struct io_cb_data *data = (struct io_cb_data *) opaque;
446 engine_llass_t llass = (engine_llass_t) data->handler_value;
447 gpgme_error_t err = 0;
453 err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
456 /* Reading a full line may not be possible when
457 communicating over a socket in nonblocking mode. In this
458 case, we are done for now. */
459 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
461 TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
462 "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
467 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
468 "fd 0x%x: error reading assuan line: %s",
469 fd, gpg_strerror (err));
471 else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
473 char *src = line + 2;
474 char *end = line + linelen;
480 if (*src == '%' && src + 2 < end)
482 /* Handle escaped characters. */
484 *dst++ = _gpgme_hextobyte (src);
494 if (linelen && llass->user.data_cb)
495 err = llass->user.data_cb (llass->user.data_cb_value,
498 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
499 "fd 0x%x: D inlinedata; status from cb: %s",
500 fd, (llass->user.data_cb ?
501 (err? gpg_strerror (err):"ok"):"no callback"));
503 else if (linelen >= 3
504 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
505 && (line[3] == '\0' || line[3] == ' '))
507 /* END received. Tell the data callback. */
508 if (llass->user.data_cb)
509 err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
511 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
512 "fd 0x%x: END line; status from cb: %s",
513 fd, (llass->user.data_cb ?
514 (err? gpg_strerror (err):"ok"):"no callback"));
516 else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
521 for (src=line+2; *src == ' '; src++)
524 args = strchr (src, ' ');
526 args = line + linelen; /* Let it point to an empty string. */
533 if (llass->user.status_cb)
534 err = llass->user.status_cb (llass->user.status_cb_value,
537 TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
538 "fd 0x%x: S line (%s) - status from cb: %s",
539 fd, line+2, (llass->user.status_cb ?
540 (err? gpg_strerror (err):"ok"):"no callback"));
542 else if (linelen >= 7
543 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
544 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
546 && (line[7] == '\0' || line[7] == ' '))
551 for (src=line+7; *src == ' '; src++)
554 args = strchr (src, ' ');
556 args = line + linelen; /* Let it point to an empty string. */
563 err = inquire_cb (llass, src, args);
566 /* Flush and send END. */
567 err = assuan_send_data (llass->assuan_ctx, NULL, 0);
569 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
571 /* Flush and send CANcel. */
572 err = assuan_send_data (llass->assuan_ctx, NULL, 1);
575 else if (linelen >= 3
576 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
577 && (line[3] == '\0' || line[3] == ' '))
582 err = gpg_error (GPG_ERR_GENERAL);
583 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
584 "fd 0x%x: ERR line: %s",
585 fd, err ? gpg_strerror (err) : "ok");
587 /* Command execution errors are not fatal, as we use
588 a session based protocol. */
590 llass->last_op_err = err;
592 /* The caller will do the rest (namely, call cancel_op,
593 which closes status_fd). */
596 else if (linelen >= 2
597 && line[0] == 'O' && line[1] == 'K'
598 && (line[2] == '\0' || line[2] == ' '))
600 TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
601 "fd 0x%x: OK line", fd);
603 llass->last_op_err = 0;
605 _gpgme_io_close (llass->status_cb.fd);
610 /* Comment line or invalid line. */
614 while (!err && assuan_pending_line (llass->assuan_ctx));
621 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
625 TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
626 "fd %d, dir %d", iocbd->fd, iocbd->dir);
627 err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
628 iocbd->fd, iocbd->dir,
629 handler, iocbd->data, &iocbd->tag);
631 return TRACE_ERR (err);
633 /* FIXME Kludge around poll() problem. */
634 err = _gpgme_io_set_nonblocking (iocbd->fd);
635 return TRACE_ERR (err);
640 start (engine_llass_t llass, const char *command)
643 assuan_fd_t afdlist[5];
648 /* We need to know the fd used by assuan for reads. We do this by
649 using the assumption that the first returned fd from
650 assuan_get_active_fds() is always this one. */
651 nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
652 afdlist, DIM (afdlist));
654 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
656 for (i = 0; i < nfds; i++)
657 fdlist[i] = (int) afdlist[i];
659 /* We "duplicate" the file descriptor, so we can close it here (we
660 can't close fdlist[0], as that is closed by libassuan, and
661 closing it here might cause libassuan to close some unrelated FD
662 later). Alternatively, we could special case status_fd and
663 register/unregister it manually as needed, but this increases
664 code duplication and is more complicated as we can not use the
665 close notifications etc. A third alternative would be to let
666 Assuan know that we closed the FD, but that complicates the
669 llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
670 if (llass->status_cb.fd < 0)
671 return gpg_error_from_syserror ();
673 if (_gpgme_io_set_close_notify (llass->status_cb.fd,
674 close_notify_handler, llass))
676 _gpgme_io_close (llass->status_cb.fd);
677 llass->status_cb.fd = -1;
678 return gpg_error (GPG_ERR_GENERAL);
681 err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
683 err = assuan_write_line (llass->assuan_ctx, command);
685 /* FIXME: If *command == '#' no answer is expected. */
688 llass_io_event (llass, GPGME_EVENT_START, NULL);
696 llass_transact (void *engine,
698 gpgme_assuan_data_cb_t data_cb,
700 gpgme_assuan_inquire_cb_t inq_cb,
702 gpgme_assuan_status_cb_t status_cb,
703 void *status_cb_value)
705 engine_llass_t llass = engine;
708 if (!llass || !command || !*command)
709 return gpg_error (GPG_ERR_INV_VALUE);
711 llass->user.data_cb = data_cb;
712 llass->user.data_cb_value = data_cb_value;
713 llass->user.inq_cb = inq_cb;
714 llass->user.inq_cb_value = inq_cb_value;
715 llass->user.status_cb = status_cb;
716 llass->user.status_cb_value = status_cb_value;
718 err = start (llass, command);
725 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
727 engine_llass_t llass = engine;
728 llass->io_cbs = *io_cbs;
733 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
735 engine_llass_t llass = engine;
737 TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
738 "event %p, type %d, type_data %p",
739 llass->io_cbs.event, type, type_data);
740 if (llass->io_cbs.event)
741 (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
745 struct engine_ops _gpgme_engine_ops_assuan =
747 /* Static functions. */
748 _gpgme_get_default_agent_socket,
751 llass_get_req_version,
754 /* Member functions. */
757 NULL, /* set_status_handler */
758 NULL, /* set_command_handler */
759 NULL, /* set_colon_line_handler */
761 NULL, /* set_protocol */
763 NULL, /* decrypt_verify */
767 NULL, /* encrypt_sign */
769 NULL, /* export_ext */
773 NULL, /* keylist_ext */
775 NULL, /* trustlist */
777 NULL, /* getauditlog */
778 llass_transact, /* opassuan_transact */
779 NULL, /* conf_load */
780 NULL, /* conf_save */