2010-05-07 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / src / engine-assuan.c
1 /* engine-assuan.c - Low-level Assuan protocol engine
2  * Copyright (C) 2009 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21    Note: This engine requires a modern Assuan server which uses
22    gpg-error codes.  In particular there is no backward compatible
23    mapping of old Assuan error codes implemented.
24 */
25
26
27 #if HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
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 } iocb_data_t;
62
63 /* Engine instance data.  */
64 struct engine_llass
65 {
66   assuan_context_t assuan_ctx;
67
68   int lc_ctype_set;
69   int lc_messages_set;
70
71   iocb_data_t status_cb;
72
73   struct gpgme_io_cbs io_cbs;
74
75   /* Hack for old opassuan.c interface, see there the result struct.  */
76   gpg_error_t last_op_err;
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   /* Option flags.  */
91   struct {
92     int gpg_agent:1;  /* Assume this is a gpg-agent connection.  */
93   } opt;
94
95 };
96 typedef struct engine_llass *engine_llass_t;
97
98
99 gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
100 {
101   engine_llass_t llass = engine;
102   return llass->last_op_err;
103 }
104
105
106 /* Prototypes.  */
107 static void llass_io_event (void *engine,
108                             gpgme_event_io_t type, void *type_data);
109
110
111
112
113 \f
114 /* return the default home directory.  */
115 static const char *
116 llass_get_home_dir (void)
117 {
118   /* For this engine the home directory is not a filename but a string
119      used to convey options.  The exclamation mark is a marker to show
120      that this is not a directory name. Options are strings delimited
121      by a space.  The only option defined for now is GPG_AGENT to
122      enable GPG_AGENT specific commands to send to the server at
123      connection startup.  */
124   return "!GPG_AGENT";
125 }
126
127 static char *
128 llass_get_version (const char *file_name)
129 {
130   return strdup ("1.0");
131 }
132
133
134 static const char *
135 llass_get_req_version (void)
136 {
137   return "1.0";
138 }
139
140 \f
141 static void
142 close_notify_handler (int fd, void *opaque)
143 {
144   engine_llass_t llass = opaque;
145
146   assert (fd != -1);
147   if (llass->status_cb.fd == fd)
148     {
149       if (llass->status_cb.tag)
150         llass->io_cbs.remove (llass->status_cb.tag);
151       llass->status_cb.fd = -1;
152       llass->status_cb.tag = NULL;
153     }
154 }
155
156
157
158 static gpgme_error_t
159 llass_cancel (void *engine)
160 {
161   engine_llass_t llass = engine;
162
163   if (!llass)
164     return gpg_error (GPG_ERR_INV_VALUE);
165
166   if (llass->status_cb.fd != -1)
167     _gpgme_io_close (llass->status_cb.fd);
168
169   if (llass->assuan_ctx)
170     {
171       assuan_release (llass->assuan_ctx);
172       llass->assuan_ctx = NULL;
173     }
174
175   return 0;
176 }
177
178
179 static gpgme_error_t
180 llass_cancel_op (void *engine)
181 {
182   engine_llass_t llass = engine;
183
184   if (!llass)
185     return gpg_error (GPG_ERR_INV_VALUE);
186
187   if (llass->status_cb.fd != -1)
188     _gpgme_io_close (llass->status_cb.fd);
189
190   return 0;
191 }
192
193
194 static void
195 llass_release (void *engine)
196 {
197   engine_llass_t llass = engine;
198
199   if (!llass)
200     return;
201
202   llass_cancel (engine);
203
204   free (llass);
205 }
206
207
208 /* Create a new instance. If HOME_DIR is NULL standard options for use
209    with gpg-agent are issued.  */  
210 static gpgme_error_t
211 llass_new (void **engine, const char *file_name, const char *home_dir)
212 {
213   gpgme_error_t err = 0;
214   engine_llass_t llass;
215   char *optstr;
216
217   llass = calloc (1, sizeof *llass);
218   if (!llass)
219     return gpg_error_from_syserror ();
220
221   llass->status_cb.fd = -1;
222   llass->status_cb.dir = 1;
223   llass->status_cb.tag = 0;
224   llass->status_cb.data = llass;
225
226   /* Parse_options.  */
227   if (home_dir && *home_dir == '!')
228     {
229       home_dir++;
230       /* Very simple parser only working for the one option we support.  */
231       /* Note that wk promised to write a regression test if this
232          parser will be extended.  */
233       if (!strncmp (home_dir, "GPG_AGENT", 9) 
234           && (!home_dir[9] || home_dir[9] == ' '))
235         llass->opt.gpg_agent = 1;
236     }
237
238   err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
239                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
240                         NULL);
241   if (err)
242     goto leave;
243   assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
244
245   err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
246   if (err)
247     goto leave;
248
249   if (llass->opt.gpg_agent)
250     {
251       char *dft_display = NULL;
252
253       err = _gpgme_getenv ("DISPLAY", &dft_display);
254       if (err)
255         goto leave;
256       if (dft_display)
257         {
258           if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
259             {
260               err = gpg_error_from_syserror ();
261               free (dft_display);
262               goto leave;
263             }
264           free (dft_display);
265
266           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
267                                  NULL, NULL, NULL);
268           free (optstr);
269           if (err)
270             goto leave;
271         }
272     }
273
274   if (llass->opt.gpg_agent && isatty (1))
275     {
276       int rc;
277       char dft_ttyname[64];
278       char *dft_ttytype = NULL;
279
280       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
281       if (rc)
282         {
283           err = gpg_error_from_errno (rc);
284           goto leave;
285         }
286       else
287         {
288           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
289             {
290               err = gpg_error_from_syserror ();
291               goto leave;
292             }
293           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
294                                  NULL, NULL, NULL);
295           free (optstr);
296           if (err)
297             goto leave;
298
299           err = _gpgme_getenv ("TERM", &dft_ttytype);
300           if (err)
301             goto leave;
302           if (dft_ttytype)
303             {
304               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
305                 {
306                   err = gpg_error_from_syserror ();
307                   free (dft_ttytype);
308                   goto leave;
309                 }
310               free (dft_ttytype);
311               
312               err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
313                                      NULL, NULL, NULL, NULL);
314               free (optstr);
315               if (err)
316                 goto leave;
317             }
318         }
319     }
320
321
322 #ifdef HAVE_W32_SYSTEM
323   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
324      llass to tell us when it needs it.  */
325   if (!err && llass->opt.gpg_agent)
326     {
327       err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
328                              NULL, NULL, NULL, NULL, NULL, NULL);
329       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
330         err = 0; /* This work only with recent gpg-agents.  */
331     }
332 #endif /*HAVE_W32_SYSTEM*/
333
334
335  leave:
336   /* Close the server ends of the pipes (because of this, we must use
337      the stored server_fd_str in the function start).  Our ends are
338      closed in llass_release().  */
339
340   if (err)
341     llass_release (llass);
342   else
343     *engine = llass;
344
345   return err;
346 }
347
348
349 static gpgme_error_t
350 llass_set_locale (void *engine, int category, const char *value)
351 {
352   gpgme_error_t err;
353   engine_llass_t llass = engine;
354   char *optstr;
355   char *catstr;
356
357   if (!llass->opt.gpg_agent)
358     return 0;
359
360   /* FIXME: If value is NULL, we need to reset the option to default.
361      But we can't do this.  So we error out here.  gpg-agent needs
362      support for this.  */
363   if (0)
364     ;
365 #ifdef LC_CTYPE
366   else if (category == LC_CTYPE)
367     {
368       catstr = "lc-ctype";
369       if (!value && llass->lc_ctype_set)
370         return gpg_error (GPG_ERR_INV_VALUE);
371       if (value)
372         llass->lc_ctype_set = 1;
373     }
374 #endif
375 #ifdef LC_MESSAGES
376   else if (category == LC_MESSAGES)
377     {
378       catstr = "lc-messages";
379       if (!value && llass->lc_messages_set)
380         return gpg_error (GPG_ERR_INV_VALUE);
381       if (value)
382         llass->lc_messages_set = 1;
383     }
384 #endif /* LC_MESSAGES */
385   else
386     return gpg_error (GPG_ERR_INV_VALUE);
387
388   /* FIXME: Reset value to default.  */
389   if (!value)
390     return 0;
391
392   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
393     err = gpg_error_from_errno (errno);
394   else
395     {
396       err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
397                              NULL, NULL, NULL, NULL);
398       free (optstr);
399     }
400   return err;
401 }
402
403
404 /* This is the inquiry callback.  It handles stuff which ee need to
405    handle here and passes everything on to the user callback.  */
406 static gpgme_error_t
407 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
408 {
409   gpg_error_t err;
410
411   if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
412     {
413       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
414     }
415
416   if (llass->user.inq_cb)
417     {
418       gpgme_data_t data = NULL;
419
420       err = llass->user.inq_cb (llass->user.inq_cb_value,
421                                 keyword, args, &data);
422       if (!err && data)
423         {
424           /* FIXME: Returning data is not yet implemented.  However we
425              need to allow the caller to cleanup his data object.
426              Thus we run the callback in finish mode immediately.  */
427           err = llass->user.inq_cb (llass->user.inq_cb_value,
428                                     NULL, NULL, &data);
429         }
430     }
431   else
432     err = 0;
433
434   return err;
435 }
436
437
438 static gpgme_error_t
439 llass_status_handler (void *opaque, int fd)
440 {
441   struct io_cb_data *data = (struct io_cb_data *) opaque;
442   engine_llass_t llass = (engine_llass_t) data->handler_value;
443   gpgme_error_t err = 0;
444   char *line;
445   size_t linelen;
446
447   do
448     {
449       err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
450       if (err)
451         {
452           /* Reading a full line may not be possible when
453              communicating over a socket in nonblocking mode.  In this
454              case, we are done for now.  */
455           if (gpg_err_code (err) == GPG_ERR_EAGAIN)
456             {
457               TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
458                       "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
459               err = 0;
460               continue;
461             }
462           
463           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
464                   "fd 0x%x: error reading assuan line: %s",
465                   fd, gpg_strerror (err));
466         }
467       else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
468         {
469           char *src = line + 2;
470           char *end = line + linelen;
471           char *dst = src;
472
473           linelen = 0;
474           while (src < end)
475             {
476               if (*src == '%' && src + 2 < end)
477                 {
478                   /* Handle escaped characters.  */
479                   ++src;
480                   *dst++ = _gpgme_hextobyte (src);
481                   src += 2;
482                 }
483               else
484                 *dst++ = *src++;
485
486               linelen++;
487             }
488
489           src = line + 2;
490           if (linelen && llass->user.data_cb)
491             err = llass->user.data_cb (llass->user.data_cb_value,
492                                        src, linelen);
493
494           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
495                   "fd 0x%x: D inlinedata; status from cb: %s",
496                   fd, (llass->user.data_cb ?
497                        (err? gpg_strerror (err):"ok"):"no callback"));
498         }
499       else if (linelen >= 3
500                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
501                && (line[3] == '\0' || line[3] == ' '))
502         {
503           /* END received.  Tell the data callback.  */
504           if (llass->user.data_cb)
505             err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
506
507           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
508                   "fd 0x%x: END line; status from cb: %s",
509                   fd, (llass->user.data_cb ?
510                        (err? gpg_strerror (err):"ok"):"no callback"));
511         }
512       else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
513         {
514           char *args;
515           char *src;
516
517           for (src=line+2; *src == ' '; src++)
518             ;
519
520           args = strchr (src, ' ');
521           if (!args)
522             args = line + linelen; /* Let it point to an empty string.  */
523           else
524             *(args++) = 0;
525
526           while (*args == ' ')
527             args++;
528
529           if (llass->user.status_cb)
530             err = llass->user.status_cb (llass->user.status_cb_value,
531                                          src, args);
532
533           TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
534                   "fd 0x%x: S line (%s) - status from cb: %s",
535                   fd, line+2, (llass->user.status_cb ?
536                                (err? gpg_strerror (err):"ok"):"no callback"));
537         }
538       else if (linelen >= 7
539                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
540                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
541                && line[6] == 'E'
542                && (line[7] == '\0' || line[7] == ' '))
543         {
544           char *src;
545           char *args;
546
547           for (src=line+7; *src == ' '; src++)
548             ;
549
550           args = strchr (src, ' ');
551           if (!args)
552             args = line + linelen; /* Let it point to an empty string.  */
553           else
554             *(args++) = 0;
555
556           while (*args == ' ')
557             args++;
558
559           err = inquire_cb (llass, src, args);
560           if (!err) 
561             {
562               /* Flush and send END.  */
563               err = assuan_send_data (llass->assuan_ctx, NULL, 0);
564             }
565           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
566             {
567               /* Flush and send CANcel.  */
568               err = assuan_send_data (llass->assuan_ctx, NULL, 1);
569             }
570         }
571       else if (linelen >= 3
572                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
573                && (line[3] == '\0' || line[3] == ' '))
574         {
575           if (line[3] == ' ')
576             err = atoi (line+4);
577           else
578             err = gpg_error (GPG_ERR_GENERAL);
579           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
580                   "fd 0x%x: ERR line: %s",
581                   fd, err ? gpg_strerror (err) : "ok");
582
583           /* Command execution errors are not fatal, as we use
584              a session based protocol.  */
585           data->op_err = err;
586           llass->last_op_err = err;
587
588           /* The caller will do the rest (namely, call cancel_op,
589              which closes status_fd).  */
590           return 0;
591         }
592       else if (linelen >= 2
593                && line[0] == 'O' && line[1] == 'K'
594                && (line[2] == '\0' || line[2] == ' '))
595         {
596           TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
597                   "fd 0x%x: OK line", fd);
598
599           llass->last_op_err = 0;
600
601           _gpgme_io_close (llass->status_cb.fd);
602           return 0;
603         }
604       else
605         {
606           /* Comment line or invalid line.  */
607         }
608
609     }
610   while (!err && assuan_pending_line (llass->assuan_ctx));
611
612   return err;
613 }
614
615
616 static gpgme_error_t
617 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
618 {
619   gpgme_error_t err;
620
621   TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
622               "fd %d, dir %d", iocbd->fd, iocbd->dir);
623   err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
624                               iocbd->fd, iocbd->dir,
625                               handler, iocbd->data, &iocbd->tag);
626   if (err)
627     return TRACE_ERR (err);
628   if (!iocbd->dir)
629     /* FIXME Kludge around poll() problem.  */
630     err = _gpgme_io_set_nonblocking (iocbd->fd);
631   return TRACE_ERR (err);
632 }
633
634
635 static gpgme_error_t
636 start (engine_llass_t llass, const char *command)
637 {
638   gpgme_error_t err;
639   assuan_fd_t afdlist[5];
640   int fdlist[5];
641   int nfds;
642   int i;
643
644   /* We need to know the fd used by assuan for reads.  We do this by
645      using the assumption that the first returned fd from
646      assuan_get_active_fds() is always this one.  */
647   nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
648                                 afdlist, DIM (afdlist));
649   if (nfds < 1)
650     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
651   /* For now... */
652   for (i = 0; i < nfds; i++)
653     fdlist[i] = (int) afdlist[i];
654
655   /* We "duplicate" the file descriptor, so we can close it here (we
656      can't close fdlist[0], as that is closed by libassuan, and
657      closing it here might cause libassuan to close some unrelated FD
658      later).  Alternatively, we could special case status_fd and
659      register/unregister it manually as needed, but this increases
660      code duplication and is more complicated as we can not use the
661      close notifications etc.  A third alternative would be to let
662      Assuan know that we closed the FD, but that complicates the
663      Assuan interface.  */
664
665   llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
666   if (llass->status_cb.fd < 0)
667     return gpg_error_from_syserror ();
668
669   if (_gpgme_io_set_close_notify (llass->status_cb.fd,
670                                   close_notify_handler, llass))
671     {
672       _gpgme_io_close (llass->status_cb.fd);
673       llass->status_cb.fd = -1;
674       return gpg_error (GPG_ERR_GENERAL);
675     }
676
677   err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
678   if (!err)
679     err = assuan_write_line (llass->assuan_ctx, command);
680
681   /* FIXME: If *command == '#' no answer is expected.  */
682
683   if (!err)
684     llass_io_event (llass, GPGME_EVENT_START, NULL);
685
686   return err;
687 }
688
689
690
691 static gpgme_error_t
692 llass_transact (void *engine,
693                 const char *command,
694                 gpgme_assuan_data_cb_t data_cb,
695                 void *data_cb_value,
696                 gpgme_assuan_inquire_cb_t inq_cb,
697                 void *inq_cb_value,
698                 gpgme_assuan_status_cb_t status_cb,
699                 void *status_cb_value)
700 {
701   engine_llass_t llass = engine;
702   gpgme_error_t err;
703
704   if (!llass || !command || !*command)
705     return gpg_error (GPG_ERR_INV_VALUE);
706
707   llass->user.data_cb = data_cb;
708   llass->user.data_cb_value = data_cb_value;
709   llass->user.inq_cb = inq_cb;
710   llass->user.inq_cb_value = inq_cb_value;
711   llass->user.status_cb = status_cb;
712   llass->user.status_cb_value = status_cb_value;
713
714   err = start (llass, command);
715   return err;
716 }
717
718
719
720 static void
721 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
722 {
723   engine_llass_t llass = engine;
724   llass->io_cbs = *io_cbs;
725 }
726
727
728 static void
729 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
730 {
731   engine_llass_t llass = engine;
732
733   TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
734           "event %p, type %d, type_data %p",
735           llass->io_cbs.event, type, type_data);
736   if (llass->io_cbs.event)
737     (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
738 }
739
740
741 struct engine_ops _gpgme_engine_ops_assuan =
742   {
743     /* Static functions.  */
744     _gpgme_get_default_agent_socket,
745     llass_get_home_dir,
746     llass_get_version,
747     llass_get_req_version,
748     llass_new,
749
750     /* Member functions.  */
751     llass_release,
752     NULL,               /* reset */
753     NULL,               /* set_status_handler */
754     NULL,               /* set_command_handler */
755     NULL,               /* set_colon_line_handler */
756     llass_set_locale,
757     NULL,               /* set_protocol */
758     NULL,               /* decrypt */
759     NULL,               /* decrypt_verify */
760     NULL,               /* delete */
761     NULL,               /* edit */
762     NULL,               /* encrypt */
763     NULL,               /* encrypt_sign */
764     NULL,               /* export */
765     NULL,               /* export_ext */
766     NULL,               /* genkey */
767     NULL,               /* import */
768     NULL,               /* keylist */
769     NULL,               /* keylist_ext */
770     NULL,               /* sign */
771     NULL,               /* trustlist */
772     NULL,               /* verify */
773     NULL,               /* getauditlog */
774     llass_transact,     /* opassuan_transact */
775     NULL,               /* conf_load */
776     NULL,               /* conf_save */
777     llass_set_io_cbs,
778     llass_io_event,
779     llass_cancel,
780     llass_cancel_op
781   };