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