Use Assuan socket wrapper calls.
[gnupg.git] / tools / gpg-connect-agent.c
1 /* gpg-connect-agent.c - Tool to connect to the agent.
2  *      Copyright (C) 2005, 2007 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27 #include <assuan.h>
28
29 #include "i18n.h"
30 #include "../common/util.h"
31 #include "../common/asshelp.h"
32 #include "../common/sysutils.h"
33
34
35 /* Constants to identify the commands and options. */
36 enum cmd_and_opt_values
37   {
38     aNull = 0,
39     oQuiet      = 'q',
40     oVerbose    = 'v',
41     oRawSocket  = 'S',
42     oExec       = 'E',
43
44     oNoVerbose  = 500,
45     oHomedir,
46     oHex,
47     oDecode,
48     oNoExtConnect
49
50   };
51
52
53 /* The list of commands and options. */
54 static ARGPARSE_OPTS opts[] =
55   {
56     { 301, NULL, 0, N_("@\nOptions:\n ") },
57     
58     { oVerbose, "verbose",  0, N_("verbose") },
59     { oQuiet, "quiet",      0, N_("quiet") },
60     { oHex,   "hex",        0, N_("print data out hex encoded") },
61     { oDecode,"decode",     0, N_("decode received data lines") },
62     { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")},
63     { oExec, "exec", 0, N_("run the Assuan server given on the command line")},
64     { oNoExtConnect, "no-ext-connect",
65                             0, N_("do not use extended connect mode")},
66
67     /* hidden options */
68     { oNoVerbose, "no-verbose",  0, "@"},
69     { oHomedir, "homedir", 2, "@" },   
70     {0}
71   };
72
73
74 /* We keep all global options in the structure OPT.  */
75 struct
76 {
77   int verbose;          /* Verbosity level.  */
78   int quiet;            /* Be extra quiet.  */
79   const char *homedir;  /* Configuration directory name */
80   int hex;              /* Print data lines in hex format. */
81   int decode;           /* Decode received data lines.  */
82   const char *raw_socket; /* Name of socket to connect in raw mode. */
83   int exec;             /* Run the pgm given on the command line. */
84   unsigned int connect_flags;    /* Flags used for connecting. */
85 } opt;
86
87
88
89 /* Definitions for /definq commands and a global linked list with all
90    the definitions. */
91 struct definq_s
92 {
93   struct definq_s *next;
94   char *name;     /* Name of inquiry or NULL for any name. */
95   int is_prog;     /* True if this is a program to run. */
96   char file[1];   /* Name of file or program. */
97 };
98 typedef struct definq_s *definq_t;
99
100 static definq_t definq_list;
101 static definq_t *definq_list_tail = &definq_list;
102
103
104
105 /*-- local prototypes --*/
106 static int read_and_print_response (assuan_context_t ctx);
107 static assuan_context_t start_agent (void);
108
109
110
111 \f
112 /* Print usage information and and provide strings for help. */
113 static const char *
114 my_strusage( int level )
115 {
116   const char *p;
117
118   switch (level)
119     {
120     case 11: p = "gpg-connect-agent (GnuPG)";
121       break;
122     case 13: p = VERSION; break;
123     case 17: p = PRINTABLE_OS_NAME; break;
124     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
125       break;
126     case 1:
127     case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
128       break;
129     case 41:
130       p = _("Syntax: gpg-connect-agent [options]\n"
131             "Connect to a running agent and send commands\n");
132       break;
133     case 31: p = "\nHome: "; break;
134     case 32: p = opt.homedir; break;
135     case 33: p = "\n"; break;
136
137     default: p = NULL; break;
138     }
139   return p;
140 }
141
142
143 /* Store an inquire response pattern.  Note, that this function may
144    change the content of LINE.  We assume that leading white spaces
145    are already removed. */
146 static void
147 add_definq (char *line, int is_prog)
148 {
149   definq_t d;
150   char *name, *p;
151
152   /* Get name. */
153   name = line;
154   for (p=name; *p && !spacep (p); p++)
155     ;
156   if (*p)
157     *p++ = 0;
158   while (spacep (p))
159     p++;
160
161   d = xmalloc (sizeof *d + strlen (p) );
162   strcpy (d->file, p);
163   d->is_prog = is_prog;
164   if ( !strcmp (name, "*"))
165     d->name = NULL;
166   else
167     d->name = xstrdup (name);
168
169   d->next = NULL;
170   *definq_list_tail = d;
171   definq_list_tail = &d->next;
172 }
173
174
175 /* Show all inquiry defintions. */
176 static void
177 show_definq (void)
178 {
179   definq_t d;
180
181   for (d=definq_list; d; d = d->next)
182     if (d->name)
183       printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
184   for (d=definq_list; d; d = d->next)
185     if (!d->name)
186       printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
187 }
188
189
190 /* Clear all inquiry definitions. */
191 static void
192 clear_definq (void)
193 {
194   while (definq_list)
195     { 
196       definq_t tmp = definq_list->next;
197       xfree (definq_list->name);
198       xfree (definq_list);
199       definq_list = tmp;
200     }
201   definq_list_tail = &definq_list;
202 }      
203
204
205 static void
206 do_sendfd (assuan_context_t ctx, char *line)
207 {
208   FILE *fp;
209   char *name, *mode, *p;
210   int rc, fd;
211
212   /* Get file name. */
213   name = line;
214   for (p=name; *p && !spacep (p); p++)
215     ;
216   if (*p)
217     *p++ = 0;
218   while (spacep (p))
219     p++;
220
221   /* Get mode.  */
222   mode = p;
223   if (!*mode)
224     mode = "r";
225   else
226     {
227       for (p=mode; *p && !spacep (p); p++)
228         ;
229       if (*p)
230         *p++ = 0;
231     }
232
233   /* Open and send. */
234   fp = fopen (name, mode);
235   if (!fp)
236     {
237       log_error ("can't open `%s' in \"%s\" mode: %s\n",
238                  name, mode, strerror (errno));
239       return;
240     }
241   fd = fileno (fp);
242
243   if (opt.verbose)
244     log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
245                name, mode, fd);
246
247   rc = assuan_sendfd (ctx, INT2FD (fd) );
248   if (rc)
249     log_error ("sednig  descriptor %d failed: %s\n", fd, gpg_strerror (rc));
250   fclose (fp);
251 }
252
253
254 static void
255 do_recvfd (assuan_context_t ctx, char *line)
256 {
257   log_info ("This command has not yet been implemented\n");
258 }
259
260
261 /* gpg-connect-agent's entry point. */
262 int
263 main (int argc, char **argv)
264 {
265   ARGPARSE_ARGS pargs;
266   int no_more_options = 0;
267   assuan_context_t ctx;
268   char *line, *p;
269   size_t linesize;
270   int rc;
271
272   set_strusage (my_strusage);
273   log_set_prefix ("gpg-connect-agent", 1);
274
275   /* Make sure that our subsystems are ready.  */
276   init_common_subsystems ();
277
278   assuan_set_assuan_err_source (0);
279
280   i18n_init();
281
282   opt.homedir = default_homedir ();
283   opt.connect_flags = 1; /* Use extended connect mode.  */
284
285   /* Parse the command line. */
286   pargs.argc  = &argc;
287   pargs.argv  = &argv;
288   pargs.flags =  1;  /* Do not remove the args.  */
289   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
290     {
291       switch (pargs.r_opt)
292         {
293         case oQuiet:     opt.quiet = 1; break;
294         case oVerbose:   opt.verbose++; break;
295         case oNoVerbose: opt.verbose = 0; break;
296         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
297         case oHex:       opt.hex = 1; break;
298         case oDecode:    opt.decode = 1; break;
299         case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
300         case oExec:      opt.exec = 1; break;
301         case oNoExtConnect: opt.connect_flags &= ~(1); break;
302
303         default: pargs.err = 2; break;
304         }
305     }
306
307   if (log_get_errorcount (0))
308     exit (2);
309
310   if (opt.exec)
311     {
312       if (!argc)
313         {
314           log_error (_("option \"%s\" requires a program "
315                        "and optional arguments\n"), "--exec" );
316           exit (1);
317         }
318     }
319   else if (argc)
320     usage (1);
321
322   if (opt.exec && opt.raw_socket)
323     log_info (_("option \"%s\" ignored due to \"%s\"\n"),
324               "--raw-socket", "--exec");
325
326   if (opt.exec)
327     {
328       int no_close[3];
329
330       no_close[0] = fileno (stderr);
331       no_close[1] = log_get_fd ();
332       no_close[2] = -1;
333       rc = assuan_pipe_connect_ext (&ctx, *argv, (const char **)argv,
334                                     no_close, NULL, NULL,
335                                     opt.connect_flags);
336       if (rc)
337         {
338           log_error ("assuan_pipe_connect_ext failed: %s\n",
339                      gpg_strerror (rc));
340           exit (1);
341         }
342
343       if (opt.verbose)
344         log_info ("server `%s' started\n", *argv);
345
346     }
347   else if (opt.raw_socket)
348     {
349       rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
350                                       opt.connect_flags);
351       if (rc)
352         {
353           log_error ("can't connect to socket `%s': %s\n",
354                      opt.raw_socket, gpg_strerror (rc));
355           exit (1);
356         }
357
358       if (opt.verbose)
359         log_info ("connection to socket `%s' established\n", opt.raw_socket);
360     }
361   else
362     ctx = start_agent ();
363
364   /* See whether there is a line pending from the server (in case
365      assuan did not run the initial handshaking).  */
366   if (assuan_pending_line (ctx))
367     {
368       rc = read_and_print_response (ctx);
369       if (rc)
370         log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
371     }
372
373   line = NULL;
374   linesize = 0;
375   for (;;)
376     {
377       int n;
378       size_t maxlength;
379
380       maxlength = 2048;
381       n = read_line (stdin, &line, &linesize, &maxlength);
382       if (n < 0)
383         {
384           log_error (_("error reading input: %s\n"), strerror (errno));
385           exit (1);
386         }
387       if (!n)
388         break; /* EOF */
389       if (!maxlength)
390         {
391           log_error (_("line too long - skipped\n"));
392           continue;
393         }
394       if (memchr (line, 0, n))
395         log_info (_("line shortened due to embedded Nul character\n"));
396       if (line[n-1] == '\n')
397         line[n-1] = 0;
398       if (*line == '/')
399         {
400           /* Handle control commands. */
401           char *cmd = line+1;
402
403           for (p=cmd; *p && !spacep (p); p++)
404             ;
405           if (*p)
406             *p++ = 0;
407           while (spacep (p))
408             p++;
409           if (!strcmp (cmd, "definqfile"))
410             {
411               add_definq (p, 0);
412             }
413           else if (!strcmp (cmd, "definqprog"))
414             {
415               add_definq (p, 1);
416             }
417           else if (!strcmp (cmd, "showdef"))
418             {
419               show_definq ();
420             }
421           else if (!strcmp (cmd, "cleardef"))
422             {
423               clear_definq ();
424             }
425           else if (!strcmp (cmd, "echo"))
426             {
427               puts (p);
428             }
429           else if (!strcmp (cmd, "sendfd"))
430             {
431               do_sendfd (ctx, p);
432               continue;
433             }
434           else if (!strcmp (cmd, "recvfd"))
435             {
436               do_recvfd (ctx, p);
437               continue;
438             }
439           else if (!strcmp (cmd, "hex"))
440             opt.hex = 1;
441           else if (!strcmp (cmd, "nohex"))
442             opt.hex = 0;
443           else if (!strcmp (cmd, "decode"))
444             opt.decode = 1;
445           else if (!strcmp (cmd, "nodecode"))
446             opt.decode = 0;
447           else if (!strcmp (cmd, "help"))
448             {
449               puts (
450 "Available commands:\n"
451 "/echo ARGS             Echo ARGS.\n"
452 "/definqfile NAME FILE\n"
453 "    Use content of FILE for inquiries with NAME.\n"
454 "    NAME may be \"*\" to match any inquiry.\n"
455 "/definqprog NAME PGM\n"
456 "    Run PGM for inquiries matching NAME and pass the\n"
457 "    entire line to it as arguments.\n"
458 "/showdef               Print all definitions.\n"
459 "/cleardef              Delete all definitions.\n"
460 "/sendfd FILE MODE      Open FILE and pass descriptor to server.\n"
461 "/recvfd                Receive FD from server and print. \n"
462 "/[no]hex               Enable hex dumping of received data lines.\n"
463 "/[no]decode            Enable decoding of received data lines.\n"
464 "/help                  Print this help.");
465             }
466           else
467             log_error (_("unknown command `%s'\n"), cmd );
468       
469           continue;
470         }
471       
472       rc = assuan_write_line (ctx, line);
473       if (rc)
474         {
475           log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
476           break;
477         }
478       if (*line == '#' || !*line)
479         continue; /* Don't expect a response for a comment line. */
480
481       rc = read_and_print_response (ctx);
482       if (rc)
483         log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
484
485       /* FIXME: If the last command was BYE or the server died for
486          some other reason, we won't notice until we get the next
487          input command.  Probing the connection with a non-blocking
488          read could help to notice termination or other problems
489          early.  */
490     }
491
492   if (opt.verbose)
493     log_info ("closing connection to agent\n");
494   
495   return 0; 
496 }
497
498
499 /* Handle an Inquire from the server.  Return False if it could not be
500    handled; in this case the caller shll complete the operation.  LINE
501    is the complete line as received from the server.  This function
502    may change the content of LINE. */
503 static int
504 handle_inquire (assuan_context_t ctx, char *line)
505 {
506   const char *name;
507   definq_t d;
508   FILE *fp;
509   char buffer[1024];
510   int rc, n;
511
512   /* Skip the command and trailing spaces. */
513   for (; *line && !spacep (line); line++)
514     ;
515   while (spacep (line))
516     line++;
517   /* Get the name. */
518   name = line;
519   for (; *line && !spacep (line); line++)
520     ;
521   if (*line)
522     *line++ = 0;
523
524   /* Now match it against our list. he second loop is todetect the
525      match all entry. **/
526   for (d=definq_list; d; d = d->next)
527     if (d->name && !strcmp (d->name, name))
528         break;
529   if (!d)
530     for (d=definq_list; d; d = d->next)
531       if (!d->name)
532         break;
533   if (!d)
534     {
535       if (opt.verbose)
536         log_info ("no handler for inquiry `%s' found\n", name);
537       return 0;
538     }
539
540   if (d->is_prog)
541     {
542       fp = popen (d->file, "r");
543       if (!fp)
544         log_error ("error executing `%s': %s\n", d->file, strerror (errno));
545       else if (opt.verbose)
546         log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
547     }
548   else
549     {
550       fp = fopen (d->file, "rb");
551       if (!fp)
552         log_error ("error opening `%s': %s\n", d->file, strerror (errno));
553       else if (opt.verbose)
554         log_error ("handling inquiry `%s' by returning content of `%s'\n",
555                    name, d->file);
556     }
557   if (!fp)
558     return 0;
559
560   while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
561     {
562       rc = assuan_send_data (ctx, buffer, n);
563       if (rc)
564         {
565           log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
566           break;
567         }
568     }
569   if (ferror (fp))
570     log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
571
572   rc = assuan_send_data (ctx, NULL, 0);
573   if (rc)
574     log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
575
576   if (d->is_prog)
577     {
578       if (pclose (fp))
579         log_error ("error running `%s': %s\n", d->file, strerror (errno));
580     }
581   else
582     fclose (fp);
583   return 1;
584 }
585
586
587 /* Read all response lines from server and print them.  Returns 0 on
588    success or an assuan error code. */
589 static int
590 read_and_print_response (assuan_context_t ctx)
591 {
592   char *line;
593   size_t linelen;
594   assuan_error_t rc;
595   int i, j;
596   int need_lf = 0;
597
598   for (;;)
599     {
600       do 
601         {
602           rc = assuan_read_line (ctx, &line, &linelen);
603           if (rc)
604             return rc;
605
606           if (opt.verbose > 1 && *line == '#')
607             {
608               fwrite (line, linelen, 1, stdout);
609               putchar ('\n');
610             }
611         }    
612       while (*line == '#' || !linelen);
613
614       if (linelen >= 1
615           && line[0] == 'D' && line[1] == ' ')
616         {
617           if (opt.hex)
618             {
619               for (i=2; i < linelen; )
620                 {
621                   int save_i = i;
622
623                   printf ("D[%04X] ", i-2);
624                   for (j=0; j < 16 ; j++, i++)
625                     {
626                       if (j == 8)
627                         putchar (' ');
628                       if (i < linelen)
629                         printf (" %02X", ((unsigned char*)line)[i]);
630                       else
631                         fputs ("   ", stdout);
632                     }
633                   fputs ("   ", stdout);
634                   i= save_i;
635                   for (j=0; j < 16; j++, i++)
636                     {
637                       unsigned int c = ((unsigned char*)line)[i];
638                       if ( i >= linelen )
639                         putchar (' ');
640                       else if (isascii (c) && isprint (c) && !iscntrl (c))
641                         putchar (c);
642                       else
643                         putchar ('.');
644                     }
645                   putchar ('\n');
646                 }
647             }
648           else if (opt.decode)
649             {
650               const unsigned char *s;
651               int need_d = 1;
652               int c = 0;
653
654               for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ )
655                 {
656                   if (need_d)
657                     {
658                       fputs ("D ", stdout);
659                       need_d = 0;
660                     }
661                   if (*s == '%' && j+2 < linelen)
662                     { 
663                       s++; j++;
664                       c = xtoi_2 ( s );
665                       s++; j++;
666                     }
667                   else
668                     c = *s;
669                   if (c == '\n')
670                     need_d = 1;
671                   putchar (c);
672                 }
673               need_lf = (c != '\n');
674             }
675           else
676             {
677               fwrite (line, linelen, 1, stdout);
678               putchar ('\n');
679             }
680         }
681       else 
682         {
683           if (need_lf)
684             {
685               putchar ('\n');
686               need_lf = 0;
687             }
688
689           if (linelen >= 1
690               && line[0] == 'S' 
691               && (line[1] == '\0' || line[1] == ' '))
692             {
693               fwrite (line, linelen, 1, stdout);
694               putchar ('\n');
695             }  
696           else if (linelen >= 2
697                    && line[0] == 'O' && line[1] == 'K'
698                    && (line[2] == '\0' || line[2] == ' '))
699             {
700               fwrite (line, linelen, 1, stdout);
701               putchar ('\n');
702               return 0;
703             }
704           else if (linelen >= 3
705                    && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
706                    && (line[3] == '\0' || line[3] == ' '))
707             {
708               fwrite (line, linelen, 1, stdout);
709               putchar ('\n');
710               return 0;
711             }  
712           else if (linelen >= 7
713                    && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
714                    && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
715                    && line[6] == 'E' 
716                    && (line[7] == '\0' || line[7] == ' '))
717             {
718               fwrite (line, linelen, 1, stdout);
719               putchar ('\n');
720               if (!handle_inquire (ctx, line))
721                 assuan_write_line (ctx, "CANCEL");
722             }
723           else if (linelen >= 3
724                    && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
725                    && (line[3] == '\0' || line[3] == ' '))
726             {
727               fwrite (line, linelen, 1, stdout);
728               putchar ('\n');
729               /* Received from server, thus more responses are expected.  */
730             }
731           else
732             return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
733         }
734     }
735 }
736
737
738
739
740 /* Connect to the agent and send the standard options.  */
741 static assuan_context_t
742 start_agent (void)
743 {
744   int rc = 0;
745   char *infostr, *p;
746   assuan_context_t ctx;
747
748   infostr = getenv ("GPG_AGENT_INFO");
749   if (!infostr || !*infostr)
750     {
751       char *sockname;
752
753       /* Check whether we can connect at the standard socket.  */
754       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
755       rc = assuan_socket_connect (&ctx, sockname, 0);
756       xfree (sockname);
757     }
758   else
759     {
760       int prot;
761       int pid;
762
763       infostr = xstrdup (infostr);
764       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
765         {
766           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
767           xfree (infostr);
768           exit (1);
769         }
770       *p++ = 0;
771       pid = atoi (p);
772       while (*p && *p != PATHSEP_C)
773         p++;
774       prot = *p? atoi (p+1) : 0;
775       if (prot != 1)
776         {
777           log_error (_("gpg-agent protocol version %d is not supported\n"),
778                      prot);
779           xfree (infostr);
780           exit (1);
781         }
782
783       rc = assuan_socket_connect (&ctx, infostr, pid);
784       xfree (infostr);
785     }
786
787   if (rc)
788     {
789       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
790       exit (1);
791     }
792
793   if (opt.verbose)
794     log_info ("connection to agent established\n");
795
796   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
797   if (rc)
798     {
799       log_error (_("error sending %s command: %s\n"), "RESET", 
800                  gpg_strerror (rc));
801       exit (1);
802     }
803
804   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
805                                   NULL, NULL, NULL, NULL, NULL);
806   if (rc)
807     {
808       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
809       exit (1);
810     }
811
812   return ctx;
813 }