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 2 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, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
32 #include "../common/util.h"
33 #include "../common/asshelp.h"
37 /* Constants to identify the commands and options. */
38 enum cmd_and_opt_values
55 /* The list of commands and options. */
56 static ARGPARSE_OPTS opts
[] =
58 { 301, NULL
, 0, N_("@\nOptions:\n ") },
60 { oVerbose
, "verbose", 0, N_("verbose") },
61 { oQuiet
, "quiet", 0, N_("quiet") },
62 { oHex
, "hex", 0, N_("print data out hex encoded") },
63 { oDecode
,"decode", 0, N_("decode received data lines") },
64 { oRawSocket
, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")},
65 { oExec
, "exec", 0, N_("run the Assuan server given on the command line")},
66 { oNoExtConnect
, "no-ext-connect",
67 0, N_("do not use extended connect mode")},
70 { oNoVerbose
, "no-verbose", 0, "@"},
71 { oHomedir
, "homedir", 2, "@" },
76 /* We keep all global options in the structure OPT. */
79 int verbose
; /* Verbosity level. */
80 int quiet
; /* Be extra quiet. */
81 const char *homedir
; /* Configuration directory name */
82 int hex
; /* Print data lines in hex format. */
83 int decode
; /* Decode received data lines. */
84 const char *raw_socket
; /* Name of socket to connect in raw mode. */
85 int exec
; /* Run the pgm given on the command line. */
86 unsigned int connect_flags
; /* Flags used for connecting. */
91 /* Definitions for /definq commands and a global linked list with all
95 struct definq_s
*next
;
96 char *name
; /* Name of inquiry or NULL for any name. */
97 int is_prog
; /* True if this is a program to run. */
98 char file
[1]; /* Name of file or program. */
100 typedef struct definq_s
*definq_t
;
102 static definq_t definq_list
;
103 static definq_t
*definq_list_tail
= &definq_list
;
107 /*-- local prototypes --*/
108 static int read_and_print_response (assuan_context_t ctx
);
109 static assuan_context_t
start_agent (void);
114 /* Print usage information and and provide strings for help. */
116 my_strusage( int level
)
122 case 11: p
= "gpg-connect-agent (GnuPG)";
124 case 13: p
= VERSION
; break;
125 case 17: p
= PRINTABLE_OS_NAME
; break;
126 case 19: p
= _("Please report bugs to <" PACKAGE_BUGREPORT
">.\n");
129 case 40: p
= _("Usage: gpg-connect-agent [options] (-h for help)");
132 p
= _("Syntax: gpg-connect-agent [options]\n"
133 "Connect to a running agent and send commands\n");
135 case 31: p
= "\nHome: "; break;
136 case 32: p
= opt
.homedir
; break;
137 case 33: p
= "\n"; break;
139 default: p
= NULL
; break;
145 /* Initialize the gettext system. */
149 #ifdef USE_SIMPLE_GETTEXT
150 set_gettext_file (PACKAGE_GT
);
153 setlocale (LC_ALL
, "" );
154 bindtextdomain (PACKAGE_GT
, LOCALEDIR
);
155 textdomain (PACKAGE_GT
);
160 /* Store an inquire response pattern. Note, that this function may
161 change the content of LINE. We assume that leading white spaces
162 are already removed. */
164 add_definq (char *line
, int is_prog
)
171 for (p
=name
; *p
&& !spacep (p
); p
++)
178 d
= xmalloc (sizeof *d
+ strlen (p
) );
180 d
->is_prog
= is_prog
;
181 if ( !strcmp (name
, "*"))
184 d
->name
= xstrdup (name
);
187 *definq_list_tail
= d
;
188 definq_list_tail
= &d
->next
;
192 /* Show all inquiry defintions. */
198 for (d
=definq_list
; d
; d
= d
->next
)
200 printf ("%-20s %c %s\n", d
->name
, d
->is_prog
? 'p':'f', d
->file
);
201 for (d
=definq_list
; d
; d
= d
->next
)
203 printf ("%-20s %c %s\n", "*", d
->is_prog
? 'p':'f', d
->file
);
207 /* Clear all inquiry definitions. */
213 definq_t tmp
= definq_list
->next
;
214 xfree (definq_list
->name
);
218 definq_list_tail
= &definq_list
;
223 do_sendfd (assuan_context_t ctx
, char *line
)
226 char *name
, *mode
, *p
;
231 for (p
=name
; *p
&& !spacep (p
); p
++)
244 for (p
=mode
; *p
&& !spacep (p
); p
++)
251 fp
= fopen (name
, mode
);
254 log_error ("can't open `%s' in \"%s\" mode: %s\n",
255 name
, mode
, strerror (errno
));
261 log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
264 rc
= assuan_sendfd (ctx
, fd
);
266 log_error ("sednig descriptor %d failed: %s\n", fd
, gpg_strerror (rc
));
272 do_recvfd (assuan_context_t ctx
, char *line
)
274 log_info ("This command has not yet been implemented\n");
279 /* gpg-connect-agent's entry point. */
281 main (int argc
, char **argv
)
284 int no_more_options
= 0;
285 assuan_context_t ctx
;
290 set_strusage (my_strusage
);
291 log_set_prefix ("gpg-connect-agent", 1);
292 assuan_set_assuan_err_source (0);
296 opt
.homedir
= default_homedir ();
297 opt
.connect_flags
= 1; /* Use extended connect mode. */
299 /* Parse the command line. */
302 pargs
.flags
= 1; /* Do not remove the args. */
303 while (!no_more_options
&& optfile_parse (NULL
, NULL
, NULL
, &pargs
, opts
))
307 case oQuiet
: opt
.quiet
= 1; break;
308 case oVerbose
: opt
.verbose
++; break;
309 case oNoVerbose
: opt
.verbose
= 0; break;
310 case oHomedir
: opt
.homedir
= pargs
.r
.ret_str
; break;
311 case oHex
: opt
.hex
= 1; break;
312 case oDecode
: opt
.decode
= 1; break;
313 case oRawSocket
: opt
.raw_socket
= pargs
.r
.ret_str
; break;
314 case oExec
: opt
.exec
= 1; break;
315 case oNoExtConnect
: opt
.connect_flags
&= ~(1); break;
317 default: pargs
.err
= 2; break;
321 if (log_get_errorcount (0))
328 log_error (_("option \"%s\" requires a program "
329 "and optional arguments\n"), "--exec" );
336 if (opt
.exec
&& opt
.raw_socket
)
337 log_info (_("option \"%s\" ignored due to \"%s\"\n"),
338 "--raw-socket", "--exec");
344 no_close
[0] = fileno (stderr
);
345 no_close
[1] = log_get_fd ();
347 rc
= assuan_pipe_connect_ext (&ctx
, *argv
, (const char **)argv
,
348 no_close
, NULL
, NULL
,
352 log_error ("assuan_pipe_connect_ext failed: %s\n",
358 log_info ("server `%s' started\n", *argv
);
361 else if (opt
.raw_socket
)
363 rc
= assuan_socket_connect_ext (&ctx
, opt
.raw_socket
, 0,
367 log_error ("can't connect to socket `%s': %s\n",
368 opt
.raw_socket
, gpg_strerror (rc
));
373 log_info ("connection to socket `%s' established\n", opt
.raw_socket
);
376 ctx
= start_agent ();
385 n
= read_line (stdin
, &line
, &linesize
, &maxlength
);
388 log_error (_("error reading input: %s\n"), strerror (errno
));
395 log_error (_("line too long - skipped\n"));
398 if (memchr (line
, 0, n
))
399 log_info (_("line shortened due to embedded Nul character\n"));
400 if (line
[n
-1] == '\n')
404 /* Handle control commands. */
407 for (p
=cmd
; *p
&& !spacep (p
); p
++)
413 if (!strcmp (cmd
, "definqfile"))
417 else if (!strcmp (cmd
, "definqprog"))
421 else if (!strcmp (cmd
, "showdef"))
425 else if (!strcmp (cmd
, "cleardef"))
429 else if (!strcmp (cmd
, "echo"))
433 else if (!strcmp (cmd
, "sendfd"))
438 else if (!strcmp (cmd
, "recvfd"))
443 else if (!strcmp (cmd
, "hex"))
445 else if (!strcmp (cmd
, "nohex"))
447 else if (!strcmp (cmd
, "decode"))
449 else if (!strcmp (cmd
, "nodecode"))
451 else if (!strcmp (cmd
, "help"))
454 "Available commands:\n"
455 "/echo ARGS Echo ARGS.\n"
456 "/definqfile NAME FILE\n"
457 " Use content of FILE for inquiries with NAME.\n"
458 " NAME may be \"*\" to match any inquiry.\n"
459 "/definqprog NAME PGM\n"
460 " Run PGM for inquiries matching NAME and pass the\n"
461 " entire line to it as arguments.\n"
462 "/showdef Print all definitions.\n"
463 "/cleardef Delete all definitions.\n"
464 "/sendfd FILE MODE Open FILE and pass descriptor to server.\n"
465 "/recvfd Receive FD from server and print. \n"
466 "/[no]hex Enable hex dumping of received data lines.\n"
467 "/[no]decode Enable decoding of received data lines.\n"
468 "/help Print this help.");
471 log_error (_("unknown command `%s'\n"), cmd
);
476 rc
= assuan_write_line (ctx
, line
);
479 log_info (_("sending line failed: %s\n"), gpg_strerror (rc
) );
482 if (*line
== '#' || !*line
)
483 continue; /* Don't expect a response for a comment line. */
485 rc
= read_and_print_response (ctx
);
487 log_info (_("receiving line failed: %s\n"), gpg_strerror (rc
) );
491 log_info ("closing connection to agent\n");
497 /* Handle an Inquire from the server. Return False if it could not be
498 handled; in this case the caller shll complete the operation. LINE
499 is the complete line as received from the server. This function
500 may change the content of LINE. */
502 handle_inquire (assuan_context_t ctx
, char *line
)
510 /* Skip the command and trailing spaces. */
511 for (; *line
&& !spacep (line
); line
++)
513 while (spacep (line
))
517 for (; *line
&& !spacep (line
); line
++)
522 /* Now match it against our list. he second loop is todetect the
524 for (d
=definq_list
; d
; d
= d
->next
)
525 if (d
->name
&& !strcmp (d
->name
, name
))
528 for (d
=definq_list
; d
; d
= d
->next
)
534 log_info ("no handler for inquiry `%s' found\n", name
);
540 fp
= popen (d
->file
, "r");
542 log_error ("error executing `%s': %s\n", d
->file
, strerror (errno
));
543 else if (opt
.verbose
)
544 log_error ("handling inquiry `%s' by running `%s'\n", name
, d
->file
);
548 fp
= fopen (d
->file
, "rb");
550 log_error ("error opening `%s': %s\n", d
->file
, strerror (errno
));
551 else if (opt
.verbose
)
552 log_error ("handling inquiry `%s' by returning content of `%s'\n",
558 while ( (n
= fread (buffer
, 1, sizeof buffer
, fp
)) )
560 rc
= assuan_send_data (ctx
, buffer
, n
);
563 log_error ("sending data back failed: %s\n", gpg_strerror (rc
) );
568 log_error ("error reading from `%s': %s\n", d
->file
, strerror (errno
));
570 rc
= assuan_send_data (ctx
, NULL
, 0);
572 log_error ("sending data back failed: %s\n", gpg_strerror (rc
) );
577 log_error ("error running `%s': %s\n", d
->file
, strerror (errno
));
585 /* Read all response lines from server and print them. Returns 0 on
586 success or an assuan error code. */
588 read_and_print_response (assuan_context_t ctx
)
600 rc
= assuan_read_line (ctx
, &line
, &linelen
);
604 if (opt
.verbose
> 1 && *line
== '#')
606 fwrite (line
, linelen
, 1, stdout
);
610 while (*line
== '#' || !linelen
);
613 && line
[0] == 'D' && line
[1] == ' ')
617 for (i
=2; i
< linelen
; )
621 printf ("D[%04X] ", i
-2);
622 for (j
=0; j
< 16 ; j
++, i
++)
627 printf (" %02X", ((unsigned char*)line
)[i
]);
633 for (j
=0; j
< 16; j
++, i
++)
635 unsigned int c
= ((unsigned char*)line
)[i
];
638 else if (isascii (c
) && isprint (c
) && !iscntrl (c
))
648 const unsigned char *s
;
652 for (j
=2, s
=(unsigned char*)line
+2; j
< linelen
; j
++, s
++ )
656 fputs ("D ", stdout
);
659 if (*s
== '%' && j
+2 < linelen
)
671 need_lf
= (c
!= '\n');
675 fwrite (line
, linelen
, 1, stdout
);
689 && (line
[1] == '\0' || line
[1] == ' '))
691 fwrite (line
, linelen
, 1, stdout
);
694 else if (linelen
>= 2
695 && line
[0] == 'O' && line
[1] == 'K'
696 && (line
[2] == '\0' || line
[2] == ' '))
698 fwrite (line
, linelen
, 1, stdout
);
702 else if (linelen
>= 3
703 && line
[0] == 'E' && line
[1] == 'R' && line
[2] == 'R'
704 && (line
[3] == '\0' || line
[3] == ' '))
706 fwrite (line
, linelen
, 1, stdout
);
710 else if (linelen
>= 7
711 && line
[0] == 'I' && line
[1] == 'N' && line
[2] == 'Q'
712 && line
[3] == 'U' && line
[4] == 'I' && line
[5] == 'R'
714 && (line
[7] == '\0' || line
[7] == ' '))
716 fwrite (line
, linelen
, 1, stdout
);
718 if (!handle_inquire (ctx
, line
))
719 assuan_write_line (ctx
, "CANCEL");
721 else if (linelen
>= 3
722 && line
[0] == 'E' && line
[1] == 'N' && line
[2] == 'D'
723 && (line
[3] == '\0' || line
[3] == ' '))
725 fwrite (line
, linelen
, 1, stdout
);
727 /* Received from server, thus more responses are expected. */
730 return gpg_error (GPG_ERR_ASS_INV_RESPONSE
);
738 /* Connect to the agent and send the standard options. */
739 static assuan_context_t
744 assuan_context_t ctx
;
746 infostr
= getenv ("GPG_AGENT_INFO");
747 if (!infostr
|| !*infostr
)
751 /* Check whether we can connect at the standard socket. */
752 sockname
= make_filename (opt
.homedir
, "S.gpg-agent", NULL
);
753 rc
= assuan_socket_connect (&ctx
, sockname
, 0);
761 infostr
= xstrdup (infostr
);
762 if ( !(p
= strchr (infostr
, PATHSEP_C
)) || p
== infostr
)
764 log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
770 while (*p
&& *p
!= PATHSEP_C
)
772 prot
= *p
? atoi (p
+1) : 0;
775 log_error (_("gpg-agent protocol version %d is not supported\n"),
781 rc
= assuan_socket_connect (&ctx
, infostr
, pid
);
787 log_error ("can't connect to the agent: %s\n", gpg_strerror (rc
));
792 log_info ("connection to agent established\n");
794 rc
= assuan_transact (ctx
, "RESET", NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
797 log_error (_("error sending %s command: %s\n"), "RESET",
802 rc
= send_pinentry_environment (ctx
, GPG_ERR_SOURCE_DEFAULT
,
803 NULL
, NULL
, NULL
, NULL
, NULL
);
806 log_error (_("error sending standard options: %s\n"), gpg_strerror (rc
));