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