Changed to GPLv3.
[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
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, 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
262 /* gpg-connect-agent's entry point. */
263 int
264 main (int argc, char **argv)
265 {
266   ARGPARSE_ARGS pargs;
267   int no_more_options = 0;
268   assuan_context_t ctx;
269   char *line, *p;
270   size_t linesize;
271   int rc;
272
273   set_strusage (my_strusage);
274   log_set_prefix ("gpg-connect-agent", 1);
275
276   /* Make sure that our subsystems are ready.  */
277   init_common_subsystems ();
278
279   assuan_set_assuan_err_source (0);
280
281   i18n_init();
282
283   opt.homedir = default_homedir ();
284   opt.connect_flags = 1; /* Use extended connect mode.  */
285
286   /* Parse the command line. */
287   pargs.argc  = &argc;
288   pargs.argv  = &argv;
289   pargs.flags =  1;  /* Do not remove the args.  */
290   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
291     {
292       switch (pargs.r_opt)
293         {
294         case oQuiet:     opt.quiet = 1; break;
295         case oVerbose:   opt.verbose++; break;
296         case oNoVerbose: opt.verbose = 0; break;
297         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
298         case oHex:       opt.hex = 1; break;
299         case oDecode:    opt.decode = 1; break;
300         case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
301         case oExec:      opt.exec = 1; break;
302         case oNoExtConnect: opt.connect_flags &= ~(1); break;
303
304         default: pargs.err = 2; break;
305         }
306     }
307
308   if (log_get_errorcount (0))
309     exit (2);
310
311   if (opt.exec)
312     {
313       if (!argc)
314         {
315           log_error (_("option \"%s\" requires a program "
316                        "and optional arguments\n"), "--exec" );
317           exit (1);
318         }
319     }
320   else if (argc)
321     usage (1);
322
323   if (opt.exec && opt.raw_socket)
324     log_info (_("option \"%s\" ignored due to \"%s\"\n"),
325               "--raw-socket", "--exec");
326
327   if (opt.exec)
328     {
329       int no_close[3];
330
331       no_close[0] = fileno (stderr);
332       no_close[1] = log_get_fd ();
333       no_close[2] = -1;
334       rc = assuan_pipe_connect_ext (&ctx, *argv, (const char **)argv,
335                                     no_close, NULL, NULL,
336                                     opt.connect_flags);
337       if (rc)
338         {
339           log_error ("assuan_pipe_connect_ext failed: %s\n",
340                      gpg_strerror (rc));
341           exit (1);
342         }
343
344       if (opt.verbose)
345         log_info ("server `%s' started\n", *argv);
346
347     }
348   else if (opt.raw_socket)
349     {
350       rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
351                                       opt.connect_flags);
352       if (rc)
353         {
354           log_error ("can't connect to socket `%s': %s\n",
355                      opt.raw_socket, gpg_strerror (rc));
356           exit (1);
357         }
358
359       if (opt.verbose)
360         log_info ("connection to socket `%s' established\n", opt.raw_socket);
361     }
362   else
363     ctx = start_agent ();
364   line = NULL;
365   linesize = 0;
366   for (;;)
367     {
368       int n;
369       size_t maxlength;
370
371       maxlength = 2048;
372       n = read_line (stdin, &line, &linesize, &maxlength);
373       if (n < 0)
374         {
375           log_error (_("error reading input: %s\n"), strerror (errno));
376           exit (1);
377         }
378       if (!n)
379         break; /* EOF */
380       if (!maxlength)
381         {
382           log_error (_("line too long - skipped\n"));
383           continue;
384         }
385       if (memchr (line, 0, n))
386         log_info (_("line shortened due to embedded Nul character\n"));
387       if (line[n-1] == '\n')
388         line[n-1] = 0;
389       if (*line == '/')
390         {
391           /* Handle control commands. */
392           char *cmd = line+1;
393
394           for (p=cmd; *p && !spacep (p); p++)
395             ;
396           if (*p)
397             *p++ = 0;
398           while (spacep (p))
399             p++;
400           if (!strcmp (cmd, "definqfile"))
401             {
402               add_definq (p, 0);
403             }
404           else if (!strcmp (cmd, "definqprog"))
405             {
406               add_definq (p, 1);
407             }
408           else if (!strcmp (cmd, "showdef"))
409             {
410               show_definq ();
411             }
412           else if (!strcmp (cmd, "cleardef"))
413             {
414               clear_definq ();
415             }
416           else if (!strcmp (cmd, "echo"))
417             {
418               puts (p);
419             }
420           else if (!strcmp (cmd, "sendfd"))
421             {
422               do_sendfd (ctx, p);
423               continue;
424             }
425           else if (!strcmp (cmd, "recvfd"))
426             {
427               do_recvfd (ctx, p);
428               continue;
429             }
430           else if (!strcmp (cmd, "hex"))
431             opt.hex = 1;
432           else if (!strcmp (cmd, "nohex"))
433             opt.hex = 0;
434           else if (!strcmp (cmd, "decode"))
435             opt.decode = 1;
436           else if (!strcmp (cmd, "nodecode"))
437             opt.decode = 0;
438           else if (!strcmp (cmd, "help"))
439             {
440               puts (
441 "Available commands:\n"
442 "/echo ARGS             Echo ARGS.\n"
443 "/definqfile NAME FILE\n"
444 "    Use content of FILE for inquiries with NAME.\n"
445 "    NAME may be \"*\" to match any inquiry.\n"
446 "/definqprog NAME PGM\n"
447 "    Run PGM for inquiries matching NAME and pass the\n"
448 "    entire line to it as arguments.\n"
449 "/showdef               Print all definitions.\n"
450 "/cleardef              Delete all definitions.\n"
451 "/sendfd FILE MODE      Open FILE and pass descriptor to server.\n"
452 "/recvfd                Receive FD from server and print. \n"
453 "/[no]hex               Enable hex dumping of received data lines.\n"
454 "/[no]decode            Enable decoding of received data lines.\n"
455 "/help                  Print this help.");
456             }
457           else
458             log_error (_("unknown command `%s'\n"), cmd );
459       
460           continue;
461         }
462       
463       rc = assuan_write_line (ctx, line);
464       if (rc)
465         {
466           log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
467           continue;
468         }
469       if (*line == '#' || !*line)
470         continue; /* Don't expect a response for a comment line. */
471
472       rc = read_and_print_response (ctx);
473       if (rc)
474         log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
475     }
476
477   if (opt.verbose)
478     log_info ("closing connection to agent\n");
479   
480   return 0; 
481 }
482
483
484 /* Handle an Inquire from the server.  Return False if it could not be
485    handled; in this case the caller shll complete the operation.  LINE
486    is the complete line as received from the server.  This function
487    may change the content of LINE. */
488 static int
489 handle_inquire (assuan_context_t ctx, char *line)
490 {
491   const char *name;
492   definq_t d;
493   FILE *fp;
494   char buffer[1024];
495   int rc, n;
496
497   /* Skip the command and trailing spaces. */
498   for (; *line && !spacep (line); line++)
499     ;
500   while (spacep (line))
501     line++;
502   /* Get the name. */
503   name = line;
504   for (; *line && !spacep (line); line++)
505     ;
506   if (*line)
507     *line++ = 0;
508
509   /* Now match it against our list. he second loop is todetect the
510      match all entry. **/
511   for (d=definq_list; d; d = d->next)
512     if (d->name && !strcmp (d->name, name))
513         break;
514   if (!d)
515     for (d=definq_list; d; d = d->next)
516       if (!d->name)
517         break;
518   if (!d)
519     {
520       if (opt.verbose)
521         log_info ("no handler for inquiry `%s' found\n", name);
522       return 0;
523     }
524
525   if (d->is_prog)
526     {
527       fp = popen (d->file, "r");
528       if (!fp)
529         log_error ("error executing `%s': %s\n", d->file, strerror (errno));
530       else if (opt.verbose)
531         log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
532     }
533   else
534     {
535       fp = fopen (d->file, "rb");
536       if (!fp)
537         log_error ("error opening `%s': %s\n", d->file, strerror (errno));
538       else if (opt.verbose)
539         log_error ("handling inquiry `%s' by returning content of `%s'\n",
540                    name, d->file);
541     }
542   if (!fp)
543     return 0;
544
545   while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
546     {
547       rc = assuan_send_data (ctx, buffer, n);
548       if (rc)
549         {
550           log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
551           break;
552         }
553     }
554   if (ferror (fp))
555     log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
556
557   rc = assuan_send_data (ctx, NULL, 0);
558   if (rc)
559     log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
560
561   if (d->is_prog)
562     {
563       if (pclose (fp))
564         log_error ("error running `%s': %s\n", d->file, strerror (errno));
565     }
566   else
567     fclose (fp);
568   return 1;
569 }
570
571
572 /* Read all response lines from server and print them.  Returns 0 on
573    success or an assuan error code. */
574 static int
575 read_and_print_response (assuan_context_t ctx)
576 {
577   char *line;
578   size_t linelen;
579   assuan_error_t rc;
580   int i, j;
581   int need_lf = 0;
582
583   for (;;)
584     {
585       do 
586         {
587           rc = assuan_read_line (ctx, &line, &linelen);
588           if (rc)
589             return rc;
590
591           if (opt.verbose > 1 && *line == '#')
592             {
593               fwrite (line, linelen, 1, stdout);
594               putchar ('\n');
595             }
596         }    
597       while (*line == '#' || !linelen);
598
599       if (linelen >= 1
600           && line[0] == 'D' && line[1] == ' ')
601         {
602           if (opt.hex)
603             {
604               for (i=2; i < linelen; )
605                 {
606                   int save_i = i;
607
608                   printf ("D[%04X] ", i-2);
609                   for (j=0; j < 16 ; j++, i++)
610                     {
611                       if (j == 8)
612                         putchar (' ');
613                       if (i < linelen)
614                         printf (" %02X", ((unsigned char*)line)[i]);
615                       else
616                         fputs ("   ", stdout);
617                     }
618                   fputs ("   ", stdout);
619                   i= save_i;
620                   for (j=0; j < 16; j++, i++)
621                     {
622                       unsigned int c = ((unsigned char*)line)[i];
623                       if ( i >= linelen )
624                         putchar (' ');
625                       else if (isascii (c) && isprint (c) && !iscntrl (c))
626                         putchar (c);
627                       else
628                         putchar ('.');
629                     }
630                   putchar ('\n');
631                 }
632             }
633           else if (opt.decode)
634             {
635               const unsigned char *s;
636               int need_d = 1;
637               int c = 0;
638
639               for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ )
640                 {
641                   if (need_d)
642                     {
643                       fputs ("D ", stdout);
644                       need_d = 0;
645                     }
646                   if (*s == '%' && j+2 < linelen)
647                     { 
648                       s++; j++;
649                       c = xtoi_2 ( s );
650                       s++; j++;
651                     }
652                   else
653                     c = *s;
654                   if (c == '\n')
655                     need_d = 1;
656                   putchar (c);
657                 }
658               need_lf = (c != '\n');
659             }
660           else
661             {
662               fwrite (line, linelen, 1, stdout);
663               putchar ('\n');
664             }
665         }
666       else 
667         {
668           if (need_lf)
669             {
670               putchar ('\n');
671               need_lf = 0;
672             }
673
674           if (linelen >= 1
675               && line[0] == 'S' 
676               && (line[1] == '\0' || line[1] == ' '))
677             {
678               fwrite (line, linelen, 1, stdout);
679               putchar ('\n');
680             }  
681           else if (linelen >= 2
682                    && line[0] == 'O' && line[1] == 'K'
683                    && (line[2] == '\0' || line[2] == ' '))
684             {
685               fwrite (line, linelen, 1, stdout);
686               putchar ('\n');
687               return 0;
688             }
689           else if (linelen >= 3
690                    && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
691                    && (line[3] == '\0' || line[3] == ' '))
692             {
693               fwrite (line, linelen, 1, stdout);
694               putchar ('\n');
695               return 0;
696             }  
697           else if (linelen >= 7
698                    && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
699                    && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
700                    && line[6] == 'E' 
701                    && (line[7] == '\0' || line[7] == ' '))
702             {
703               fwrite (line, linelen, 1, stdout);
704               putchar ('\n');
705               if (!handle_inquire (ctx, line))
706                 assuan_write_line (ctx, "CANCEL");
707             }
708           else if (linelen >= 3
709                    && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
710                    && (line[3] == '\0' || line[3] == ' '))
711             {
712               fwrite (line, linelen, 1, stdout);
713               putchar ('\n');
714               /* Received from server, thus more responses are expected.  */
715             }
716           else
717             return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
718         }
719     }
720 }
721
722
723
724
725 /* Connect to the agent and send the standard options.  */
726 static assuan_context_t
727 start_agent (void)
728 {
729   int rc = 0;
730   char *infostr, *p;
731   assuan_context_t ctx;
732
733   infostr = getenv ("GPG_AGENT_INFO");
734   if (!infostr || !*infostr)
735     {
736       char *sockname;
737
738       /* Check whether we can connect at the standard socket.  */
739       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
740       rc = assuan_socket_connect (&ctx, sockname, 0);
741       xfree (sockname);
742     }
743   else
744     {
745       int prot;
746       int pid;
747
748       infostr = xstrdup (infostr);
749       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
750         {
751           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
752           xfree (infostr);
753           exit (1);
754         }
755       *p++ = 0;
756       pid = atoi (p);
757       while (*p && *p != PATHSEP_C)
758         p++;
759       prot = *p? atoi (p+1) : 0;
760       if (prot != 1)
761         {
762           log_error (_("gpg-agent protocol version %d is not supported\n"),
763                      prot);
764           xfree (infostr);
765           exit (1);
766         }
767
768       rc = assuan_socket_connect (&ctx, infostr, pid);
769       xfree (infostr);
770     }
771
772   if (rc)
773     {
774       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
775       exit (1);
776     }
777
778   if (opt.verbose)
779     log_info ("connection to agent established\n");
780
781   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
782   if (rc)
783     {
784       log_error (_("error sending %s command: %s\n"), "RESET", 
785                  gpg_strerror (rc));
786       exit (1);
787     }
788
789   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
790                                   NULL, NULL, NULL, NULL, NULL);
791   if (rc)
792     {
793       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
794       exit (1);
795     }
796
797   return ctx;
798 }