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