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,
218 gpgme_error_t err = 0;
219 engine_llass_t llass;
222 (void)version; /* Not yet used. */
224 llass = calloc (1, sizeof *llass);
226 return gpg_error_from_syserror ();
228 llass->status_cb.fd = -1;
229 llass->status_cb.dir = 1;
230 llass->status_cb.tag = 0;
231 llass->status_cb.data = llass;
234 if (home_dir && *home_dir == '!')
237 /* Very simple parser only working for the one option we support. */
238 /* Note that wk promised to write a regression test if this
239 parser will be extended. */
240 if (!strncmp (home_dir, "GPG_AGENT", 9)
241 && (!home_dir[9] || home_dir[9] == ' '))
242 llass->opt.gpg_agent = 1;
245 err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
246 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
250 assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
252 err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
256 if (llass->opt.gpg_agent)
258 char *dft_display = NULL;
260 err = _gpgme_getenv ("DISPLAY", &dft_display);
265 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
267 err = gpg_error_from_syserror ();
273 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
281 if (llass->opt.gpg_agent && isatty (1))
284 char dft_ttyname[64];
285 char *dft_ttytype = NULL;
287 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
289 /* Even though isatty() returns 1, ttyname_r() may fail in many
290 ways, e.g., when /dev/pts is not accessible under chroot. */
293 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
295 err = gpg_error_from_syserror ();
298 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
304 err = _gpgme_getenv ("TERM", &dft_ttytype);
309 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
311 err = gpg_error_from_syserror ();
317 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
318 NULL, NULL, NULL, NULL);
327 #ifdef HAVE_W32_SYSTEM
328 /* Under Windows we need to use AllowSetForegroundWindow. Tell
329 llass to tell us when it needs it. */
330 if (!err && llass->opt.gpg_agent)
332 err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
333 NULL, NULL, NULL, NULL, NULL, NULL);
334 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
335 err = 0; /* This work only with recent gpg-agents. */
337 #endif /*HAVE_W32_SYSTEM*/
341 /* Close the server ends of the pipes (because of this, we must use
342 the stored server_fd_str in the function start). Our ends are
343 closed in llass_release(). */
346 llass_release (llass);
355 llass_set_locale (void *engine, int category, const char *value)
358 engine_llass_t llass = engine;
362 if (!llass->opt.gpg_agent)
365 /* FIXME: If value is NULL, we need to reset the option to default.
366 But we can't do this. So we error out here. gpg-agent needs
371 else if (category == LC_CTYPE)
374 if (!value && llass->lc_ctype_set)
375 return gpg_error (GPG_ERR_INV_VALUE);
377 llass->lc_ctype_set = 1;
381 else if (category == LC_MESSAGES)
383 catstr = "lc-messages";
384 if (!value && llass->lc_messages_set)
385 return gpg_error (GPG_ERR_INV_VALUE);
387 llass->lc_messages_set = 1;
389 #endif /* LC_MESSAGES */
391 return gpg_error (GPG_ERR_INV_VALUE);
393 /* FIXME: Reset value to default. */
397 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
398 err = gpg_error_from_syserror ();
401 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
402 NULL, NULL, NULL, NULL);
409 /* This is the inquiry callback. It handles stuff which ee need to
410 handle here and passes everything on to the user callback. */
412 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
416 if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
418 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
421 if (llass->user.inq_cb)
423 gpgme_data_t data = NULL;
425 err = llass->user.inq_cb (llass->user.inq_cb_value,
426 keyword, args, &data);
429 /* FIXME: Returning data is not yet implemented. However we
430 need to allow the caller to cleanup his data object.
431 Thus we run the callback in finish mode immediately. */
432 err = llass->user.inq_cb (llass->user.inq_cb_value,
444 llass_status_handler (void *opaque, int fd)
446 struct io_cb_data *data = (struct io_cb_data *) opaque;
447 engine_llass_t llass = (engine_llass_t) data->handler_value;
448 gpgme_error_t err = 0;
454 err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
457 /* Reading a full line may not be possible when
458 communicating over a socket in nonblocking mode. In this
459 case, we are done for now. */
460 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
462 TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
463 "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
468 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
469 "fd 0x%x: error reading assuan line: %s",
470 fd, gpg_strerror (err));
472 else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
474 char *src = line + 2;
475 char *end = line + linelen;
481 if (*src == '%' && src + 2 < end)
483 /* Handle escaped characters. */
485 *dst++ = _gpgme_hextobyte (src);
495 if (linelen && llass->user.data_cb)
496 err = llass->user.data_cb (llass->user.data_cb_value,
499 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
500 "fd 0x%x: D inlinedata; status from cb: %s",
501 fd, (llass->user.data_cb ?
502 (err? gpg_strerror (err):"ok"):"no callback"));
504 else if (linelen >= 3
505 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
506 && (line[3] == '\0' || line[3] == ' '))
508 /* END received. Tell the data callback. */
509 if (llass->user.data_cb)
510 err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
512 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
513 "fd 0x%x: END line; status from cb: %s",
514 fd, (llass->user.data_cb ?
515 (err? gpg_strerror (err):"ok"):"no callback"));
517 else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
522 for (src=line+2; *src == ' '; src++)
525 args = strchr (src, ' ');
527 args = line + linelen; /* Let it point to an empty string. */
534 if (llass->user.status_cb)
535 err = llass->user.status_cb (llass->user.status_cb_value,
538 TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
539 "fd 0x%x: S line (%s) - status from cb: %s",
540 fd, line+2, (llass->user.status_cb ?
541 (err? gpg_strerror (err):"ok"):"no callback"));
543 else if (linelen >= 7
544 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
545 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
547 && (line[7] == '\0' || line[7] == ' '))
552 for (src=line+7; *src == ' '; src++)
555 args = strchr (src, ' ');
557 args = line + linelen; /* Let it point to an empty string. */
564 err = inquire_cb (llass, src, args);
567 /* Flush and send END. */
568 err = assuan_send_data (llass->assuan_ctx, NULL, 0);
570 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
572 /* Flush and send CANcel. */
573 err = assuan_send_data (llass->assuan_ctx, NULL, 1);
576 else if (linelen >= 3
577 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
578 && (line[3] == '\0' || line[3] == ' '))
583 err = gpg_error (GPG_ERR_GENERAL);
584 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
585 "fd 0x%x: ERR line: %s",
586 fd, err ? gpg_strerror (err) : "ok");
588 /* Command execution errors are not fatal, as we use
589 a session based protocol. */
591 llass->last_op_err = err;
593 /* The caller will do the rest (namely, call cancel_op,
594 which closes status_fd). */
597 else if (linelen >= 2
598 && line[0] == 'O' && line[1] == 'K'
599 && (line[2] == '\0' || line[2] == ' '))
601 TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
602 "fd 0x%x: OK line", fd);
604 llass->last_op_err = 0;
606 _gpgme_io_close (llass->status_cb.fd);
611 /* Comment line or invalid line. */
615 while (!err && assuan_pending_line (llass->assuan_ctx));
622 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
626 TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
627 "fd %d, dir %d", iocbd->fd, iocbd->dir);
628 err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
629 iocbd->fd, iocbd->dir,
630 handler, iocbd->data, &iocbd->tag);
632 return TRACE_ERR (err);
634 /* FIXME Kludge around poll() problem. */
635 err = _gpgme_io_set_nonblocking (iocbd->fd);
636 return TRACE_ERR (err);
641 start (engine_llass_t llass, const char *command)
644 assuan_fd_t afdlist[5];
649 /* We need to know the fd used by assuan for reads. We do this by
650 using the assumption that the first returned fd from
651 assuan_get_active_fds() is always this one. */
652 nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
653 afdlist, DIM (afdlist));
655 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
657 for (i = 0; i < nfds; i++)
658 fdlist[i] = (int) afdlist[i];
660 /* We "duplicate" the file descriptor, so we can close it here (we
661 can't close fdlist[0], as that is closed by libassuan, and
662 closing it here might cause libassuan to close some unrelated FD
663 later). Alternatively, we could special case status_fd and
664 register/unregister it manually as needed, but this increases
665 code duplication and is more complicated as we can not use the
666 close notifications etc. A third alternative would be to let
667 Assuan know that we closed the FD, but that complicates the
670 llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
671 if (llass->status_cb.fd < 0)
672 return gpg_error_from_syserror ();
674 if (_gpgme_io_set_close_notify (llass->status_cb.fd,
675 close_notify_handler, llass))
677 _gpgme_io_close (llass->status_cb.fd);
678 llass->status_cb.fd = -1;
679 return gpg_error (GPG_ERR_GENERAL);
682 err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
684 err = assuan_write_line (llass->assuan_ctx, command);
686 /* FIXME: If *command == '#' no answer is expected. */
689 llass_io_event (llass, GPGME_EVENT_START, NULL);
697 llass_transact (void *engine,
699 gpgme_assuan_data_cb_t data_cb,
701 gpgme_assuan_inquire_cb_t inq_cb,
703 gpgme_assuan_status_cb_t status_cb,
704 void *status_cb_value)
706 engine_llass_t llass = engine;
709 if (!llass || !command || !*command)
710 return gpg_error (GPG_ERR_INV_VALUE);
712 llass->user.data_cb = data_cb;
713 llass->user.data_cb_value = data_cb_value;
714 llass->user.inq_cb = inq_cb;
715 llass->user.inq_cb_value = inq_cb_value;
716 llass->user.status_cb = status_cb;
717 llass->user.status_cb_value = status_cb_value;
719 err = start (llass, command);
726 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
728 engine_llass_t llass = engine;
729 llass->io_cbs = *io_cbs;
734 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
736 engine_llass_t llass = engine;
738 TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
739 "event %p, type %d, type_data %p",
740 llass->io_cbs.event, type, type_data);
741 if (llass->io_cbs.event)
742 (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
746 struct engine_ops _gpgme_engine_ops_assuan =
748 /* Static functions. */
749 _gpgme_get_default_agent_socket,
752 llass_get_req_version,
755 /* Member functions. */
758 NULL, /* set_status_cb */
759 NULL, /* set_status_handler */
760 NULL, /* set_command_handler */
761 NULL, /* set_colon_line_handler */
763 NULL, /* set_protocol */
765 NULL, /* decrypt_verify */
769 NULL, /* encrypt_sign */
771 NULL, /* export_ext */
775 NULL, /* keylist_ext */
777 NULL, /* trustlist */
779 NULL, /* getauditlog */
780 llass_transact, /* opassuan_transact */
781 NULL, /* conf_load */
782 NULL, /* conf_save */
788 NULL, /* set_pinentry_mode */