2009-11-10 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   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 static gpgme_error_t
396 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
397                            engine_status_handler_t status_fnc,
398                            void *status_fnc_value)
399 {
400   gpg_error_t err;
401   char *line;
402   size_t linelen;
403
404   err = assuan_write_line (ctx, cmd);
405   if (err)
406     return err;
407
408   do
409     {
410       err = assuan_read_line (ctx, &line, &linelen);
411       if (err)
412         return err;
413
414       if (*line == '#' || !linelen)
415         continue;
416
417       if (linelen >= 2
418           && line[0] == 'O' && line[1] == 'K'
419           && (line[2] == '\0' || line[2] == ' '))
420         return 0;
421       else if (linelen >= 4
422           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
423           && line[3] == ' ')
424         err = atoi (&line[4]);
425       else if (linelen >= 2
426                && line[0] == 'S' && line[1] == ' ')
427         {
428           char *rest;
429
430           rest = strchr (line + 2, ' ');
431           if (!rest)
432             rest = line + linelen; /* set to an empty string */
433           else
434             *(rest++) = 0;
435
436           /* Nothing to do with status lines.  */
437         }
438       else
439         err = gpg_error (GPG_ERR_GENERAL);
440     }
441   while (!err);
442
443   return err;
444 }
445
446
447 static gpgme_error_t
448 status_handler (void *opaque, int fd)
449 {
450   struct io_cb_data *data = (struct io_cb_data *) opaque;
451   engine_g13_t g13 = (engine_g13_t) data->handler_value;
452   gpgme_error_t err = 0;
453   char *line;
454   size_t linelen;
455
456   do
457     {
458       err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
459       if (err)
460         {
461           /* Try our best to terminate the connection friendly.  */
462           /*      assuan_write_line (g13->assuan_ctx, "BYE"); */
463           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
464                   "fd 0x%x: error reading assuan line: %s",
465                   fd, gpg_strerror (err));
466         }
467       else if (linelen >= 3
468                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
469                && (line[3] == '\0' || line[3] == ' '))
470         {
471           if (line[3] == ' ')
472             err = atoi (&line[4]);
473           if (! err)
474             err = gpg_error (GPG_ERR_GENERAL);
475           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
476                   "fd 0x%x: ERR line: %s",
477                   fd, err ? gpg_strerror (err) : "ok");
478           
479           /* Command execution errors are not fatal, as we use
480              a session based protocol.  */
481           data->op_err = err;
482
483           /* The caller will do the rest (namely, call cancel_op,
484              which closes status_fd).  */
485           return 0;
486         }
487       else if (linelen >= 2
488                && line[0] == 'O' && line[1] == 'K'
489                && (line[2] == '\0' || line[2] == ' '))
490         {
491           TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
492                   "fd 0x%x: OK line", fd);
493
494           _gpgme_io_close (g13->status_cb.fd);
495           return 0;
496         }
497       else if (linelen > 2
498                && line[0] == 'D' && line[1] == ' ')
499         {
500           /* We are using the colon handler even for plain inline data
501              - strange name for that function but for historic reasons
502              we keep it.  */
503           /* FIXME We can't use this for binary data because we
504              assume this is a string.  For the current usage of colon
505              output it is correct.  */
506           char *src = line + 2;
507           char *end = line + linelen;
508           char *dst = src;
509
510           linelen = 0;
511           while (src < end)
512             {
513               if (*src == '%' && src + 2 < end)
514                 {
515                   /* Handle escaped characters.  */
516                   ++src;
517                   *dst++ = _gpgme_hextobyte (src);
518                   src += 2;
519                 }
520               else
521                 *dst++ = *src++;
522
523               linelen++;
524             }
525
526           src = line + 2;
527           if (linelen && g13->user.data_cb)
528             err = g13->user.data_cb (g13->user.data_cb_value,
529                                        src, linelen);
530           else
531             err = 0;
532
533           TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
534                   "fd 0x%x: D inlinedata; status from cb: %s",
535                   fd, (g13->user.data_cb ?
536                        (err? gpg_strerror (err):"ok"):"no callback"));
537
538         }
539       else if (linelen > 2
540                && line[0] == 'S' && line[1] == ' ')
541         {
542           char *src;
543           char *args;
544
545           src = line + 2;
546           while (*src == ' ')
547             src++;
548           
549           args = strchr (line + 2, ' ');
550           if (!args)
551             args = line + linelen; /* set to an empty string */
552           else
553             *(args++) = 0;
554
555           while (*args == ' ')
556             args++;
557
558           if (g13->user.status_cb)
559             err = g13->user.status_cb (g13->user.status_cb_value,
560                                        src, args);
561           else
562             err = 0;
563
564           TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
565                   "fd 0x%x: S line (%s) - status from cb: %s",
566                   fd, line+2, (g13->user.status_cb ?
567                                (err? gpg_strerror (err):"ok"):"no callback"));
568         }
569       else if (linelen >= 7
570                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
571                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
572                && line[6] == 'E' 
573                && (line[7] == '\0' || line[7] == ' '))
574         {
575           char *src;
576           char *args;
577           
578           for (src=line+7; *src == ' '; src++)
579             ;
580
581           args = strchr (src, ' ');
582           if (!args)
583             args = line + linelen; /* Let it point to an empty string.  */
584           else
585             *(args++) = 0;
586
587           while (*args == ' ')
588             args++;
589
590           err = default_inq_cb (g13, src, args);
591           if (!err) 
592             {
593               /* Flush and send END.  */
594               err = assuan_send_data (g13->assuan_ctx, NULL, 0);
595             }
596           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
597             {
598               /* Flush and send CANcel.  */
599               err = assuan_send_data (g13->assuan_ctx, NULL, 1);
600             }
601           assuan_write_line (g13->assuan_ctx, "END");
602         }
603     }
604   while (!err && assuan_pending_line (g13->assuan_ctx));
605   
606   return err;
607 }
608   
609
610 static gpgme_error_t
611 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
612 {
613   gpgme_error_t err;
614
615   TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
616               "fd %d, dir %d", iocbd->fd, iocbd->dir);
617   err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
618                               iocbd->fd, iocbd->dir,
619                               handler, iocbd->data, &iocbd->tag);
620   if (err)
621     return TRACE_ERR (err);
622   if (!iocbd->dir)
623     /* FIXME Kludge around poll() problem.  */
624     err = _gpgme_io_set_nonblocking (iocbd->fd);
625   return TRACE_ERR (err);
626 }
627
628
629 static gpgme_error_t
630 start (engine_g13_t g13, const char *command)
631 {
632   gpgme_error_t err;
633   int fdlist[5];
634   int nfds;
635
636   /* We need to know the fd used by assuan for reads.  We do this by
637      using the assumption that the first returned fd from
638      assuan_get_active_fds() is always this one.  */
639   nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
640                                 fdlist, DIM (fdlist));
641   if (nfds < 1)
642     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
643
644   /* We "duplicate" the file descriptor, so we can close it here (we
645      can't close fdlist[0], as that is closed by libassuan, and
646      closing it here might cause libassuan to close some unrelated FD
647      later).  Alternatively, we could special case status_fd and
648      register/unregister it manually as needed, but this increases
649      code duplication and is more complicated as we can not use the
650      close notifications etc.  A third alternative would be to let
651      Assuan know that we closed the FD, but that complicates the
652      Assuan interface.  */
653
654   g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
655   if (g13->status_cb.fd < 0)
656     return gpg_error_from_syserror ();
657
658   if (_gpgme_io_set_close_notify (g13->status_cb.fd,
659                                   close_notify_handler, g13))
660     {
661       _gpgme_io_close (g13->status_cb.fd);
662       g13->status_cb.fd = -1;
663       return gpg_error (GPG_ERR_GENERAL);
664     }
665
666   err = add_io_cb (g13, &g13->status_cb, status_handler);
667   if (!err)
668     err = assuan_write_line (g13->assuan_ctx, command);
669
670   if (!err)
671     g13_io_event (g13, GPGME_EVENT_START, NULL);
672
673   return err;
674 }
675
676
677 #if USE_DESCRIPTOR_PASSING
678 static gpgme_error_t
679 g13_reset (void *engine)
680 {
681   engine_g13_t g13 = engine;
682
683   /* We must send a reset because we need to reset the list of
684      signers.  Note that RESET does not reset OPTION commands. */
685   return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
686 }
687 #endif
688
689
690 static gpgme_error_t
691 g13_transact (void *engine,
692                 const char *command,
693                 gpgme_assuan_data_cb_t data_cb,
694                 void *data_cb_value,
695                 gpgme_assuan_inquire_cb_t inq_cb,
696                 void *inq_cb_value,
697                 gpgme_assuan_status_cb_t status_cb,
698                 void *status_cb_value)
699 {
700   engine_g13_t g13 = engine;
701   gpgme_error_t err;
702
703   if (!g13 || !command || !*command)
704     return gpg_error (GPG_ERR_INV_VALUE);
705
706   g13->user.data_cb = data_cb;
707   g13->user.data_cb_value = data_cb_value;
708   g13->user.inq_cb = inq_cb;
709   g13->user.inq_cb_value = inq_cb_value;
710   g13->user.status_cb = status_cb;
711   g13->user.status_cb_value = status_cb_value;
712
713   err = start (g13, command);
714   return err;
715 }
716
717
718
719 static void
720 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
721 {
722   engine_g13_t g13 = engine;
723   g13->io_cbs = *io_cbs;
724 }
725
726
727 static void
728 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
729 {
730   engine_g13_t g13 = engine;
731
732   TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
733           "event %p, type %d, type_data %p",
734           g13->io_cbs.event, type, type_data);
735   if (g13->io_cbs.event)
736     (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
737 }
738
739
740 struct engine_ops _gpgme_engine_ops_g13 =
741   {
742     /* Static functions.  */
743     _gpgme_get_g13_path,
744     NULL,
745     g13_get_version,
746     g13_get_req_version,
747     g13_new,
748
749     /* Member functions.  */
750     g13_release,
751 #if USE_DESCRIPTOR_PASSING
752     g13_reset,
753 #else
754     NULL,                       /* reset */
755 #endif
756     NULL,               /* set_status_handler */
757     NULL,               /* set_command_handler */
758     NULL,               /* set_colon_line_handler */
759     g13_set_locale,
760     NULL,               /* set_protocol */
761     NULL,               /* decrypt */
762     NULL,               /* decrypt_verify */
763     NULL,               /* delete */
764     NULL,               /* edit */
765     NULL,               /* encrypt */
766     NULL,               /* encrypt_sign */
767     NULL,               /* export */
768     NULL,               /* export_ext */
769     NULL,               /* genkey */
770     NULL,               /* import */
771     NULL,               /* keylist */
772     NULL,               /* keylist_ext */
773     NULL,               /* sign */
774     NULL,               /* trustlist */
775     NULL,               /* verify */
776     NULL,               /* getauditlog */
777     g13_transact,
778     NULL,               /* conf_load */
779     NULL,               /* conf_save */
780     g13_set_io_cbs,
781     g13_io_event,
782     g13_cancel,
783     g13_cancel_op,
784   };