2009-10-26 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 (&llass->assuan_ctx);
237   if (err)
238     goto leave;
239   err = assuan_socket_connect (llass->assuan_ctx, file_name, 0);
240   if (err)
241     goto leave;
242
243   if (llass->opt.gpg_agent)
244     {
245       char *dft_display = NULL;
246
247       err = _gpgme_getenv ("DISPLAY", &dft_display);
248       if (err)
249         goto leave;
250       if (dft_display)
251         {
252           if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
253             {
254               err = gpg_error_from_syserror ();
255               free (dft_display);
256               goto leave;
257             }
258           free (dft_display);
259
260           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
261                                  NULL, NULL, NULL);
262           free (optstr);
263           if (err)
264             goto leave;
265         }
266     }
267
268   if (llass->opt.gpg_agent && isatty (1))
269     {
270       int rc;
271       char dft_ttyname[64];
272       char *dft_ttytype = NULL;
273
274       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
275       if (rc)
276         {
277           err = gpg_error_from_errno (rc);
278           goto leave;
279         }
280       else
281         {
282           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
283             {
284               err = gpg_error_from_syserror ();
285               goto leave;
286             }
287           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
288                                  NULL, NULL, NULL);
289           free (optstr);
290           if (err)
291             goto leave;
292
293           err = _gpgme_getenv ("TERM", &dft_ttytype);
294           if (err)
295             goto leave;
296           if (dft_ttytype)
297             {
298               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
299                 {
300                   err = gpg_error_from_syserror ();
301                   free (dft_ttytype);
302                   goto leave;
303                 }
304               free (dft_ttytype);
305               
306               err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
307                                      NULL, NULL, NULL, NULL);
308               free (optstr);
309               if (err)
310                 goto leave;
311             }
312         }
313     }
314
315
316 #ifdef HAVE_W32_SYSTEM
317   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
318      llass to tell us when it needs it.  */
319   if (!err && llass->opt.gpg_agent)
320     {
321       err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
322                              NULL, NULL, NULL, NULL, NULL, NULL);
323       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
324         err = 0; /* This work only with recent gpg-agents.  */
325     }
326 #endif /*HAVE_W32_SYSTEM*/
327
328
329  leave:
330   /* Close the server ends of the pipes (because of this, we must use
331      the stored server_fd_str in the function start).  Our ends are
332      closed in llass_release().  */
333
334   if (err)
335     llass_release (llass);
336   else
337     *engine = llass;
338
339   return err;
340 }
341
342
343 static gpgme_error_t
344 llass_set_locale (void *engine, int category, const char *value)
345 {
346   gpgme_error_t err;
347   engine_llass_t llass = engine;
348   char *optstr;
349   char *catstr;
350
351   if (!llass->opt.gpg_agent)
352     return 0;
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.  gpg-agent needs
356      support for this.  */
357   if (category == LC_CTYPE)
358     {
359       catstr = "lc-ctype";
360       if (!value && llass->lc_ctype_set)
361         return gpg_error (GPG_ERR_INV_VALUE);
362       if (value)
363         llass->lc_ctype_set = 1;
364     }
365 #ifdef LC_MESSAGES
366   else if (category == LC_MESSAGES)
367     {
368       catstr = "lc-messages";
369       if (!value && llass->lc_messages_set)
370         return gpg_error (GPG_ERR_INV_VALUE);
371       if (value)
372         llass->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 (llass->assuan_ctx, optstr, NULL, NULL,
387                              NULL, NULL, NULL, NULL);
388       free (optstr);
389     }
390   return err;
391 }
392
393
394 /* This is the inquiry callback.  It handles stuff which ee need to
395    handle here and passes everything on to the user callback.  */
396 static gpgme_error_t
397 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
398 {
399   gpg_error_t err;
400
401   if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
402     {
403       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
404     }
405
406   if (llass->user.inq_cb)
407     {
408       gpgme_data_t data = NULL;
409
410       err = llass->user.inq_cb (llass->user.inq_cb_value,
411                                 keyword, args, &data);
412       if (!err && data)
413         {
414           /* FIXME: Returning data is not yet implemented.  However we
415              need to allow the caller to cleanup his data object.
416              Thus we run the callback in finish mode immediately.  */
417           err = llass->user.inq_cb (llass->user.inq_cb_value,
418                                     NULL, NULL, &data);
419         }
420     }
421   else
422     err = 0;
423
424   return err;
425 }
426
427
428 static gpgme_error_t
429 llass_status_handler (void *opaque, int fd)
430 {
431   struct io_cb_data *data = (struct io_cb_data *) opaque;
432   engine_llass_t llass = (engine_llass_t) data->handler_value;
433   gpgme_error_t err = 0;
434   char *line;
435   size_t linelen;
436
437   do
438     {
439       err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
440       if (err)
441         {
442           /* Reading a full line may not be possible when
443              communicating over a socket in nonblocking mode.  In this
444              case, we are done for now.  */
445           if (gpg_err_code (err) == GPG_ERR_EAGAIN)
446             {
447               TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
448                       "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
449               err = 0;
450               continue;
451             }
452           
453           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
454                   "fd 0x%x: error reading assuan line: %s",
455                   fd, gpg_strerror (err));
456         }
457       else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
458         {
459           char *src = line + 2;
460           char *end = line + linelen;
461           char *dst = src;
462
463           linelen = 0;
464           while (src < end)
465             {
466               if (*src == '%' && src + 2 < end)
467                 {
468                   /* Handle escaped characters.  */
469                   ++src;
470                   *dst++ = _gpgme_hextobyte (src);
471                   src += 2;
472                 }
473               else
474                 *dst++ = *src++;
475
476               linelen++;
477             }
478
479           src = line + 2;
480           if (linelen && llass->user.data_cb)
481             err = llass->user.data_cb (llass->user.data_cb_value,
482                                        src, linelen);
483
484           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
485                   "fd 0x%x: D inlinedata; status from cb: %s",
486                   fd, (llass->user.data_cb ?
487                        (err? gpg_strerror (err):"ok"):"no callback"));
488         }
489       else if (linelen >= 3
490                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
491                && (line[3] == '\0' || line[3] == ' '))
492         {
493           /* END received.  Tell the data callback.  */
494           if (llass->user.data_cb)
495             err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
496
497           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
498                   "fd 0x%x: END line; status from cb: %s",
499                   fd, (llass->user.data_cb ?
500                        (err? gpg_strerror (err):"ok"):"no callback"));
501         }
502       else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
503         {
504           char *args;
505           char *src;
506
507           for (src=line+2; *src == ' '; src++)
508             ;
509
510           args = strchr (src, ' ');
511           if (!args)
512             args = line + linelen; /* Let it point to an empty string.  */
513           else
514             *(args++) = 0;
515
516           while (*args == ' ')
517             args++;
518
519           if (llass->user.status_cb)
520             err = llass->user.status_cb (llass->user.status_cb_value,
521                                          src, args);
522
523           TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
524                   "fd 0x%x: S line (%s) - status from cb: %s",
525                   fd, line+2, (llass->user.status_cb ?
526                                (err? gpg_strerror (err):"ok"):"no callback"));
527         }
528       else if (linelen >= 7
529                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
530                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
531                && line[6] == 'E'
532                && (line[7] == '\0' || line[7] == ' '))
533         {
534           char *src;
535           char *args;
536
537           for (src=line+7; *src == ' '; src++)
538             ;
539
540           args = strchr (src, ' ');
541           if (!args)
542             args = line + linelen; /* Let it point to an empty string.  */
543           else
544             *(args++) = 0;
545
546           while (*args == ' ')
547             args++;
548
549           err = inquire_cb (llass, src, args);
550           if (!err) 
551             {
552               /* Flush and send END.  */
553               err = assuan_send_data (llass->assuan_ctx, NULL, 0);
554             }
555           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
556             {
557               /* Flush and send CANcel.  */
558               err = assuan_send_data (llass->assuan_ctx, NULL, 1);
559             }
560         }
561       else if (linelen >= 3
562                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
563                && (line[3] == '\0' || line[3] == ' '))
564         {
565           if (line[3] == ' ')
566             err = atoi (line+4);
567           else
568             err = gpg_error (GPG_ERR_GENERAL);
569           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
570                   "fd 0x%x: ERR line: %s",
571                   fd, err ? gpg_strerror (err) : "ok");
572
573           /* Command execution errors are not fatal, as we use
574              a session based protocol.  */
575           data->op_err = err;
576           llass->last_op_err = err;
577
578           /* The caller will do the rest (namely, call cancel_op,
579              which closes status_fd).  */
580           return 0;
581         }
582       else if (linelen >= 2
583                && line[0] == 'O' && line[1] == 'K'
584                && (line[2] == '\0' || line[2] == ' '))
585         {
586           TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
587                   "fd 0x%x: OK line", fd);
588
589           llass->last_op_err = 0;
590
591           _gpgme_io_close (llass->status_cb.fd);
592           return 0;
593         }
594       else
595         {
596           /* Comment line or invalid line.  */
597         }
598
599     }
600   while (!err && assuan_pending_line (llass->assuan_ctx));
601
602   return err;
603 }
604
605
606 static gpgme_error_t
607 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
608 {
609   gpgme_error_t err;
610
611   TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
612               "fd %d, dir %d", iocbd->fd, iocbd->dir);
613   err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
614                               iocbd->fd, iocbd->dir,
615                               handler, iocbd->data, &iocbd->tag);
616   if (err)
617     return TRACE_ERR (err);
618   if (!iocbd->dir)
619     /* FIXME Kludge around poll() problem.  */
620     err = _gpgme_io_set_nonblocking (iocbd->fd);
621   return TRACE_ERR (err);
622 }
623
624
625 static gpgme_error_t
626 start (engine_llass_t llass, const char *command)
627 {
628   gpgme_error_t err;
629   int fdlist[5];
630   int nfds;
631
632   /* We need to know the fd used by assuan for reads.  We do this by
633      using the assumption that the first returned fd from
634      assuan_get_active_fds() is always this one.  */
635   nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
636                                 fdlist, DIM (fdlist));
637   if (nfds < 1)
638     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
639
640   /* We "duplicate" the file descriptor, so we can close it here (we
641      can't close fdlist[0], as that is closed by libassuan, and
642      closing it here might cause libassuan to close some unrelated FD
643      later).  Alternatively, we could special case status_fd and
644      register/unregister it manually as needed, but this increases
645      code duplication and is more complicated as we can not use the
646      close notifications etc.  A third alternative would be to let
647      Assuan know that we closed the FD, but that complicates the
648      Assuan interface.  */
649
650   llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
651   if (llass->status_cb.fd < 0)
652     return gpg_error_from_syserror ();
653
654   if (_gpgme_io_set_close_notify (llass->status_cb.fd,
655                                   close_notify_handler, llass))
656     {
657       _gpgme_io_close (llass->status_cb.fd);
658       llass->status_cb.fd = -1;
659       return gpg_error (GPG_ERR_GENERAL);
660     }
661
662   err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
663   if (!err)
664     err = assuan_write_line (llass->assuan_ctx, command);
665
666   /* FIXME: If *command == '#' no answer is expected.  */
667
668   if (!err)
669     llass_io_event (llass, GPGME_EVENT_START, NULL);
670
671   return err;
672 }
673
674
675
676 static gpgme_error_t
677 llass_transact (void *engine,
678                 const char *command,
679                 gpgme_assuan_data_cb_t data_cb,
680                 void *data_cb_value,
681                 gpgme_assuan_inquire_cb_t inq_cb,
682                 void *inq_cb_value,
683                 gpgme_assuan_status_cb_t status_cb,
684                 void *status_cb_value)
685 {
686   engine_llass_t llass = engine;
687   gpgme_error_t err;
688
689   if (!llass || !command || !*command)
690     return gpg_error (GPG_ERR_INV_VALUE);
691
692   llass->user.data_cb = data_cb;
693   llass->user.data_cb_value = data_cb_value;
694   llass->user.inq_cb = inq_cb;
695   llass->user.inq_cb_value = inq_cb_value;
696   llass->user.status_cb = status_cb;
697   llass->user.status_cb_value = status_cb_value;
698
699   err = start (llass, command);
700   return err;
701 }
702
703
704
705 static void
706 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
707 {
708   engine_llass_t llass = engine;
709   llass->io_cbs = *io_cbs;
710 }
711
712
713 static void
714 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
715 {
716   engine_llass_t llass = engine;
717
718   TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
719           "event %p, type %d, type_data %p",
720           llass->io_cbs.event, type, type_data);
721   if (llass->io_cbs.event)
722     (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
723 }
724
725
726 struct engine_ops _gpgme_engine_ops_assuan =
727   {
728     /* Static functions.  */
729     _gpgme_get_default_agent_socket,
730     llass_get_home_dir,
731     llass_get_version,
732     llass_get_req_version,
733     llass_new,
734
735     /* Member functions.  */
736     llass_release,
737     NULL,               /* reset */
738     NULL,               /* set_status_handler */
739     NULL,               /* set_command_handler */
740     NULL,               /* set_colon_line_handler */
741     llass_set_locale,
742     NULL,               /* decrypt */
743     NULL,               /* delete */
744     NULL,               /* edit */
745     NULL,               /* encrypt */
746     NULL,               /* encrypt_sign */
747     NULL,               /* export */
748     NULL,               /* export_ext */
749     NULL,               /* genkey */
750     NULL,               /* import */
751     NULL,               /* keylist */
752     NULL,               /* keylist_ext */
753     NULL,               /* sign */
754     NULL,               /* trustlist */
755     NULL,               /* verify */
756     NULL,               /* getauditlog */
757     llass_transact,     /* opassuan_transact */
758     NULL,               /* conf_load */
759     NULL,               /* conf_save */
760     llass_set_io_cbs,
761     llass_io_event,
762     llass_cancel,
763     llass_cancel_op
764   };