31fed9504ce4beca0b8661c73deaf42ec09fb609
[gnupg.git] / tools / gpg-connect-agent.c
1 /* gpg-connect-agent.c - Tool to connect to the agent.
2  *      Copyright (C) 2005 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 2 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, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <assuan.h>
30
31 #include "i18n.h"
32 #include "../common/util.h"
33 #include "../common/asshelp.h"
34
35
36
37 /* Constants to identify the commands and options. */
38 enum cmd_and_opt_values
39   {
40     aNull = 0,
41     oQuiet      = 'q',
42     oVerbose    = 'v',
43     oRawSocket  = 'S',
44     oExec       = 'E',
45
46     oNoVerbose  = 500,
47     oHomedir,
48     oHex,
49     oNoExtConnect
50
51   };
52
53
54 /* The list of commands and options. */
55 static ARGPARSE_OPTS opts[] =
56   {
57     { 301, NULL, 0, N_("@\nOptions:\n ") },
58     
59     { oVerbose, "verbose",  0, N_("verbose") },
60     { oQuiet, "quiet",      0, N_("quiet") },
61     { oHex,   "hex",        0, N_("print data out hex encoded") },
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   const char *raw_socket; /* Name of socket to connect in raw mode. */
82   int exec;             /* Run the pgm given on the command line. */
83   unsigned int connect_flags;    /* Flags used for connecting. */
84 } opt;
85
86
87
88 /* Definitions for /definq commands and a global linked list with all
89    the definitions. */
90 struct definq_s
91 {
92   struct definq_s *next;
93   char *name;     /* Name of inquiry or NULL for any name. */
94   int is_prog;     /* True if this is a program to run. */
95   char file[1];   /* Name of file or program. */
96 };
97 typedef struct definq_s *definq_t;
98
99 static definq_t definq_list;
100 static definq_t *definq_list_tail = &definq_list;
101
102
103
104 /*-- local prototypes --*/
105 static int read_and_print_response (assuan_context_t ctx);
106 static assuan_context_t start_agent (void);
107
108
109
110 \f
111 /* Print usage information and and provide strings for help. */
112 static const char *
113 my_strusage( int level )
114 {
115   const char *p;
116
117   switch (level)
118     {
119     case 11: p = "gpg-connect-agent (GnuPG)";
120       break;
121     case 13: p = VERSION; break;
122     case 17: p = PRINTABLE_OS_NAME; break;
123     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
124       break;
125     case 1:
126     case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
127       break;
128     case 41:
129       p = _("Syntax: gpg-connect-agent [options]\n"
130             "Connect to a running agent and send commands\n");
131       break;
132     case 31: p = "\nHome: "; break;
133     case 32: p = opt.homedir; break;
134     case 33: p = "\n"; break;
135
136     default: p = NULL; break;
137     }
138   return p;
139 }
140
141
142 /* Initialize the gettext system. */
143 static void
144 i18n_init(void)
145 {
146 #ifdef USE_SIMPLE_GETTEXT
147   set_gettext_file (PACKAGE_GT);
148 #else
149 # ifdef ENABLE_NLS
150   setlocale (LC_ALL, "" );
151   bindtextdomain (PACKAGE_GT, LOCALEDIR);
152   textdomain (PACKAGE_GT);
153 # endif
154 #endif
155 }
156
157 /* Store an inquire response pattern.  Note, that this function may
158    change the content of LINE.  We assume that leading white spaces
159    are already removed. */
160 static void
161 add_definq (char *line, int is_prog)
162 {
163   definq_t d;
164   char *name, *p;
165
166   /* Get name. */
167   name = line;
168   for (p=name; *p && !spacep (p); p++)
169     ;
170   if (*p)
171     *p++ = 0;
172   while (spacep (p))
173     p++;
174
175   d = xmalloc (sizeof *d + strlen (p) );
176   strcpy (d->file, p);
177   d->is_prog = is_prog;
178   if ( !strcmp (name, "*"))
179     d->name = NULL;
180   else
181     d->name = xstrdup (name);
182
183   d->next = NULL;
184   *definq_list_tail = d;
185   definq_list_tail = &d->next;
186 }
187
188
189 /* Show all inquiry defintions. */
190 static void
191 show_definq (void)
192 {
193   definq_t d;
194
195   for (d=definq_list; d; d = d->next)
196     if (d->name)
197       printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
198   for (d=definq_list; d; d = d->next)
199     if (!d->name)
200       printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
201 }
202
203
204 /* Clear all inquiry definitions. */
205 static void
206 clear_definq (void)
207 {
208   while (definq_list)
209     { 
210       definq_t tmp = definq_list->next;
211       xfree (definq_list->name);
212       xfree (definq_list);
213       definq_list = tmp;
214     }
215   definq_list_tail = &definq_list;
216 }      
217
218
219 static void
220 do_sendfd (assuan_context_t ctx, char *line)
221 {
222   FILE *fp;
223   char *name, *mode, *p;
224   int rc, fd;
225
226   /* Get file name. */
227   name = line;
228   for (p=name; *p && !spacep (p); p++)
229     ;
230   if (*p)
231     *p++ = 0;
232   while (spacep (p))
233     p++;
234
235   /* Get mode.  */
236   mode = p;
237   if (!*mode)
238     mode = "r";
239   else
240     {
241       for (p=mode; *p && !spacep (p); p++)
242         ;
243       if (*p)
244         *p++ = 0;
245     }
246
247   /* Open and send. */
248   fp = fopen (name, mode);
249   if (!fp)
250     {
251       log_error ("can't open `%s' in \"%s\" mode: %s\n",
252                  name, mode, strerror (errno));
253       return;
254     }
255   fd = fileno (fp);
256
257   if (opt.verbose)
258     log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
259                name, mode, fd);
260
261   rc = assuan_sendfd (ctx, fd);
262   if (rc)
263     log_error ("sednig  descriptor %d failed: %s\n", fd, gpg_strerror (rc));
264   fclose (fp);
265 }
266
267
268 static void
269 do_recvfd (assuan_context_t ctx, char *line)
270 {
271   log_info ("This command has not yet been implemented\n");
272 }
273
274
275
276 /* gpg-connect-agent's entry point. */
277 int
278 main (int argc, char **argv)
279 {
280   ARGPARSE_ARGS pargs;
281   int no_more_options = 0;
282   assuan_context_t ctx;
283   char *line, *p;
284   size_t linesize;
285   int rc;
286
287   set_strusage (my_strusage);
288   log_set_prefix ("gpg-connect-agent", 1);
289   assuan_set_assuan_err_source (0);
290
291   i18n_init();
292
293   opt.homedir = default_homedir ();
294   opt.connect_flags = 1; /* Use extended connect mode.  */
295
296   /* Parse the command line. */
297   pargs.argc  = &argc;
298   pargs.argv  = &argv;
299   pargs.flags =  1;  /* Do not remove the args.  */
300   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
301     {
302       switch (pargs.r_opt)
303         {
304         case oQuiet:     opt.quiet = 1; break;
305         case oVerbose:   opt.verbose++; break;
306         case oNoVerbose: opt.verbose = 0; break;
307         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
308         case oHex:       opt.hex = 1; break;
309         case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
310         case oExec:      opt.exec = 1; break;
311         case oNoExtConnect: opt.connect_flags &= ~(1); break;
312
313         default: pargs.err = 2; break;
314         }
315     }
316
317   if (log_get_errorcount (0))
318     exit (2);
319
320   if (opt.exec)
321     {
322       if (!argc)
323         {
324           log_error (_("option \"%s\" requires a program "
325                        "and optional arguments\n"), "--exec" );
326           exit (1);
327         }
328     }
329   else if (argc)
330     usage (1);
331
332   if (opt.exec && opt.raw_socket)
333     log_info (_("option \"%s\" ignored due to \"%s\"\n"),
334               "--raw-socket", "--exec");
335
336   if (opt.exec)
337     {
338       int no_close[3];
339
340       no_close[0] = fileno (stderr);
341       no_close[1] = log_get_fd ();
342       no_close[2] = -1;
343       rc = assuan_pipe_connect_ext (&ctx, *argv, (const char **)argv,
344                                     no_close, NULL, NULL,
345                                     opt.connect_flags);
346       if (rc)
347         {
348           log_error ("assuan_pipe_connect_ext failed: %s\n",
349                      gpg_strerror (rc));
350           exit (1);
351         }
352
353       if (opt.verbose)
354         log_info ("server `%s' started\n", *argv);
355
356     }
357   else if (opt.raw_socket)
358     {
359       rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
360                                       opt.connect_flags);
361       if (rc)
362         {
363           log_error ("can't connect to socket `%s': %s\n",
364                      opt.raw_socket, gpg_strerror (rc));
365           exit (1);
366         }
367
368       if (opt.verbose)
369         log_info ("connection to socket `%s' established\n", opt.raw_socket);
370     }
371   else
372     ctx = start_agent ();
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, "help"))
440             {
441               puts (
442 "Available commands:\n"
443 "/echo ARGS             Echo ARGS.\n"
444 "/definqfile NAME FILE\n"
445 "    Use content of FILE for inquiries with NAME.\n"
446 "    NAME may be \"*\" to match any inquiry.\n"
447 "/definqprog NAME PGM\n"
448 "    Run PGM for inquiries matching NAME and pass the\n"
449 "    entire line to it as arguments.\n"
450 "/showdef               Print all definitions.\n"
451 "/cleardef              Delete all definitions.\n"
452 "/sendfd FILE MODE      Open FILE and pass descripor to server.\n"
453 "/recvfd                Receive FD from server and print. \n"
454                     "/help                  Print this help.");
455             }
456           else
457             log_error (_("unknown command `%s'\n"), cmd );
458       
459           continue;
460         }
461       
462       rc = assuan_write_line (ctx, line);
463       if (rc)
464         {
465           log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
466           continue;
467         }
468       if (*line == '#' || !*line)
469         continue; /* Don't expect a response for a comment line. */
470
471       rc = read_and_print_response (ctx);
472       if (rc)
473         log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
474     }
475
476   if (opt.verbose)
477     log_info ("closing connection to agent\n");
478   
479   return 0; 
480 }
481
482
483 /* Handle an Inquire from the server.  Return False if it could not be
484    handled; in this case the caller shll complete the operation.  LINE
485    is the complete line as received from the server.  This function
486    may change the content of LINE. */
487 static int
488 handle_inquire (assuan_context_t ctx, char *line)
489 {
490   const char *name;
491   definq_t d;
492   FILE *fp;
493   char buffer[1024];
494   int rc, n;
495
496   /* Skip the command and trailing spaces. */
497   for (; *line && !spacep (line); line++)
498     ;
499   while (spacep (line))
500     line++;
501   /* Get the name. */
502   name = line;
503   for (; *line && !spacep (line); line++)
504     ;
505   if (*line)
506     *line++ = 0;
507
508   /* Now match it against our list. he second loop is todetect the
509      match all entry. **/
510   for (d=definq_list; d; d = d->next)
511     if (d->name && !strcmp (d->name, name))
512         break;
513   if (!d)
514     for (d=definq_list; d; d = d->next)
515       if (!d->name)
516         break;
517   if (!d)
518     {
519       if (opt.verbose)
520         log_info ("no handler for inquiry `%s' found\n", name);
521       return 0;
522     }
523
524   if (d->is_prog)
525     {
526       fp = popen (d->file, "r");
527       if (!fp)
528         log_error ("error executing `%s': %s\n", d->file, strerror (errno));
529       else if (opt.verbose)
530         log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
531     }
532   else
533     {
534       fp = fopen (d->file, "rb");
535       if (!fp)
536         log_error ("error opening `%s': %s\n", d->file, strerror (errno));
537       else if (opt.verbose)
538         log_error ("handling inquiry `%s' by returning content of `%s'\n",
539                    name, d->file);
540     }
541   if (!fp)
542     return 0;
543
544   while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
545     {
546       rc = assuan_send_data (ctx, buffer, n);
547       if (rc)
548         {
549           log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
550           break;
551         }
552     }
553   if (ferror (fp))
554     log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
555
556   rc = assuan_send_data (ctx, NULL, 0);
557   if (rc)
558     log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
559
560   if (d->is_prog)
561     {
562       if (pclose (fp))
563         log_error ("error running `%s': %s\n", d->file, strerror (errno));
564     }
565   else
566     fclose (fp);
567   return 1;
568 }
569
570
571 /* Read all response lines from server and print them.  Returns 0 on
572    success or an assuan error code. */
573 static int
574 read_and_print_response (assuan_context_t ctx)
575 {
576   char *line;
577   size_t linelen;
578   assuan_error_t rc;
579   int i, j;
580
581   for (;;)
582     {
583       do 
584         {
585           rc = assuan_read_line (ctx, &line, &linelen);
586           if (rc)
587             return rc;
588
589           if (opt.verbose > 1 && *line == '#')
590             {
591               fwrite (line, linelen, 1, stdout);
592               putchar ('\n');
593             }
594         }    
595       while (*line == '#' || !linelen);
596
597       if (linelen >= 1
598           && line[0] == 'D' && line[1] == ' ')
599         {
600           if (opt.hex)
601             {
602               for (i=2; i < linelen; )
603                 {
604                   int save_i = i;
605
606                   printf ("D[%04X] ", i-2);
607                   for (j=0; j < 16 ; j++, i++)
608                     {
609                       if (j == 8)
610                         putchar (' ');
611                       if (i < linelen)
612                         printf (" %02X", ((unsigned char*)line)[i]);
613                       else
614                         fputs ("   ", stdout);
615                     }
616                   fputs ("   ", stdout);
617                   i= save_i;
618                   for (j=0; j < 16; j++, i++)
619                     {
620                       unsigned int c = ((unsigned char*)line)[i];
621                       if ( i >= linelen )
622                         putchar (' ');
623                       else if (isascii (c) && isprint (c) && !iscntrl (c))
624                         putchar (c);
625                       else
626                         putchar ('.');
627                     }
628                   putchar ('\n');
629                 }
630             }
631           else
632             {
633               fwrite (line, linelen, 1, stdout);
634               putchar ('\n');
635             }
636         }
637       else if (linelen >= 1
638                && line[0] == 'S' 
639                && (line[1] == '\0' || line[1] == ' '))
640         {
641           fwrite (line, linelen, 1, stdout);
642           putchar ('\n');
643         }  
644       else if (linelen >= 2
645                && line[0] == 'O' && line[1] == 'K'
646                && (line[2] == '\0' || line[2] == ' '))
647         {
648           fwrite (line, linelen, 1, stdout);
649           putchar ('\n');
650           return 0;
651         }
652       else if (linelen >= 3
653                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
654                && (line[3] == '\0' || line[3] == ' '))
655         {
656           fwrite (line, linelen, 1, stdout);
657           putchar ('\n');
658           return 0;
659         }  
660       else if (linelen >= 7
661                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
662                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
663                && line[6] == 'E' 
664                && (line[7] == '\0' || line[7] == ' '))
665         {
666           fwrite (line, linelen, 1, stdout);
667           putchar ('\n');
668           if (!handle_inquire (ctx, line))
669             assuan_write_line (ctx, "CANCEL");
670         }
671       else if (linelen >= 3
672                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
673                && (line[3] == '\0' || line[3] == ' '))
674         {
675           fwrite (line, linelen, 1, stdout);
676           putchar ('\n');
677           /* Received from server, thus more responses are expected.  */
678         }
679       else
680         return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
681     }
682 }
683
684
685
686
687 /* Connect to the agent and send the standard options.  */
688 static assuan_context_t
689 start_agent (void)
690 {
691   int rc = 0;
692   char *infostr, *p;
693   assuan_context_t ctx;
694
695   infostr = getenv ("GPG_AGENT_INFO");
696   if (!infostr || !*infostr)
697     {
698       char *sockname;
699
700       /* Check whether we can connect at the standard socket.  */
701       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
702       rc = assuan_socket_connect (&ctx, sockname, 0);
703       xfree (sockname);
704     }
705   else
706     {
707       int prot;
708       int pid;
709
710       infostr = xstrdup (infostr);
711       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
712         {
713           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
714           xfree (infostr);
715           exit (1);
716         }
717       *p++ = 0;
718       pid = atoi (p);
719       while (*p && *p != PATHSEP_C)
720         p++;
721       prot = *p? atoi (p+1) : 0;
722       if (prot != 1)
723         {
724           log_error (_("gpg-agent protocol version %d is not supported\n"),
725                      prot);
726           xfree (infostr);
727           exit (1);
728         }
729
730       rc = assuan_socket_connect (&ctx, infostr, pid);
731       xfree (infostr);
732     }
733
734   if (rc)
735     {
736       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
737       exit (1);
738     }
739
740   if (opt.verbose)
741     log_info ("connection to agent established\n");
742
743   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
744   if (rc)
745     {
746       log_error (_("error sending %s command: %s\n"), "RESET", 
747                  gpg_strerror (rc));
748       exit (1);
749     }
750
751   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
752                                   NULL, NULL, NULL, NULL, NULL);
753   if (rc)
754     {
755       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
756       exit (1);
757     }
758
759   return ctx;
760 }