qt: Handle diagnostic audit log for CMS
[gpgme.git] / src / engine-g13.c
1 /* engine-g13.c - G13 engine.
2  * Copyright (C) 2000 Werner Koch (dd9jn)
3  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <https://gnu.org/licenses/>.
19  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_SYS_TYPES_H
29 # include <sys/types.h>
30 #endif
31 #include <assert.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #ifdef HAVE_LOCALE_H
36 #include <locale.h>
37 #endif
38 #include <fcntl.h> /* FIXME */
39 #include <errno.h>
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
48 #include "assuan.h"
49 #include "debug.h"
50
51 #include "engine-backend.h"
52
53 \f
54 typedef struct
55 {
56   int fd;       /* FD we talk about.  */
57   int server_fd;/* Server FD for this connection.  */
58   int dir;      /* Inbound/Outbound, maybe given implicit?  */
59   void *data;   /* Handler-specific data.  */
60   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
61   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
62                              need this because _gpgme_io_fd2str can't
63                              be used on a closed descriptor.  */
64 } iocb_data_t;
65
66
67 struct engine_g13
68 {
69   assuan_context_t assuan_ctx;
70
71   int lc_ctype_set;
72   int lc_messages_set;
73
74   iocb_data_t status_cb;
75
76   struct gpgme_io_cbs io_cbs;
77
78   /* User provided callbacks.  */
79   struct {
80     gpgme_assuan_data_cb_t data_cb;
81     void *data_cb_value;
82
83     gpgme_assuan_inquire_cb_t inq_cb;
84     void *inq_cb_value;
85
86     gpgme_assuan_status_cb_t status_cb;
87     void *status_cb_value;
88   } user;
89 };
90
91 typedef struct engine_g13 *engine_g13_t;
92
93
94 static void g13_io_event (void *engine,
95                             gpgme_event_io_t type, void *type_data);
96
97
98 \f
99 static char *
100 g13_get_version (const char *file_name)
101 {
102   return _gpgme_get_program_version (file_name ? file_name
103                                      : _gpgme_get_default_g13_name ());
104 }
105
106
107 static const char *
108 g13_get_req_version (void)
109 {
110   return "2.1.0";
111 }
112
113 \f
114 static void
115 close_notify_handler (int fd, void *opaque)
116 {
117   engine_g13_t g13 = opaque;
118
119   assert (fd != -1);
120   if (g13->status_cb.fd == fd)
121     {
122       if (g13->status_cb.tag)
123         (*g13->io_cbs.remove) (g13->status_cb.tag);
124       g13->status_cb.fd = -1;
125       g13->status_cb.tag = NULL;
126     }
127 }
128
129
130 /* This is the default inquiry callback.  We use it to handle the
131    Pinentry notifications.  */
132 static gpgme_error_t
133 default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
134 {
135   gpg_error_t err;
136
137   if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
138     {
139       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
140     }
141
142   if (g13->user.inq_cb)
143     {
144       gpgme_data_t data = NULL;
145
146       err = g13->user.inq_cb (g13->user.inq_cb_value,
147                                 keyword, args, &data);
148       if (!err && data)
149         {
150           /* FIXME: Returning data is not yet implemented.  However we
151              need to allow the caller to cleanup his data object.
152              Thus we run the callback in finish mode immediately.  */
153           err = g13->user.inq_cb (g13->user.inq_cb_value,
154                                   NULL, NULL, &data);
155         }
156     }
157   else
158     err = 0;
159
160   return err;
161 }
162
163
164 static gpgme_error_t
165 g13_cancel (void *engine)
166 {
167   engine_g13_t g13 = engine;
168
169   if (!g13)
170     return gpg_error (GPG_ERR_INV_VALUE);
171
172   if (g13->status_cb.fd != -1)
173     _gpgme_io_close (g13->status_cb.fd);
174
175   if (g13->assuan_ctx)
176     {
177       assuan_release (g13->assuan_ctx);
178       g13->assuan_ctx = NULL;
179     }
180
181   return 0;
182 }
183
184
185 static gpgme_error_t
186 g13_cancel_op (void *engine)
187 {
188   engine_g13_t g13 = engine;
189
190   if (!g13)
191     return gpg_error (GPG_ERR_INV_VALUE);
192
193   if (g13->status_cb.fd != -1)
194     _gpgme_io_close (g13->status_cb.fd);
195
196   return 0;
197 }
198
199
200 static void
201 g13_release (void *engine)
202 {
203   engine_g13_t g13 = engine;
204
205   if (!g13)
206     return;
207
208   g13_cancel (engine);
209
210   free (g13);
211 }
212
213
214 static gpgme_error_t
215 g13_new (void **engine, const char *file_name, const char *home_dir,
216          const char *version)
217 {
218   gpgme_error_t err = 0;
219   engine_g13_t g13;
220   const char *pgmname;
221   int argc;
222   const char *argv[5];
223   char *dft_display = NULL;
224   char dft_ttyname[64];
225   char *env_tty = NULL;
226   char *dft_ttytype = NULL;
227   char *optstr;
228
229   (void)version; /* Not yet used.  */
230
231   g13 = calloc (1, sizeof *g13);
232   if (!g13)
233     return gpg_error_from_syserror ();
234
235   g13->status_cb.fd = -1;
236   g13->status_cb.dir = 1;
237   g13->status_cb.tag = 0;
238   g13->status_cb.data = g13;
239
240   pgmname = file_name ? file_name : _gpgme_get_default_g13_name ();
241   argc = 0;
242   argv[argc++] = _gpgme_get_basename (pgmname);
243   if (home_dir)
244     {
245       argv[argc++] = "--homedir";
246       argv[argc++] = home_dir;
247     }
248   argv[argc++] = "--server";
249   argv[argc++] = NULL;
250
251   err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
252                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
253                         NULL);
254   if (err)
255     goto leave;
256   assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
257
258 #if USE_DESCRIPTOR_PASSING
259   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
260                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
261 #else
262   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
263                              NULL, NULL, NULL, 0);
264 #endif
265   if (err)
266     goto leave;
267
268   err = _gpgme_getenv ("DISPLAY", &dft_display);
269   if (err)
270     goto leave;
271   if (dft_display)
272     {
273       if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
274         {
275           free (dft_display);
276           err = gpg_error_from_syserror ();
277           goto leave;
278         }
279       free (dft_display);
280
281       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
282                              NULL, NULL, NULL);
283       gpgrt_free (optstr);
284       if (err)
285         goto leave;
286     }
287
288   err = _gpgme_getenv ("GPG_TTY", &env_tty);
289   if (isatty (1) || env_tty || err)
290     {
291       int rc = 0;
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 (g13->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                   free (dft_ttytype);
326                   err = gpg_error_from_syserror ();
327                   goto leave;
328                 }
329               free (dft_ttytype);
330
331               err = assuan_transact (g13->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 #ifdef HAVE_W32_SYSTEM
341   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
342      g13 to tell us when it needs it.  */
343   if (!err)
344     {
345       err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
346                              NULL, NULL, NULL, NULL, NULL, NULL);
347       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
348         err = 0; /* This is a new feature of g13.  */
349     }
350 #endif /*HAVE_W32_SYSTEM*/
351
352  leave:
353
354   if (err)
355     g13_release (g13);
356   else
357     *engine = g13;
358
359   return err;
360 }
361
362
363 static gpgme_error_t
364 g13_set_locale (void *engine, int category, const char *value)
365 {
366   engine_g13_t g13 = engine;
367   gpgme_error_t err;
368   char *optstr;
369   const char *catstr;
370
371   /* FIXME: If value is NULL, we need to reset the option to default.
372      But we can't do this.  So we error out here.  G13 needs support
373      for this.  */
374   if (0)
375     ;
376 #ifdef LC_CTYPE
377   else if (category == LC_CTYPE)
378     {
379       catstr = "lc-ctype";
380       if (!value && g13->lc_ctype_set)
381         return gpg_error (GPG_ERR_INV_VALUE);
382       if (value)
383         g13->lc_ctype_set = 1;
384     }
385 #endif
386 #ifdef LC_MESSAGES
387   else if (category == LC_MESSAGES)
388     {
389       catstr = "lc-messages";
390       if (!value && g13->lc_messages_set)
391         return gpg_error (GPG_ERR_INV_VALUE);
392       if (value)
393         g13->lc_messages_set = 1;
394     }
395 #endif /* LC_MESSAGES */
396   else
397     return gpg_error (GPG_ERR_INV_VALUE);
398
399   /* FIXME: Reset value to default.  */
400   if (!value)
401     return 0;
402
403   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
404     err = gpg_error_from_syserror ();
405   else
406     {
407       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
408                              NULL, NULL, NULL, NULL);
409       gpgrt_free (optstr);
410     }
411
412   return err;
413 }
414
415
416 #if USE_DESCRIPTOR_PASSING
417 static gpgme_error_t
418 g13_assuan_simple_command (assuan_context_t ctx, const char *cmd,
419                            engine_status_handler_t status_fnc,
420                            void *status_fnc_value)
421 {
422   gpg_error_t err;
423   char *line;
424   size_t linelen;
425
426   (void)status_fnc;
427   (void)status_fnc_value;
428
429   err = assuan_write_line (ctx, cmd);
430   if (err)
431     return err;
432
433   do
434     {
435       err = assuan_read_line (ctx, &line, &linelen);
436       if (err)
437         return err;
438
439       if (*line == '#' || !linelen)
440         continue;
441
442       if (linelen >= 2
443           && line[0] == 'O' && line[1] == 'K'
444           && (line[2] == '\0' || line[2] == ' '))
445         return 0;
446       else if (linelen >= 4
447           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
448           && line[3] == ' ')
449         err = atoi (&line[4]);
450       else if (linelen >= 2
451                && line[0] == 'S' && line[1] == ' ')
452         {
453           char *rest;
454
455           rest = strchr (line + 2, ' ');
456           if (!rest)
457             rest = line + linelen; /* set to an empty string */
458           else
459             *(rest++) = 0;
460
461           /* Nothing to do with status lines.  */
462         }
463       else
464         err = gpg_error (GPG_ERR_GENERAL);
465     }
466   while (!err);
467
468   return err;
469 }
470 #endif
471
472
473 static gpgme_error_t
474 status_handler (void *opaque, int fd)
475 {
476   struct io_cb_data *data = (struct io_cb_data *) opaque;
477   engine_g13_t g13 = (engine_g13_t) data->handler_value;
478   gpgme_error_t err = 0;
479   char *line;
480   size_t linelen;
481
482   do
483     {
484       err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
485       if (err)
486         {
487           /* Try our best to terminate the connection friendly.  */
488           /*      assuan_write_line (g13->assuan_ctx, "BYE"); */
489           TRACE (DEBUG_CTX, "gpgme:status_handler", g13,
490                   "fd 0x%x: error reading assuan line: %s",
491                   fd, gpg_strerror (err));
492         }
493       else if (linelen >= 3
494                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
495                && (line[3] == '\0' || line[3] == ' '))
496         {
497           if (line[3] == ' ')
498             err = atoi (&line[4]);
499           if (! err)
500             err = gpg_error (GPG_ERR_GENERAL);
501           TRACE (DEBUG_CTX, "gpgme:status_handler", g13,
502                   "fd 0x%x: ERR line: %s",
503                   fd, err ? gpg_strerror (err) : "ok");
504
505           /* Command execution errors are not fatal, as we use
506              a session based protocol.  */
507           data->op_err = err;
508
509           /* The caller will do the rest (namely, call cancel_op,
510              which closes status_fd).  */
511           return 0;
512         }
513       else if (linelen >= 2
514                && line[0] == 'O' && line[1] == 'K'
515                && (line[2] == '\0' || line[2] == ' '))
516         {
517           TRACE (DEBUG_CTX, "gpgme:status_handler", g13,
518                   "fd 0x%x: OK line", fd);
519
520           _gpgme_io_close (g13->status_cb.fd);
521           return 0;
522         }
523       else if (linelen > 2
524                && line[0] == 'D' && line[1] == ' ')
525         {
526           /* We are using the colon handler even for plain inline data
527              - strange name for that function but for historic reasons
528              we keep it.  */
529           /* FIXME We can't use this for binary data because we
530              assume this is a string.  For the current usage of colon
531              output it is correct.  */
532           char *src = line + 2;
533           char *end = line + linelen;
534           char *dst = src;
535
536           linelen = 0;
537           while (src < end)
538             {
539               if (*src == '%' && src + 2 < end)
540                 {
541                   /* Handle escaped characters.  */
542                   ++src;
543                   *dst++ = _gpgme_hextobyte (src);
544                   src += 2;
545                 }
546               else
547                 *dst++ = *src++;
548
549               linelen++;
550             }
551
552           src = line + 2;
553           if (linelen && g13->user.data_cb)
554             err = g13->user.data_cb (g13->user.data_cb_value,
555                                        src, linelen);
556           else
557             err = 0;
558
559           TRACE (DEBUG_CTX, "gpgme:g13_status_handler", g13,
560                   "fd 0x%x: D inlinedata; status from cb: %s",
561                   fd, (g13->user.data_cb ?
562                        (err? gpg_strerror (err):"ok"):"no callback"));
563
564         }
565       else if (linelen > 2
566                && line[0] == 'S' && line[1] == ' ')
567         {
568           char *src;
569           char *args;
570
571           src = line + 2;
572           while (*src == ' ')
573             src++;
574
575           args = strchr (line + 2, ' ');
576           if (!args)
577             args = line + linelen; /* set to an empty string */
578           else
579             *(args++) = 0;
580
581           while (*args == ' ')
582             args++;
583
584           if (g13->user.status_cb)
585             err = g13->user.status_cb (g13->user.status_cb_value,
586                                        src, args);
587           else
588             err = 0;
589
590           TRACE (DEBUG_CTX, "gpgme:g13_status_handler", g13,
591                   "fd 0x%x: S line (%s) - status from cb: %s",
592                   fd, line+2, (g13->user.status_cb ?
593                                (err? gpg_strerror (err):"ok"):"no callback"));
594         }
595       else if (linelen >= 7
596                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
597                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
598                && line[6] == 'E'
599                && (line[7] == '\0' || line[7] == ' '))
600         {
601           char *src;
602           char *args;
603
604           for (src=line+7; *src == ' '; src++)
605             ;
606
607           args = strchr (src, ' ');
608           if (!args)
609             args = line + linelen; /* Let it point to an empty string.  */
610           else
611             *(args++) = 0;
612
613           while (*args == ' ')
614             args++;
615
616           err = default_inq_cb (g13, src, args);
617           if (!err)
618             {
619               /* Flush and send END.  */
620               err = assuan_send_data (g13->assuan_ctx, NULL, 0);
621             }
622           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
623             {
624               /* Flush and send CANcel.  */
625               err = assuan_send_data (g13->assuan_ctx, NULL, 1);
626             }
627           assuan_write_line (g13->assuan_ctx, "END");
628         }
629     }
630   while (!err && assuan_pending_line (g13->assuan_ctx));
631
632   return err;
633 }
634
635
636 static gpgme_error_t
637 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
638 {
639   gpgme_error_t err;
640
641   TRACE_BEG  (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
642               "fd %d, dir %d", iocbd->fd, iocbd->dir);
643   err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
644                               iocbd->fd, iocbd->dir,
645                               handler, iocbd->data, &iocbd->tag);
646   if (err)
647     return TRACE_ERR (err);
648   if (!iocbd->dir)
649     /* FIXME Kludge around poll() problem.  */
650     err = _gpgme_io_set_nonblocking (iocbd->fd);
651   return TRACE_ERR (err);
652 }
653
654
655 static gpgme_error_t
656 start (engine_g13_t g13, const char *command)
657 {
658   gpgme_error_t err;
659   assuan_fd_t afdlist[5];
660   int fdlist[5];
661   int nfds;
662   int i;
663
664   /* We need to know the fd used by assuan for reads.  We do this by
665      using the assumption that the first returned fd from
666      assuan_get_active_fds() is always this one.  */
667   nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
668                                 afdlist, DIM (afdlist));
669   if (nfds < 1)
670     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
671   /* For now... */
672   for (i = 0; i < nfds; i++)
673     fdlist[i] = (int) afdlist[i];
674
675   /* We "duplicate" the file descriptor, so we can close it here (we
676      can't close fdlist[0], as that is closed by libassuan, and
677      closing it here might cause libassuan to close some unrelated FD
678      later).  Alternatively, we could special case status_fd and
679      register/unregister it manually as needed, but this increases
680      code duplication and is more complicated as we can not use the
681      close notifications etc.  A third alternative would be to let
682      Assuan know that we closed the FD, but that complicates the
683      Assuan interface.  */
684
685   g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
686   if (g13->status_cb.fd < 0)
687     return gpg_error_from_syserror ();
688
689   if (_gpgme_io_set_close_notify (g13->status_cb.fd,
690                                   close_notify_handler, g13))
691     {
692       _gpgme_io_close (g13->status_cb.fd);
693       g13->status_cb.fd = -1;
694       return gpg_error (GPG_ERR_GENERAL);
695     }
696
697   err = add_io_cb (g13, &g13->status_cb, status_handler);
698   if (!err)
699     err = assuan_write_line (g13->assuan_ctx, command);
700
701   if (!err)
702     g13_io_event (g13, GPGME_EVENT_START, NULL);
703
704   return err;
705 }
706
707
708 #if USE_DESCRIPTOR_PASSING
709 static gpgme_error_t
710 g13_reset (void *engine)
711 {
712   engine_g13_t g13 = engine;
713
714   /* We must send a reset because we need to reset the list of
715      signers.  Note that RESET does not reset OPTION commands. */
716   return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
717 }
718 #endif
719
720
721 static gpgme_error_t
722 g13_transact (void *engine,
723                 const char *command,
724                 gpgme_assuan_data_cb_t data_cb,
725                 void *data_cb_value,
726                 gpgme_assuan_inquire_cb_t inq_cb,
727                 void *inq_cb_value,
728                 gpgme_assuan_status_cb_t status_cb,
729                 void *status_cb_value)
730 {
731   engine_g13_t g13 = engine;
732   gpgme_error_t err;
733
734   if (!g13 || !command || !*command)
735     return gpg_error (GPG_ERR_INV_VALUE);
736
737   g13->user.data_cb = data_cb;
738   g13->user.data_cb_value = data_cb_value;
739   g13->user.inq_cb = inq_cb;
740   g13->user.inq_cb_value = inq_cb_value;
741   g13->user.status_cb = status_cb;
742   g13->user.status_cb_value = status_cb_value;
743
744   err = start (g13, command);
745   return err;
746 }
747
748
749
750 static void
751 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
752 {
753   engine_g13_t g13 = engine;
754   g13->io_cbs = *io_cbs;
755 }
756
757
758 static void
759 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
760 {
761   engine_g13_t g13 = engine;
762
763   TRACE (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
764           "event %p, type %d, type_data %p",
765           g13->io_cbs.event, type, type_data);
766   if (g13->io_cbs.event)
767     (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
768 }
769
770
771 struct engine_ops _gpgme_engine_ops_g13 =
772   {
773     /* Static functions.  */
774     _gpgme_get_default_g13_name,
775     NULL,
776     g13_get_version,
777     g13_get_req_version,
778     g13_new,
779
780     /* Member functions.  */
781     g13_release,
782 #if USE_DESCRIPTOR_PASSING
783     g13_reset,
784 #else
785     NULL,                       /* reset */
786 #endif
787     NULL,               /* set_status_cb */
788     NULL,               /* set_status_handler */
789     NULL,               /* set_command_handler */
790     NULL,               /* set_colon_line_handler */
791     g13_set_locale,
792     NULL,               /* set_protocol */
793     NULL,               /* set_engine_flags */
794     NULL,               /* decrypt */
795     NULL,               /* delete */
796     NULL,               /* edit */
797     NULL,               /* encrypt */
798     NULL,               /* encrypt_sign */
799     NULL,               /* export */
800     NULL,               /* export_ext */
801     NULL,               /* genkey */
802     NULL,               /* import */
803     NULL,               /* keylist */
804     NULL,               /* keylist_ext */
805     NULL,               /* keylist_data */
806     NULL,               /* keysign */
807     NULL,               /* tofu_policy */
808     NULL,               /* sign */
809     NULL,               /* trustlist */
810     NULL,               /* verify */
811     NULL,               /* getauditlog */
812     g13_transact,
813     NULL,               /* conf_load */
814     NULL,               /* conf_save */
815     NULL,               /* conf_dir */
816     NULL,               /* query_swdb */
817     g13_set_io_cbs,
818     g13_io_event,
819     g13_cancel,
820     g13_cancel_op,
821     NULL,               /* passwd */
822     NULL,               /* set_pinentry_mode */
823     NULL                /* opspawn */
824   };