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 #include <sys/types.h>
53 #include "engine-backend.h"
58 int fd; /* FD we talk about. */
59 int server_fd;/* Server FD for this connection. */
60 int dir; /* Inbound/Outbound, maybe given implicit? */
61 void *data; /* Handler-specific data. */
62 void *tag; /* ID from the user for gpgme_remove_io_callback. */
65 /* Engine instance data. */
68 assuan_context_t assuan_ctx;
73 iocb_data_t status_cb;
75 struct gpgme_io_cbs io_cbs;
77 /* Hack for old opassuan.c interface, see there the result struct. */
78 gpg_error_t last_op_err;
80 /* User provided callbacks. */
82 gpgme_assuan_data_cb_t data_cb;
85 gpgme_assuan_inquire_cb_t inq_cb;
88 gpgme_assuan_status_cb_t status_cb;
89 void *status_cb_value;
94 int gpg_agent:1; /* Assume this is a gpg-agent connection. */
98 typedef struct engine_llass *engine_llass_t;
101 gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
103 engine_llass_t llass = engine;
104 return llass->last_op_err;
109 static void llass_io_event (void *engine,
110 gpgme_event_io_t type, void *type_data);
116 /* return the default home directory. */
118 llass_get_home_dir (void)
120 /* For this engine the home directory is not a filename but a string
121 used to convey options. The exclamation mark is a marker to show
122 that this is not a directory name. Options are strings delimited
123 by a space. The only option defined for now is GPG_AGENT to
124 enable GPG_AGENT specific commands to send to the server at
125 connection startup. */
130 llass_get_version (const char *file_name)
132 return strdup ("1.0");
137 llass_get_req_version (void)
144 close_notify_handler (int fd, void *opaque)
146 engine_llass_t llass = opaque;
149 if (llass->status_cb.fd == fd)
151 if (llass->status_cb.tag)
152 llass->io_cbs.remove (llass->status_cb.tag);
153 llass->status_cb.fd = -1;
154 llass->status_cb.tag = NULL;
161 llass_cancel (void *engine)
163 engine_llass_t llass = engine;
166 return gpg_error (GPG_ERR_INV_VALUE);
168 if (llass->status_cb.fd != -1)
169 _gpgme_io_close (llass->status_cb.fd);
171 if (llass->assuan_ctx)
173 assuan_release (llass->assuan_ctx);
174 llass->assuan_ctx = NULL;
182 llass_cancel_op (void *engine)
184 engine_llass_t llass = engine;
187 return gpg_error (GPG_ERR_INV_VALUE);
189 if (llass->status_cb.fd != -1)
190 _gpgme_io_close (llass->status_cb.fd);
197 llass_release (void *engine)
199 engine_llass_t llass = engine;
204 llass_cancel (engine);
210 /* Create a new instance. If HOME_DIR is NULL standard options for use
211 with gpg-agent are issued. */
213 llass_new (void **engine, const char *file_name, const char *home_dir)
215 gpgme_error_t err = 0;
216 engine_llass_t llass;
219 llass = calloc (1, sizeof *llass);
221 return gpg_error_from_syserror ();
223 llass->status_cb.fd = -1;
224 llass->status_cb.dir = 1;
225 llass->status_cb.tag = 0;
226 llass->status_cb.data = llass;
229 if (home_dir && *home_dir == '!')
232 /* Very simple parser only working for the one option we support. */
233 /* Note that wk promised to write a regression test if this
234 parser will be extended. */
235 if (!strncmp (home_dir, "GPG_AGENT", 9)
236 && (!home_dir[9] || home_dir[9] == ' '))
237 llass->opt.gpg_agent = 1;
240 err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
241 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
245 assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
247 err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
251 if (llass->opt.gpg_agent)
253 char *dft_display = NULL;
255 err = _gpgme_getenv ("DISPLAY", &dft_display);
260 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
262 err = gpg_error_from_syserror ();
268 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
276 if (llass->opt.gpg_agent && isatty (1))
279 char dft_ttyname[64];
280 char *dft_ttytype = NULL;
282 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
285 err = gpg_error_from_errno (rc);
290 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
292 err = gpg_error_from_syserror ();
295 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
301 err = _gpgme_getenv ("TERM", &dft_ttytype);
306 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
308 err = gpg_error_from_syserror ();
314 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
315 NULL, NULL, NULL, NULL);
324 #ifdef HAVE_W32_SYSTEM
325 /* Under Windows we need to use AllowSetForegroundWindow. Tell
326 llass to tell us when it needs it. */
327 if (!err && llass->opt.gpg_agent)
329 err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
330 NULL, NULL, NULL, NULL, NULL, NULL);
331 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
332 err = 0; /* This work only with recent gpg-agents. */
334 #endif /*HAVE_W32_SYSTEM*/
338 /* Close the server ends of the pipes (because of this, we must use
339 the stored server_fd_str in the function start). Our ends are
340 closed in llass_release(). */
343 llass_release (llass);
352 llass_set_locale (void *engine, int category, const char *value)
355 engine_llass_t llass = engine;
359 if (!llass->opt.gpg_agent)
362 /* FIXME: If value is NULL, we need to reset the option to default.
363 But we can't do this. So we error out here. gpg-agent needs
368 else if (category == LC_CTYPE)
371 if (!value && llass->lc_ctype_set)
372 return gpg_error (GPG_ERR_INV_VALUE);
374 llass->lc_ctype_set = 1;
378 else if (category == LC_MESSAGES)
380 catstr = "lc-messages";
381 if (!value && llass->lc_messages_set)
382 return gpg_error (GPG_ERR_INV_VALUE);
384 llass->lc_messages_set = 1;
386 #endif /* LC_MESSAGES */
388 return gpg_error (GPG_ERR_INV_VALUE);
390 /* FIXME: Reset value to default. */
394 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
395 err = gpg_error_from_errno (errno);
398 err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
399 NULL, NULL, NULL, NULL);
406 /* This is the inquiry callback. It handles stuff which ee need to
407 handle here and passes everything on to the user callback. */
409 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
413 if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
415 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
418 if (llass->user.inq_cb)
420 gpgme_data_t data = NULL;
422 err = llass->user.inq_cb (llass->user.inq_cb_value,
423 keyword, args, &data);
426 /* FIXME: Returning data is not yet implemented. However we
427 need to allow the caller to cleanup his data object.
428 Thus we run the callback in finish mode immediately. */
429 err = llass->user.inq_cb (llass->user.inq_cb_value,
441 llass_status_handler (void *opaque, int fd)
443 struct io_cb_data *data = (struct io_cb_data *) opaque;
444 engine_llass_t llass = (engine_llass_t) data->handler_value;
445 gpgme_error_t err = 0;
451 err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
454 /* Reading a full line may not be possible when
455 communicating over a socket in nonblocking mode. In this
456 case, we are done for now. */
457 if (gpg_err_code (err) == GPG_ERR_EAGAIN)
459 TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
460 "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
465 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
466 "fd 0x%x: error reading assuan line: %s",
467 fd, gpg_strerror (err));
469 else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
471 char *src = line + 2;
472 char *end = line + linelen;
478 if (*src == '%' && src + 2 < end)
480 /* Handle escaped characters. */
482 *dst++ = _gpgme_hextobyte (src);
492 if (linelen && llass->user.data_cb)
493 err = llass->user.data_cb (llass->user.data_cb_value,
496 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
497 "fd 0x%x: D inlinedata; status from cb: %s",
498 fd, (llass->user.data_cb ?
499 (err? gpg_strerror (err):"ok"):"no callback"));
501 else if (linelen >= 3
502 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
503 && (line[3] == '\0' || line[3] == ' '))
505 /* END received. Tell the data callback. */
506 if (llass->user.data_cb)
507 err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
509 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
510 "fd 0x%x: END line; status from cb: %s",
511 fd, (llass->user.data_cb ?
512 (err? gpg_strerror (err):"ok"):"no callback"));
514 else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
519 for (src=line+2; *src == ' '; src++)
522 args = strchr (src, ' ');
524 args = line + linelen; /* Let it point to an empty string. */
531 if (llass->user.status_cb)
532 err = llass->user.status_cb (llass->user.status_cb_value,
535 TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
536 "fd 0x%x: S line (%s) - status from cb: %s",
537 fd, line+2, (llass->user.status_cb ?
538 (err? gpg_strerror (err):"ok"):"no callback"));
540 else if (linelen >= 7
541 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
542 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
544 && (line[7] == '\0' || line[7] == ' '))
549 for (src=line+7; *src == ' '; src++)
552 args = strchr (src, ' ');
554 args = line + linelen; /* Let it point to an empty string. */
561 err = inquire_cb (llass, src, args);
564 /* Flush and send END. */
565 err = assuan_send_data (llass->assuan_ctx, NULL, 0);
567 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
569 /* Flush and send CANcel. */
570 err = assuan_send_data (llass->assuan_ctx, NULL, 1);
573 else if (linelen >= 3
574 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
575 && (line[3] == '\0' || line[3] == ' '))
580 err = gpg_error (GPG_ERR_GENERAL);
581 TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
582 "fd 0x%x: ERR line: %s",
583 fd, err ? gpg_strerror (err) : "ok");
585 /* Command execution errors are not fatal, as we use
586 a session based protocol. */
588 llass->last_op_err = err;
590 /* The caller will do the rest (namely, call cancel_op,
591 which closes status_fd). */
594 else if (linelen >= 2
595 && line[0] == 'O' && line[1] == 'K'
596 && (line[2] == '\0' || line[2] == ' '))
598 TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
599 "fd 0x%x: OK line", fd);
601 llass->last_op_err = 0;
603 _gpgme_io_close (llass->status_cb.fd);
608 /* Comment line or invalid line. */
612 while (!err && assuan_pending_line (llass->assuan_ctx));
619 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
623 TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
624 "fd %d, dir %d", iocbd->fd, iocbd->dir);
625 err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
626 iocbd->fd, iocbd->dir,
627 handler, iocbd->data, &iocbd->tag);
629 return TRACE_ERR (err);
631 /* FIXME Kludge around poll() problem. */
632 err = _gpgme_io_set_nonblocking (iocbd->fd);
633 return TRACE_ERR (err);
638 start (engine_llass_t llass, const char *command)
641 assuan_fd_t afdlist[5];
646 /* We need to know the fd used by assuan for reads. We do this by
647 using the assumption that the first returned fd from
648 assuan_get_active_fds() is always this one. */
649 nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
650 afdlist, DIM (afdlist));
652 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
654 for (i = 0; i < nfds; i++)
655 fdlist[i] = (int) afdlist[i];
657 /* We "duplicate" the file descriptor, so we can close it here (we
658 can't close fdlist[0], as that is closed by libassuan, and
659 closing it here might cause libassuan to close some unrelated FD
660 later). Alternatively, we could special case status_fd and
661 register/unregister it manually as needed, but this increases
662 code duplication and is more complicated as we can not use the
663 close notifications etc. A third alternative would be to let
664 Assuan know that we closed the FD, but that complicates the
667 llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
668 if (llass->status_cb.fd < 0)
669 return gpg_error_from_syserror ();
671 if (_gpgme_io_set_close_notify (llass->status_cb.fd,
672 close_notify_handler, llass))
674 _gpgme_io_close (llass->status_cb.fd);
675 llass->status_cb.fd = -1;
676 return gpg_error (GPG_ERR_GENERAL);
679 err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
681 err = assuan_write_line (llass->assuan_ctx, command);
683 /* FIXME: If *command == '#' no answer is expected. */
686 llass_io_event (llass, GPGME_EVENT_START, NULL);
694 llass_transact (void *engine,
696 gpgme_assuan_data_cb_t data_cb,
698 gpgme_assuan_inquire_cb_t inq_cb,
700 gpgme_assuan_status_cb_t status_cb,
701 void *status_cb_value)
703 engine_llass_t llass = engine;
706 if (!llass || !command || !*command)
707 return gpg_error (GPG_ERR_INV_VALUE);
709 llass->user.data_cb = data_cb;
710 llass->user.data_cb_value = data_cb_value;
711 llass->user.inq_cb = inq_cb;
712 llass->user.inq_cb_value = inq_cb_value;
713 llass->user.status_cb = status_cb;
714 llass->user.status_cb_value = status_cb_value;
716 err = start (llass, command);
723 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
725 engine_llass_t llass = engine;
726 llass->io_cbs = *io_cbs;
731 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
733 engine_llass_t llass = engine;
735 TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
736 "event %p, type %d, type_data %p",
737 llass->io_cbs.event, type, type_data);
738 if (llass->io_cbs.event)
739 (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
743 struct engine_ops _gpgme_engine_ops_assuan =
745 /* Static functions. */
746 _gpgme_get_default_agent_socket,
749 llass_get_req_version,
752 /* Member functions. */
755 NULL, /* set_status_handler */
756 NULL, /* set_command_handler */
757 NULL, /* set_colon_line_handler */
759 NULL, /* set_protocol */
761 NULL, /* decrypt_verify */
765 NULL, /* encrypt_sign */
767 NULL, /* export_ext */
771 NULL, /* keylist_ext */
773 NULL, /* trustlist */
775 NULL, /* getauditlog */
776 llass_transact, /* opassuan_transact */
777 NULL, /* conf_load */
778 NULL, /* conf_save */