core: New function gpgme_op_tofu_policy
[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, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
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 *dft_ttytype = NULL;
226   char *optstr;
227
228   (void)version; /* Not yet used.  */
229
230   g13 = calloc (1, sizeof *g13);
231   if (!g13)
232     return gpg_error_from_syserror ();
233
234   g13->status_cb.fd = -1;
235   g13->status_cb.dir = 1;
236   g13->status_cb.tag = 0;
237   g13->status_cb.data = g13;
238
239   pgmname = file_name ? file_name : _gpgme_get_default_g13_name ();
240   argc = 0;
241   argv[argc++] = _gpgme_get_basename (pgmname);
242   if (home_dir)
243     {
244       argv[argc++] = "--homedir";
245       argv[argc++] = home_dir;
246     }
247   argv[argc++] = "--server";
248   argv[argc++] = NULL;
249
250   err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
251                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
252                         NULL);
253   if (err)
254     goto leave;
255   assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
256
257 #if USE_DESCRIPTOR_PASSING
258   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
259                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
260 #else
261   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
262                              NULL, NULL, NULL, 0);
263 #endif
264   if (err)
265     goto leave;
266
267   err = _gpgme_getenv ("DISPLAY", &dft_display);
268   if (err)
269     goto leave;
270   if (dft_display)
271     {
272       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
273         {
274           free (dft_display);
275           err = gpg_error_from_syserror ();
276           goto leave;
277         }
278       free (dft_display);
279
280       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
281                              NULL, NULL, NULL);
282       free (optstr);
283       if (err)
284         goto leave;
285     }
286
287   if (isatty (1))
288     {
289       int rc;
290
291       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
292
293       /* Even though isatty() returns 1, ttyname_r() may fail in many
294          ways, e.g., when /dev/pts is not accessible under chroot.  */
295       if (!rc)
296         {
297           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
298             {
299               err = gpg_error_from_syserror ();
300               goto leave;
301             }
302           err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
303                                  NULL, NULL, NULL);
304           free (optstr);
305           if (err)
306             goto leave;
307
308           err = _gpgme_getenv ("TERM", &dft_ttytype);
309           if (err)
310             goto leave;
311           if (dft_ttytype)
312             {
313               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
314                 {
315                   free (dft_ttytype);
316                   err = gpg_error_from_syserror ();
317                   goto leave;
318                 }
319               free (dft_ttytype);
320
321               err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
322                                      NULL, NULL, NULL, NULL);
323               free (optstr);
324               if (err)
325                 goto leave;
326             }
327         }
328     }
329
330 #ifdef HAVE_W32_SYSTEM
331   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
332      g13 to tell us when it needs it.  */
333   if (!err)
334     {
335       err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
336                              NULL, NULL, NULL, NULL, NULL, NULL);
337       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
338         err = 0; /* This is a new feature of g13.  */
339     }
340 #endif /*HAVE_W32_SYSTEM*/
341
342  leave:
343
344   if (err)
345     g13_release (g13);
346   else
347     *engine = g13;
348
349   return err;
350 }
351
352
353 static gpgme_error_t
354 g13_set_locale (void *engine, int category, const char *value)
355 {
356   engine_g13_t g13 = engine;
357   gpgme_error_t err;
358   char *optstr;
359   const char *catstr;
360
361   /* FIXME: If value is NULL, we need to reset the option to default.
362      But we can't do this.  So we error out here.  G13 needs support
363      for this.  */
364   if (0)
365     ;
366 #ifdef LC_CTYPE
367   else if (category == LC_CTYPE)
368     {
369       catstr = "lc-ctype";
370       if (!value && g13->lc_ctype_set)
371         return gpg_error (GPG_ERR_INV_VALUE);
372       if (value)
373         g13->lc_ctype_set = 1;
374     }
375 #endif
376 #ifdef LC_MESSAGES
377   else if (category == LC_MESSAGES)
378     {
379       catstr = "lc-messages";
380       if (!value && g13->lc_messages_set)
381         return gpg_error (GPG_ERR_INV_VALUE);
382       if (value)
383         g13->lc_messages_set = 1;
384     }
385 #endif /* LC_MESSAGES */
386   else
387     return gpg_error (GPG_ERR_INV_VALUE);
388
389   /* FIXME: Reset value to default.  */
390   if (!value)
391     return 0;
392
393   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
394     err = gpg_error_from_syserror ();
395   else
396     {
397       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
398                              NULL, NULL, NULL, NULL);
399       free (optstr);
400     }
401
402   return err;
403 }
404
405
406 #if USE_DESCRIPTOR_PASSING
407 static gpgme_error_t
408 g13_assuan_simple_command (assuan_context_t ctx, const char *cmd,
409                            engine_status_handler_t status_fnc,
410                            void *status_fnc_value)
411 {
412   gpg_error_t err;
413   char *line;
414   size_t linelen;
415
416   (void)status_fnc;
417   (void)status_fnc_value;
418
419   err = assuan_write_line (ctx, cmd);
420   if (err)
421     return err;
422
423   do
424     {
425       err = assuan_read_line (ctx, &line, &linelen);
426       if (err)
427         return err;
428
429       if (*line == '#' || !linelen)
430         continue;
431
432       if (linelen >= 2
433           && line[0] == 'O' && line[1] == 'K'
434           && (line[2] == '\0' || line[2] == ' '))
435         return 0;
436       else if (linelen >= 4
437           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
438           && line[3] == ' ')
439         err = atoi (&line[4]);
440       else if (linelen >= 2
441                && line[0] == 'S' && line[1] == ' ')
442         {
443           char *rest;
444
445           rest = strchr (line + 2, ' ');
446           if (!rest)
447             rest = line + linelen; /* set to an empty string */
448           else
449             *(rest++) = 0;
450
451           /* Nothing to do with status lines.  */
452         }
453       else
454         err = gpg_error (GPG_ERR_GENERAL);
455     }
456   while (!err);
457
458   return err;
459 }
460 #endif
461
462
463 static gpgme_error_t
464 status_handler (void *opaque, int fd)
465 {
466   struct io_cb_data *data = (struct io_cb_data *) opaque;
467   engine_g13_t g13 = (engine_g13_t) data->handler_value;
468   gpgme_error_t err = 0;
469   char *line;
470   size_t linelen;
471
472   do
473     {
474       err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
475       if (err)
476         {
477           /* Try our best to terminate the connection friendly.  */
478           /*      assuan_write_line (g13->assuan_ctx, "BYE"); */
479           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
480                   "fd 0x%x: error reading assuan line: %s",
481                   fd, gpg_strerror (err));
482         }
483       else if (linelen >= 3
484                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
485                && (line[3] == '\0' || line[3] == ' '))
486         {
487           if (line[3] == ' ')
488             err = atoi (&line[4]);
489           if (! err)
490             err = gpg_error (GPG_ERR_GENERAL);
491           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
492                   "fd 0x%x: ERR line: %s",
493                   fd, err ? gpg_strerror (err) : "ok");
494
495           /* Command execution errors are not fatal, as we use
496              a session based protocol.  */
497           data->op_err = err;
498
499           /* The caller will do the rest (namely, call cancel_op,
500              which closes status_fd).  */
501           return 0;
502         }
503       else if (linelen >= 2
504                && line[0] == 'O' && line[1] == 'K'
505                && (line[2] == '\0' || line[2] == ' '))
506         {
507           TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
508                   "fd 0x%x: OK line", fd);
509
510           _gpgme_io_close (g13->status_cb.fd);
511           return 0;
512         }
513       else if (linelen > 2
514                && line[0] == 'D' && line[1] == ' ')
515         {
516           /* We are using the colon handler even for plain inline data
517              - strange name for that function but for historic reasons
518              we keep it.  */
519           /* FIXME We can't use this for binary data because we
520              assume this is a string.  For the current usage of colon
521              output it is correct.  */
522           char *src = line + 2;
523           char *end = line + linelen;
524           char *dst = src;
525
526           linelen = 0;
527           while (src < end)
528             {
529               if (*src == '%' && src + 2 < end)
530                 {
531                   /* Handle escaped characters.  */
532                   ++src;
533                   *dst++ = _gpgme_hextobyte (src);
534                   src += 2;
535                 }
536               else
537                 *dst++ = *src++;
538
539               linelen++;
540             }
541
542           src = line + 2;
543           if (linelen && g13->user.data_cb)
544             err = g13->user.data_cb (g13->user.data_cb_value,
545                                        src, linelen);
546           else
547             err = 0;
548
549           TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
550                   "fd 0x%x: D inlinedata; status from cb: %s",
551                   fd, (g13->user.data_cb ?
552                        (err? gpg_strerror (err):"ok"):"no callback"));
553
554         }
555       else if (linelen > 2
556                && line[0] == 'S' && line[1] == ' ')
557         {
558           char *src;
559           char *args;
560
561           src = line + 2;
562           while (*src == ' ')
563             src++;
564
565           args = strchr (line + 2, ' ');
566           if (!args)
567             args = line + linelen; /* set to an empty string */
568           else
569             *(args++) = 0;
570
571           while (*args == ' ')
572             args++;
573
574           if (g13->user.status_cb)
575             err = g13->user.status_cb (g13->user.status_cb_value,
576                                        src, args);
577           else
578             err = 0;
579
580           TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
581                   "fd 0x%x: S line (%s) - status from cb: %s",
582                   fd, line+2, (g13->user.status_cb ?
583                                (err? gpg_strerror (err):"ok"):"no callback"));
584         }
585       else if (linelen >= 7
586                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
587                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
588                && line[6] == 'E'
589                && (line[7] == '\0' || line[7] == ' '))
590         {
591           char *src;
592           char *args;
593
594           for (src=line+7; *src == ' '; src++)
595             ;
596
597           args = strchr (src, ' ');
598           if (!args)
599             args = line + linelen; /* Let it point to an empty string.  */
600           else
601             *(args++) = 0;
602
603           while (*args == ' ')
604             args++;
605
606           err = default_inq_cb (g13, src, args);
607           if (!err)
608             {
609               /* Flush and send END.  */
610               err = assuan_send_data (g13->assuan_ctx, NULL, 0);
611             }
612           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
613             {
614               /* Flush and send CANcel.  */
615               err = assuan_send_data (g13->assuan_ctx, NULL, 1);
616             }
617           assuan_write_line (g13->assuan_ctx, "END");
618         }
619     }
620   while (!err && assuan_pending_line (g13->assuan_ctx));
621
622   return err;
623 }
624
625
626 static gpgme_error_t
627 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
628 {
629   gpgme_error_t err;
630
631   TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
632               "fd %d, dir %d", iocbd->fd, iocbd->dir);
633   err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
634                               iocbd->fd, iocbd->dir,
635                               handler, iocbd->data, &iocbd->tag);
636   if (err)
637     return TRACE_ERR (err);
638   if (!iocbd->dir)
639     /* FIXME Kludge around poll() problem.  */
640     err = _gpgme_io_set_nonblocking (iocbd->fd);
641   return TRACE_ERR (err);
642 }
643
644
645 static gpgme_error_t
646 start (engine_g13_t g13, const char *command)
647 {
648   gpgme_error_t err;
649   assuan_fd_t afdlist[5];
650   int fdlist[5];
651   int nfds;
652   int i;
653
654   /* We need to know the fd used by assuan for reads.  We do this by
655      using the assumption that the first returned fd from
656      assuan_get_active_fds() is always this one.  */
657   nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
658                                 afdlist, DIM (afdlist));
659   if (nfds < 1)
660     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
661   /* For now... */
662   for (i = 0; i < nfds; i++)
663     fdlist[i] = (int) afdlist[i];
664
665   /* We "duplicate" the file descriptor, so we can close it here (we
666      can't close fdlist[0], as that is closed by libassuan, and
667      closing it here might cause libassuan to close some unrelated FD
668      later).  Alternatively, we could special case status_fd and
669      register/unregister it manually as needed, but this increases
670      code duplication and is more complicated as we can not use the
671      close notifications etc.  A third alternative would be to let
672      Assuan know that we closed the FD, but that complicates the
673      Assuan interface.  */
674
675   g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
676   if (g13->status_cb.fd < 0)
677     return gpg_error_from_syserror ();
678
679   if (_gpgme_io_set_close_notify (g13->status_cb.fd,
680                                   close_notify_handler, g13))
681     {
682       _gpgme_io_close (g13->status_cb.fd);
683       g13->status_cb.fd = -1;
684       return gpg_error (GPG_ERR_GENERAL);
685     }
686
687   err = add_io_cb (g13, &g13->status_cb, status_handler);
688   if (!err)
689     err = assuan_write_line (g13->assuan_ctx, command);
690
691   if (!err)
692     g13_io_event (g13, GPGME_EVENT_START, NULL);
693
694   return err;
695 }
696
697
698 #if USE_DESCRIPTOR_PASSING
699 static gpgme_error_t
700 g13_reset (void *engine)
701 {
702   engine_g13_t g13 = engine;
703
704   /* We must send a reset because we need to reset the list of
705      signers.  Note that RESET does not reset OPTION commands. */
706   return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
707 }
708 #endif
709
710
711 static gpgme_error_t
712 g13_transact (void *engine,
713                 const char *command,
714                 gpgme_assuan_data_cb_t data_cb,
715                 void *data_cb_value,
716                 gpgme_assuan_inquire_cb_t inq_cb,
717                 void *inq_cb_value,
718                 gpgme_assuan_status_cb_t status_cb,
719                 void *status_cb_value)
720 {
721   engine_g13_t g13 = engine;
722   gpgme_error_t err;
723
724   if (!g13 || !command || !*command)
725     return gpg_error (GPG_ERR_INV_VALUE);
726
727   g13->user.data_cb = data_cb;
728   g13->user.data_cb_value = data_cb_value;
729   g13->user.inq_cb = inq_cb;
730   g13->user.inq_cb_value = inq_cb_value;
731   g13->user.status_cb = status_cb;
732   g13->user.status_cb_value = status_cb_value;
733
734   err = start (g13, command);
735   return err;
736 }
737
738
739
740 static void
741 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
742 {
743   engine_g13_t g13 = engine;
744   g13->io_cbs = *io_cbs;
745 }
746
747
748 static void
749 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
750 {
751   engine_g13_t g13 = engine;
752
753   TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
754           "event %p, type %d, type_data %p",
755           g13->io_cbs.event, type, type_data);
756   if (g13->io_cbs.event)
757     (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
758 }
759
760
761 struct engine_ops _gpgme_engine_ops_g13 =
762   {
763     /* Static functions.  */
764     _gpgme_get_default_g13_name,
765     NULL,
766     g13_get_version,
767     g13_get_req_version,
768     g13_new,
769
770     /* Member functions.  */
771     g13_release,
772 #if USE_DESCRIPTOR_PASSING
773     g13_reset,
774 #else
775     NULL,                       /* reset */
776 #endif
777     NULL,               /* set_status_cb */
778     NULL,               /* set_status_handler */
779     NULL,               /* set_command_handler */
780     NULL,               /* set_colon_line_handler */
781     g13_set_locale,
782     NULL,               /* set_protocol */
783     NULL,               /* decrypt */
784     NULL,               /* decrypt_verify */
785     NULL,               /* delete */
786     NULL,               /* edit */
787     NULL,               /* encrypt */
788     NULL,               /* encrypt_sign */
789     NULL,               /* export */
790     NULL,               /* export_ext */
791     NULL,               /* genkey */
792     NULL,               /* import */
793     NULL,               /* keylist */
794     NULL,               /* keylist_ext */
795     NULL,               /* keysign */
796     NULL,               /* tofu_policy */
797     NULL,               /* sign */
798     NULL,               /* trustlist */
799     NULL,               /* verify */
800     NULL,               /* getauditlog */
801     g13_transact,
802     NULL,               /* conf_load */
803     NULL,               /* conf_save */
804     g13_set_io_cbs,
805     g13_io_event,
806     g13_cancel,
807     g13_cancel_op,
808     NULL,               /* passwd */
809     NULL,               /* set_pinentry_mode */
810     NULL                /* opspawn */
811   };