2007-07-05 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / tools / gpg-connect-agent.c
bloba07a7b67d56792f098abdb136c565ec93572a5e7
1 /* gpg-connect-agent.c - Tool to connect to the agent.
2 * Copyright (C) 2005, 2007 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
20 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27 #include <assuan.h>
29 #include "i18n.h"
30 #include "../common/util.h"
31 #include "../common/asshelp.h"
35 /* Constants to identify the commands and options. */
36 enum cmd_and_opt_values
38 aNull = 0,
39 oQuiet = 'q',
40 oVerbose = 'v',
41 oRawSocket = 'S',
42 oExec = 'E',
44 oNoVerbose = 500,
45 oHomedir,
46 oHex,
47 oDecode,
48 oNoExtConnect
53 /* The list of commands and options. */
54 static ARGPARSE_OPTS opts[] =
56 { 301, NULL, 0, N_("@\nOptions:\n ") },
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")},
67 /* hidden options */
68 { oNoVerbose, "no-verbose", 0, "@"},
69 { oHomedir, "homedir", 2, "@" },
70 {0}
74 /* We keep all global options in the structure OPT. */
75 struct
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;
89 /* Definitions for /definq commands and a global linked list with all
90 the definitions. */
91 struct definq_s
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. */
98 typedef struct definq_s *definq_t;
100 static definq_t definq_list;
101 static definq_t *definq_list_tail = &definq_list;
105 /*-- local prototypes --*/
106 static int read_and_print_response (assuan_context_t ctx);
107 static assuan_context_t start_agent (void);
112 /* Print usage information and and provide strings for help. */
113 static const char *
114 my_strusage( int level )
116 const char *p;
118 switch (level)
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;
137 default: p = NULL; break;
139 return p;
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)
149 definq_t d;
150 char *name, *p;
152 /* Get name. */
153 name = line;
154 for (p=name; *p && !spacep (p); p++)
156 if (*p)
157 *p++ = 0;
158 while (spacep (p))
159 p++;
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);
169 d->next = NULL;
170 *definq_list_tail = d;
171 definq_list_tail = &d->next;
175 /* Show all inquiry defintions. */
176 static void
177 show_definq (void)
179 definq_t d;
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);
190 /* Clear all inquiry definitions. */
191 static void
192 clear_definq (void)
194 while (definq_list)
196 definq_t tmp = definq_list->next;
197 xfree (definq_list->name);
198 xfree (definq_list);
199 definq_list = tmp;
201 definq_list_tail = &definq_list;
205 static void
206 do_sendfd (assuan_context_t ctx, char *line)
208 FILE *fp;
209 char *name, *mode, *p;
210 int rc, fd;
212 /* Get file name. */
213 name = line;
214 for (p=name; *p && !spacep (p); p++)
216 if (*p)
217 *p++ = 0;
218 while (spacep (p))
219 p++;
221 /* Get mode. */
222 mode = p;
223 if (!*mode)
224 mode = "r";
225 else
227 for (p=mode; *p && !spacep (p); p++)
229 if (*p)
230 *p++ = 0;
233 /* Open and send. */
234 fp = fopen (name, mode);
235 if (!fp)
237 log_error ("can't open `%s' in \"%s\" mode: %s\n",
238 name, mode, strerror (errno));
239 return;
241 fd = fileno (fp);
243 if (opt.verbose)
244 log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
245 name, mode, fd);
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);
254 static void
255 do_recvfd (assuan_context_t ctx, char *line)
257 log_info ("This command has not yet been implemented\n");
262 /* gpg-connect-agent's entry point. */
264 main (int argc, char **argv)
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;
273 set_strusage (my_strusage);
274 log_set_prefix ("gpg-connect-agent", 1);
276 /* Make sure that our subsystems are ready. */
277 init_common_subsystems ();
279 assuan_set_assuan_err_source (0);
281 i18n_init();
283 opt.homedir = default_homedir ();
284 opt.connect_flags = 1; /* Use extended connect mode. */
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))
292 switch (pargs.r_opt)
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;
304 default: pargs.err = 2; break;
308 if (log_get_errorcount (0))
309 exit (2);
311 if (opt.exec)
313 if (!argc)
315 log_error (_("option \"%s\" requires a program "
316 "and optional arguments\n"), "--exec" );
317 exit (1);
320 else if (argc)
321 usage (1);
323 if (opt.exec && opt.raw_socket)
324 log_info (_("option \"%s\" ignored due to \"%s\"\n"),
325 "--raw-socket", "--exec");
327 if (opt.exec)
329 int no_close[3];
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)
339 log_error ("assuan_pipe_connect_ext failed: %s\n",
340 gpg_strerror (rc));
341 exit (1);
344 if (opt.verbose)
345 log_info ("server `%s' started\n", *argv);
348 else if (opt.raw_socket)
350 rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
351 opt.connect_flags);
352 if (rc)
354 log_error ("can't connect to socket `%s': %s\n",
355 opt.raw_socket, gpg_strerror (rc));
356 exit (1);
359 if (opt.verbose)
360 log_info ("connection to socket `%s' established\n", opt.raw_socket);
362 else
363 ctx = start_agent ();
364 line = NULL;
365 linesize = 0;
366 for (;;)
368 int n;
369 size_t maxlength;
371 maxlength = 2048;
372 n = read_line (stdin, &line, &linesize, &maxlength);
373 if (n < 0)
375 log_error (_("error reading input: %s\n"), strerror (errno));
376 exit (1);
378 if (!n)
379 break; /* EOF */
380 if (!maxlength)
382 log_error (_("line too long - skipped\n"));
383 continue;
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 == '/')
391 /* Handle control commands. */
392 char *cmd = line+1;
394 for (p=cmd; *p && !spacep (p); p++)
396 if (*p)
397 *p++ = 0;
398 while (spacep (p))
399 p++;
400 if (!strcmp (cmd, "definqfile"))
402 add_definq (p, 0);
404 else if (!strcmp (cmd, "definqprog"))
406 add_definq (p, 1);
408 else if (!strcmp (cmd, "showdef"))
410 show_definq ();
412 else if (!strcmp (cmd, "cleardef"))
414 clear_definq ();
416 else if (!strcmp (cmd, "echo"))
418 puts (p);
420 else if (!strcmp (cmd, "sendfd"))
422 do_sendfd (ctx, p);
423 continue;
425 else if (!strcmp (cmd, "recvfd"))
427 do_recvfd (ctx, p);
428 continue;
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"))
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.");
457 else
458 log_error (_("unknown command `%s'\n"), cmd );
460 continue;
463 rc = assuan_write_line (ctx, line);
464 if (rc)
466 log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
467 continue;
469 if (*line == '#' || !*line)
470 continue; /* Don't expect a response for a comment line. */
472 rc = read_and_print_response (ctx);
473 if (rc)
474 log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
477 if (opt.verbose)
478 log_info ("closing connection to agent\n");
480 return 0;
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)
491 const char *name;
492 definq_t d;
493 FILE *fp;
494 char buffer[1024];
495 int rc, n;
497 /* Skip the command and trailing spaces. */
498 for (; *line && !spacep (line); line++)
500 while (spacep (line))
501 line++;
502 /* Get the name. */
503 name = line;
504 for (; *line && !spacep (line); line++)
506 if (*line)
507 *line++ = 0;
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)
520 if (opt.verbose)
521 log_info ("no handler for inquiry `%s' found\n", name);
522 return 0;
525 if (d->is_prog)
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);
533 else
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);
542 if (!fp)
543 return 0;
545 while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
547 rc = assuan_send_data (ctx, buffer, n);
548 if (rc)
550 log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
551 break;
554 if (ferror (fp))
555 log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
557 rc = assuan_send_data (ctx, NULL, 0);
558 if (rc)
559 log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
561 if (d->is_prog)
563 if (pclose (fp))
564 log_error ("error running `%s': %s\n", d->file, strerror (errno));
566 else
567 fclose (fp);
568 return 1;
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)
577 char *line;
578 size_t linelen;
579 assuan_error_t rc;
580 int i, j;
581 int need_lf = 0;
583 for (;;)
587 rc = assuan_read_line (ctx, &line, &linelen);
588 if (rc)
589 return rc;
591 if (opt.verbose > 1 && *line == '#')
593 fwrite (line, linelen, 1, stdout);
594 putchar ('\n');
597 while (*line == '#' || !linelen);
599 if (linelen >= 1
600 && line[0] == 'D' && line[1] == ' ')
602 if (opt.hex)
604 for (i=2; i < linelen; )
606 int save_i = i;
608 printf ("D[%04X] ", i-2);
609 for (j=0; j < 16 ; j++, i++)
611 if (j == 8)
612 putchar (' ');
613 if (i < linelen)
614 printf (" %02X", ((unsigned char*)line)[i]);
615 else
616 fputs (" ", stdout);
618 fputs (" ", stdout);
619 i= save_i;
620 for (j=0; j < 16; j++, i++)
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 ('.');
630 putchar ('\n');
633 else if (opt.decode)
635 const unsigned char *s;
636 int need_d = 1;
637 int c = 0;
639 for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ )
641 if (need_d)
643 fputs ("D ", stdout);
644 need_d = 0;
646 if (*s == '%' && j+2 < linelen)
648 s++; j++;
649 c = xtoi_2 ( s );
650 s++; j++;
652 else
653 c = *s;
654 if (c == '\n')
655 need_d = 1;
656 putchar (c);
658 need_lf = (c != '\n');
660 else
662 fwrite (line, linelen, 1, stdout);
663 putchar ('\n');
666 else
668 if (need_lf)
670 putchar ('\n');
671 need_lf = 0;
674 if (linelen >= 1
675 && line[0] == 'S'
676 && (line[1] == '\0' || line[1] == ' '))
678 fwrite (line, linelen, 1, stdout);
679 putchar ('\n');
681 else if (linelen >= 2
682 && line[0] == 'O' && line[1] == 'K'
683 && (line[2] == '\0' || line[2] == ' '))
685 fwrite (line, linelen, 1, stdout);
686 putchar ('\n');
687 return 0;
689 else if (linelen >= 3
690 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
691 && (line[3] == '\0' || line[3] == ' '))
693 fwrite (line, linelen, 1, stdout);
694 putchar ('\n');
695 return 0;
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] == ' '))
703 fwrite (line, linelen, 1, stdout);
704 putchar ('\n');
705 if (!handle_inquire (ctx, line))
706 assuan_write_line (ctx, "CANCEL");
708 else if (linelen >= 3
709 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
710 && (line[3] == '\0' || line[3] == ' '))
712 fwrite (line, linelen, 1, stdout);
713 putchar ('\n');
714 /* Received from server, thus more responses are expected. */
716 else
717 return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
725 /* Connect to the agent and send the standard options. */
726 static assuan_context_t
727 start_agent (void)
729 int rc = 0;
730 char *infostr, *p;
731 assuan_context_t ctx;
733 infostr = getenv ("GPG_AGENT_INFO");
734 if (!infostr || !*infostr)
736 char *sockname;
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);
743 else
745 int prot;
746 int pid;
748 infostr = xstrdup (infostr);
749 if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
751 log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
752 xfree (infostr);
753 exit (1);
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)
762 log_error (_("gpg-agent protocol version %d is not supported\n"),
763 prot);
764 xfree (infostr);
765 exit (1);
768 rc = assuan_socket_connect (&ctx, infostr, pid);
769 xfree (infostr);
772 if (rc)
774 log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
775 exit (1);
778 if (opt.verbose)
779 log_info ("connection to agent established\n");
781 rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
782 if (rc)
784 log_error (_("error sending %s command: %s\n"), "RESET",
785 gpg_strerror (rc));
786 exit (1);
789 rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
790 NULL, NULL, NULL, NULL, NULL);
791 if (rc)
793 log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
794 exit (1);
797 return ctx;