json: Implement op:encrypt
[gpgme.git] / src / engine-assuan.c
1 /* engine-assuan.c - Low-level Assuan protocol engine
2  * Copyright (C) 2009 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
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.
10  *
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.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19
20 /*
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.
24 */
25
26
27 #if HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <stdlib.h>
32 #include <string.h>
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36 #include <assert.h>
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43 #include <errno.h>
44
45 #include "gpgme.h"
46 #include "util.h"
47 #include "ops.h"
48 #include "wait.h"
49 #include "priv-io.h"
50 #include "sema.h"
51
52 #include "assuan.h"
53 #include "debug.h"
54
55 #include "engine-backend.h"
56
57 \f
58 typedef struct
59 {
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.  */
65 } iocb_data_t;
66
67 /* Engine instance data.  */
68 struct engine_llass
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   struct gpgme_io_cbs io_cbs;
78
79   /* Hack for old opassuan.c interface, see there the result struct.  */
80   gpg_error_t last_op_err;
81
82   /* User provided callbacks.  */
83   struct {
84     gpgme_assuan_data_cb_t data_cb;
85     void *data_cb_value;
86
87     gpgme_assuan_inquire_cb_t inq_cb;
88     void *inq_cb_value;
89
90     gpgme_assuan_status_cb_t status_cb;
91     void *status_cb_value;
92   } user;
93
94   /* Option flags.  */
95   struct {
96     int gpg_agent:1;  /* Assume this is a gpg-agent connection.  */
97   } opt;
98
99 };
100 typedef struct engine_llass *engine_llass_t;
101
102
103 gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
104 {
105   engine_llass_t llass = engine;
106   return llass->last_op_err;
107 }
108
109
110 /* Prototypes.  */
111 static void llass_io_event (void *engine,
112                             gpgme_event_io_t type, void *type_data);
113
114
115
116
117 \f
118 /* return the default home directory.  */
119 static const char *
120 llass_get_home_dir (void)
121 {
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.  */
128   return "!GPG_AGENT";
129 }
130
131 static char *
132 llass_get_version (const char *file_name)
133 {
134   (void)file_name;
135   return NULL;
136 }
137
138
139 static const char *
140 llass_get_req_version (void)
141 {
142   return NULL;
143 }
144
145 \f
146 static void
147 close_notify_handler (int fd, void *opaque)
148 {
149   engine_llass_t llass = opaque;
150
151   assert (fd != -1);
152   if (llass->status_cb.fd == fd)
153     {
154       if (llass->status_cb.tag)
155         llass->io_cbs.remove (llass->status_cb.tag);
156       llass->status_cb.fd = -1;
157       llass->status_cb.tag = NULL;
158     }
159 }
160
161
162
163 static gpgme_error_t
164 llass_cancel (void *engine)
165 {
166   engine_llass_t llass = engine;
167
168   if (!llass)
169     return gpg_error (GPG_ERR_INV_VALUE);
170
171   if (llass->status_cb.fd != -1)
172     _gpgme_io_close (llass->status_cb.fd);
173
174   if (llass->assuan_ctx)
175     {
176       assuan_release (llass->assuan_ctx);
177       llass->assuan_ctx = NULL;
178     }
179
180   return 0;
181 }
182
183
184 static gpgme_error_t
185 llass_cancel_op (void *engine)
186 {
187   engine_llass_t llass = engine;
188
189   if (!llass)
190     return gpg_error (GPG_ERR_INV_VALUE);
191
192   if (llass->status_cb.fd != -1)
193     _gpgme_io_close (llass->status_cb.fd);
194
195   return 0;
196 }
197
198
199 static void
200 llass_release (void *engine)
201 {
202   engine_llass_t llass = engine;
203
204   if (!llass)
205     return;
206
207   llass_cancel (engine);
208
209   free (llass);
210 }
211
212
213 /* Create a new instance. If HOME_DIR is NULL standard options for use
214    with gpg-agent are issued.  */
215 static gpgme_error_t
216 llass_new (void **engine, const char *file_name, const char *home_dir,
217            const char *version)
218 {
219   gpgme_error_t err = 0;
220   engine_llass_t llass;
221   char *optstr;
222   char *env_tty = NULL;
223
224   (void)version; /* Not yet used.  */
225
226   llass = calloc (1, sizeof *llass);
227   if (!llass)
228     return gpg_error_from_syserror ();
229
230   llass->status_cb.fd = -1;
231   llass->status_cb.dir = 1;
232   llass->status_cb.tag = 0;
233   llass->status_cb.data = llass;
234
235   /* Parse_options.  */
236   if (home_dir && *home_dir == '!')
237     {
238       home_dir++;
239       /* Very simple parser only working for the one option we support.  */
240       /* Note that wk promised to write a regression test if this
241          parser will be extended.  */
242       if (!strncmp (home_dir, "GPG_AGENT", 9)
243           && (!home_dir[9] || home_dir[9] == ' '))
244         llass->opt.gpg_agent = 1;
245     }
246
247   err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
248                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
249                         NULL);
250   if (err)
251     goto leave;
252   assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
253   assuan_set_flag (llass->assuan_ctx, ASSUAN_CONVEY_COMMENTS, 1);
254
255   err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
256   if (err)
257     goto leave;
258
259   if (llass->opt.gpg_agent)
260     {
261       char *dft_display = NULL;
262
263       err = _gpgme_getenv ("DISPLAY", &dft_display);
264       if (err)
265         goto leave;
266       if (dft_display)
267         {
268           if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
269             {
270               err = gpg_error_from_syserror ();
271               free (dft_display);
272               goto leave;
273             }
274           free (dft_display);
275
276           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
277                                  NULL, NULL, NULL);
278           gpgrt_free (optstr);
279           if (err)
280             goto leave;
281         }
282     }
283
284   if (llass->opt.gpg_agent)
285     err = _gpgme_getenv ("GPG_TTY", &env_tty);
286
287   if (llass->opt.gpg_agent && (isatty (1) || env_tty || err))
288     {
289       int rc = 0;
290       char dft_ttyname[64];
291       char *dft_ttytype = NULL;
292
293       if (err)
294         goto leave;
295       else if (env_tty)
296         {
297           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
298           free (env_tty);
299         }
300       else
301         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
302
303       /* Even though isatty() returns 1, ttyname_r() may fail in many
304          ways, e.g., when /dev/pts is not accessible under chroot.  */
305       if (!rc)
306         {
307           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
308             {
309               err = gpg_error_from_syserror ();
310               goto leave;
311             }
312           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
313                                  NULL, NULL, NULL);
314           gpgrt_free (optstr);
315           if (err)
316             goto leave;
317
318           err = _gpgme_getenv ("TERM", &dft_ttytype);
319           if (err)
320             goto leave;
321           if (dft_ttytype)
322             {
323               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
324                 {
325                   err = gpg_error_from_syserror ();
326                   free (dft_ttytype);
327                   goto leave;
328                 }
329               free (dft_ttytype);
330
331               err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
332                                      NULL, NULL, NULL, NULL);
333               gpgrt_free (optstr);
334               if (err)
335                 goto leave;
336             }
337         }
338     }
339
340
341 #ifdef HAVE_W32_SYSTEM
342   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
343      llass to tell us when it needs it.  */
344   if (!err && llass->opt.gpg_agent)
345     {
346       err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
347                              NULL, NULL, NULL, NULL, NULL, NULL);
348       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
349         err = 0; /* This work only with recent gpg-agents.  */
350     }
351 #endif /*HAVE_W32_SYSTEM*/
352
353
354  leave:
355   /* Close the server ends of the pipes (because of this, we must use
356      the stored server_fd_str in the function start).  Our ends are
357      closed in llass_release().  */
358
359   if (err)
360     llass_release (llass);
361   else
362     *engine = llass;
363
364   return err;
365 }
366
367
368 static gpgme_error_t
369 llass_set_locale (void *engine, int category, const char *value)
370 {
371   gpgme_error_t err;
372   engine_llass_t llass = engine;
373   char *optstr;
374   const char *catstr;
375
376   if (!llass->opt.gpg_agent)
377     return 0;
378
379   /* FIXME: If value is NULL, we need to reset the option to default.
380      But we can't do this.  So we error out here.  gpg-agent needs
381      support for this.  */
382   if (0)
383     ;
384 #ifdef LC_CTYPE
385   else if (category == LC_CTYPE)
386     {
387       catstr = "lc-ctype";
388       if (!value && llass->lc_ctype_set)
389         return gpg_error (GPG_ERR_INV_VALUE);
390       if (value)
391         llass->lc_ctype_set = 1;
392     }
393 #endif
394 #ifdef LC_MESSAGES
395   else if (category == LC_MESSAGES)
396     {
397       catstr = "lc-messages";
398       if (!value && llass->lc_messages_set)
399         return gpg_error (GPG_ERR_INV_VALUE);
400       if (value)
401         llass->lc_messages_set = 1;
402     }
403 #endif /* LC_MESSAGES */
404   else
405     return gpg_error (GPG_ERR_INV_VALUE);
406
407   /* FIXME: Reset value to default.  */
408   if (!value)
409     return 0;
410
411   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
412     err = gpg_error_from_syserror ();
413   else
414     {
415       err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
416                              NULL, NULL, NULL, NULL);
417       gpgrt_free (optstr);
418     }
419   return err;
420 }
421
422
423 /* This is the inquiry callback.  It handles stuff which ee need to
424    handle here and passes everything on to the user callback.  */
425 static gpgme_error_t
426 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
427 {
428   gpg_error_t err;
429
430   if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
431     {
432       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
433     }
434
435   if (llass->user.inq_cb)
436     {
437       gpgme_data_t data = NULL;
438
439       err = llass->user.inq_cb (llass->user.inq_cb_value,
440                                 keyword, args, &data);
441       if (!err && data)
442         {
443           /* FIXME: Returning data is not yet implemented.  However we
444              need to allow the caller to cleanup his data object.
445              Thus we run the callback in finish mode immediately.  */
446           err = llass->user.inq_cb (llass->user.inq_cb_value,
447                                     NULL, NULL, &data);
448         }
449     }
450   else
451     err = 0;
452
453   return err;
454 }
455
456
457 static gpgme_error_t
458 llass_status_handler (void *opaque, int fd)
459 {
460   struct io_cb_data *data = (struct io_cb_data *) opaque;
461   engine_llass_t llass = (engine_llass_t) data->handler_value;
462   gpgme_error_t err = 0;
463   char *line;
464   size_t linelen;
465
466   do
467     {
468       err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
469       if (err)
470         {
471           /* Reading a full line may not be possible when
472              communicating over a socket in nonblocking mode.  In this
473              case, we are done for now.  */
474           if (gpg_err_code (err) == GPG_ERR_EAGAIN)
475             {
476               TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
477                       "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
478               err = 0;
479               continue;
480             }
481
482           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
483                   "fd 0x%x: error reading assuan line: %s",
484                   fd, gpg_strerror (err));
485         }
486       else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
487         {
488           char *src = line + 2;
489           char *end = line + linelen;
490           char *dst = src;
491
492           linelen = 0;
493           while (src < end)
494             {
495               if (*src == '%' && src + 2 < end)
496                 {
497                   /* Handle escaped characters.  */
498                   ++src;
499                   *dst++ = _gpgme_hextobyte (src);
500                   src += 2;
501                 }
502               else
503                 *dst++ = *src++;
504
505               linelen++;
506             }
507
508           src = line + 2;
509           if (linelen && llass->user.data_cb)
510             err = llass->user.data_cb (llass->user.data_cb_value,
511                                        src, linelen);
512
513           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
514                   "fd 0x%x: D inlinedata; status from cb: %s",
515                   fd, (llass->user.data_cb ?
516                        (err? gpg_strerror (err):"ok"):"no callback"));
517         }
518       else if (linelen >= 3
519                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
520                && (line[3] == '\0' || line[3] == ' '))
521         {
522           /* END received.  Tell the data callback.  */
523           if (llass->user.data_cb)
524             err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
525
526           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
527                   "fd 0x%x: END line; status from cb: %s",
528                   fd, (llass->user.data_cb ?
529                        (err? gpg_strerror (err):"ok"):"no callback"));
530         }
531       else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
532         {
533           char *args;
534           char *src;
535
536           for (src=line+2; *src == ' '; src++)
537             ;
538
539           args = strchr (src, ' ');
540           if (!args)
541             args = line + linelen; /* Let it point to an empty string.  */
542           else
543             *(args++) = 0;
544
545           while (*args == ' ')
546             args++;
547
548           if (llass->user.status_cb)
549             err = llass->user.status_cb (llass->user.status_cb_value,
550                                          src, args);
551
552           TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
553                   "fd 0x%x: S line (%s) - status from cb: %s",
554                   fd, line+2, (llass->user.status_cb ?
555                                (err? gpg_strerror (err):"ok"):"no callback"));
556         }
557       else if (linelen >= 7
558                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
559                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
560                && line[6] == 'E'
561                && (line[7] == '\0' || line[7] == ' '))
562         {
563           char *src;
564           char *args;
565
566           for (src=line+7; *src == ' '; src++)
567             ;
568
569           args = strchr (src, ' ');
570           if (!args)
571             args = line + linelen; /* Let it point to an empty string.  */
572           else
573             *(args++) = 0;
574
575           while (*args == ' ')
576             args++;
577
578           err = inquire_cb (llass, src, args);
579           if (!err)
580             {
581               /* Flush and send END.  */
582               err = assuan_send_data (llass->assuan_ctx, NULL, 0);
583             }
584           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
585             {
586               /* Flush and send CANcel.  */
587               err = assuan_send_data (llass->assuan_ctx, NULL, 1);
588             }
589         }
590       else if (linelen >= 3
591                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
592                && (line[3] == '\0' || line[3] == ' '))
593         {
594           if (line[3] == ' ')
595             err = atoi (line+4);
596           else
597             err = gpg_error (GPG_ERR_GENERAL);
598           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
599                   "fd 0x%x: ERR line: %s",
600                   fd, err ? gpg_strerror (err) : "ok");
601
602           /* Command execution errors are not fatal, as we use
603              a session based protocol.  */
604           data->op_err = err;
605           llass->last_op_err = err;
606
607           /* The caller will do the rest (namely, call cancel_op,
608              which closes status_fd).  */
609           return 0;
610         }
611       else if (linelen >= 2
612                && line[0] == 'O' && line[1] == 'K'
613                && (line[2] == '\0' || line[2] == ' '))
614         {
615           TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
616                   "fd 0x%x: OK line", fd);
617
618           llass->last_op_err = 0;
619
620           _gpgme_io_close (llass->status_cb.fd);
621           return 0;
622         }
623       else
624         {
625           /* Comment line or invalid line.  */
626         }
627
628     }
629   while (!err && assuan_pending_line (llass->assuan_ctx));
630
631   return err;
632 }
633
634
635 static gpgme_error_t
636 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
637 {
638   gpgme_error_t err;
639
640   TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
641               "fd %d, dir %d", iocbd->fd, iocbd->dir);
642   err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
643                               iocbd->fd, iocbd->dir,
644                               handler, iocbd->data, &iocbd->tag);
645   if (err)
646     return TRACE_ERR (err);
647   if (!iocbd->dir)
648     /* FIXME Kludge around poll() problem.  */
649     err = _gpgme_io_set_nonblocking (iocbd->fd);
650   return TRACE_ERR (err);
651 }
652
653
654 static gpgme_error_t
655 start (engine_llass_t llass, const char *command)
656 {
657   gpgme_error_t err;
658   assuan_fd_t afdlist[5];
659   int fdlist[5];
660   int nfds;
661   int i;
662
663   /* We need to know the fd used by assuan for reads.  We do this by
664      using the assumption that the first returned fd from
665      assuan_get_active_fds() is always this one.  */
666   nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
667                                 afdlist, DIM (afdlist));
668   if (nfds < 1)
669     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
670   /* For now... */
671   for (i = 0; i < nfds; i++)
672     fdlist[i] = (int) afdlist[i];
673
674   /* We "duplicate" the file descriptor, so we can close it here (we
675      can't close fdlist[0], as that is closed by libassuan, and
676      closing it here might cause libassuan to close some unrelated FD
677      later).  Alternatively, we could special case status_fd and
678      register/unregister it manually as needed, but this increases
679      code duplication and is more complicated as we can not use the
680      close notifications etc.  A third alternative would be to let
681      Assuan know that we closed the FD, but that complicates the
682      Assuan interface.  */
683
684   llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
685   if (llass->status_cb.fd < 0)
686     return gpg_error_from_syserror ();
687
688   if (_gpgme_io_set_close_notify (llass->status_cb.fd,
689                                   close_notify_handler, llass))
690     {
691       _gpgme_io_close (llass->status_cb.fd);
692       llass->status_cb.fd = -1;
693       return gpg_error (GPG_ERR_GENERAL);
694     }
695
696   err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
697   if (!err)
698     err = assuan_write_line (llass->assuan_ctx, command);
699
700   /* FIXME: If *command == '#' no answer is expected.  */
701
702   if (!err)
703     llass_io_event (llass, GPGME_EVENT_START, NULL);
704
705   return err;
706 }
707
708
709
710 static gpgme_error_t
711 llass_transact (void *engine,
712                 const char *command,
713                 gpgme_assuan_data_cb_t data_cb,
714                 void *data_cb_value,
715                 gpgme_assuan_inquire_cb_t inq_cb,
716                 void *inq_cb_value,
717                 gpgme_assuan_status_cb_t status_cb,
718                 void *status_cb_value)
719 {
720   engine_llass_t llass = engine;
721   gpgme_error_t err;
722
723   if (!llass || !command || !*command)
724     return gpg_error (GPG_ERR_INV_VALUE);
725
726   llass->user.data_cb = data_cb;
727   llass->user.data_cb_value = data_cb_value;
728   llass->user.inq_cb = inq_cb;
729   llass->user.inq_cb_value = inq_cb_value;
730   llass->user.status_cb = status_cb;
731   llass->user.status_cb_value = status_cb_value;
732
733   err = start (llass, command);
734   return err;
735 }
736
737
738
739 static void
740 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
741 {
742   engine_llass_t llass = engine;
743   llass->io_cbs = *io_cbs;
744 }
745
746
747 static void
748 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
749 {
750   engine_llass_t llass = engine;
751
752   TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
753           "event %p, type %d, type_data %p",
754           llass->io_cbs.event, type, type_data);
755   if (llass->io_cbs.event)
756     (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
757 }
758
759
760 struct engine_ops _gpgme_engine_ops_assuan =
761   {
762     /* Static functions.  */
763     _gpgme_get_default_agent_socket,
764     llass_get_home_dir,
765     llass_get_version,
766     llass_get_req_version,
767     llass_new,
768
769     /* Member functions.  */
770     llass_release,
771     NULL,               /* reset */
772     NULL,               /* set_status_cb */
773     NULL,               /* set_status_handler */
774     NULL,               /* set_command_handler */
775     NULL,               /* set_colon_line_handler */
776     llass_set_locale,
777     NULL,               /* set_protocol */
778     NULL,               /* decrypt */
779     NULL,               /* delete */
780     NULL,               /* edit */
781     NULL,               /* encrypt */
782     NULL,               /* encrypt_sign */
783     NULL,               /* export */
784     NULL,               /* export_ext */
785     NULL,               /* genkey */
786     NULL,               /* import */
787     NULL,               /* keylist */
788     NULL,               /* keylist_ext */
789     NULL,               /* keylist_data */
790     NULL,               /* keysign */
791     NULL,               /* tofu_policy */
792     NULL,               /* sign */
793     NULL,               /* trustlist */
794     NULL,               /* verify */
795     NULL,               /* getauditlog */
796     llass_transact,     /* opassuan_transact */
797     NULL,               /* conf_load */
798     NULL,               /* conf_save */
799     NULL,               /* conf_dir */
800     NULL,               /* query_swdb */
801     llass_set_io_cbs,
802     llass_io_event,
803     llass_cancel,
804     llass_cancel_op,
805     NULL,               /* passwd */
806     NULL,               /* set_pinentry_mode */
807     NULL                /* opspawn */
808   };