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