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/>.
30 #include "../common/util.h"
31 #include "../common/asshelp.h"
35 /* Constants to identify the commands and options. */
36 enum cmd_and_opt_values
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")},
68 { oNoVerbose
, "no-verbose", 0, "@"},
69 { oHomedir
, "homedir", 2, "@" },
74 /* We keep all global options in the structure OPT. */
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. */
89 /* Definitions for /definq commands and a global linked list with all
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. */
114 my_strusage( int level
)
120 case 11: p
= "gpg-connect-agent (GnuPG)";
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");
127 case 40: p
= _("Usage: gpg-connect-agent [options] (-h for help)");
130 p
= _("Syntax: gpg-connect-agent [options]\n"
131 "Connect to a running agent and send commands\n");
133 case 31: p
= "\nHome: "; break;
134 case 32: p
= opt
.homedir
; break;
135 case 33: p
= "\n"; break;
137 default: p
= NULL
; break;
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. */
147 add_definq (char *line
, int is_prog
)
154 for (p
=name
; *p
&& !spacep (p
); p
++)
161 d
= xmalloc (sizeof *d
+ strlen (p
) );
163 d
->is_prog
= is_prog
;
164 if ( !strcmp (name
, "*"))
167 d
->name
= xstrdup (name
);
170 *definq_list_tail
= d
;
171 definq_list_tail
= &d
->next
;
175 /* Show all inquiry defintions. */
181 for (d
=definq_list
; d
; d
= d
->next
)
183 printf ("%-20s %c %s\n", d
->name
, d
->is_prog
? 'p':'f', d
->file
);
184 for (d
=definq_list
; d
; d
= d
->next
)
186 printf ("%-20s %c %s\n", "*", d
->is_prog
? 'p':'f', d
->file
);
190 /* Clear all inquiry definitions. */
196 definq_t tmp
= definq_list
->next
;
197 xfree (definq_list
->name
);
201 definq_list_tail
= &definq_list
;
206 do_sendfd (assuan_context_t ctx
, char *line
)
209 char *name
, *mode
, *p
;
214 for (p
=name
; *p
&& !spacep (p
); p
++)
227 for (p
=mode
; *p
&& !spacep (p
); p
++)
234 fp
= fopen (name
, mode
);
237 log_error ("can't open `%s' in \"%s\" mode: %s\n",
238 name
, mode
, strerror (errno
));
244 log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
247 rc
= assuan_sendfd (ctx
, fd
);
249 log_error ("sednig descriptor %d failed: %s\n", fd
, gpg_strerror (rc
));
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
)
267 int no_more_options
= 0;
268 assuan_context_t ctx
;
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);
283 opt
.homedir
= default_homedir ();
284 opt
.connect_flags
= 1; /* Use extended connect mode. */
286 /* Parse the command line. */
289 pargs
.flags
= 1; /* Do not remove the args. */
290 while (!no_more_options
&& optfile_parse (NULL
, NULL
, NULL
, &pargs
, opts
))
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))
315 log_error (_("option \"%s\" requires a program "
316 "and optional arguments\n"), "--exec" );
323 if (opt
.exec
&& opt
.raw_socket
)
324 log_info (_("option \"%s\" ignored due to \"%s\"\n"),
325 "--raw-socket", "--exec");
331 no_close
[0] = fileno (stderr
);
332 no_close
[1] = log_get_fd ();
334 rc
= assuan_pipe_connect_ext (&ctx
, *argv
, (const char **)argv
,
335 no_close
, NULL
, NULL
,
339 log_error ("assuan_pipe_connect_ext failed: %s\n",
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,
354 log_error ("can't connect to socket `%s': %s\n",
355 opt
.raw_socket
, gpg_strerror (rc
));
360 log_info ("connection to socket `%s' established\n", opt
.raw_socket
);
363 ctx
= start_agent ();
372 n
= read_line (stdin
, &line
, &linesize
, &maxlength
);
375 log_error (_("error reading input: %s\n"), strerror (errno
));
382 log_error (_("line too long - skipped\n"));
385 if (memchr (line
, 0, n
))
386 log_info (_("line shortened due to embedded Nul character\n"));
387 if (line
[n
-1] == '\n')
391 /* Handle control commands. */
394 for (p
=cmd
; *p
&& !spacep (p
); p
++)
400 if (!strcmp (cmd
, "definqfile"))
404 else if (!strcmp (cmd
, "definqprog"))
408 else if (!strcmp (cmd
, "showdef"))
412 else if (!strcmp (cmd
, "cleardef"))
416 else if (!strcmp (cmd
, "echo"))
420 else if (!strcmp (cmd
, "sendfd"))
425 else if (!strcmp (cmd
, "recvfd"))
430 else if (!strcmp (cmd
, "hex"))
432 else if (!strcmp (cmd
, "nohex"))
434 else if (!strcmp (cmd
, "decode"))
436 else if (!strcmp (cmd
, "nodecode"))
438 else if (!strcmp (cmd
, "help"))
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.");
458 log_error (_("unknown command `%s'\n"), cmd
);
463 rc
= assuan_write_line (ctx
, line
);
466 log_info (_("sending line failed: %s\n"), gpg_strerror (rc
) );
469 if (*line
== '#' || !*line
)
470 continue; /* Don't expect a response for a comment line. */
472 rc
= read_and_print_response (ctx
);
474 log_info (_("receiving line failed: %s\n"), gpg_strerror (rc
) );
478 log_info ("closing connection to agent\n");
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. */
489 handle_inquire (assuan_context_t ctx
, char *line
)
497 /* Skip the command and trailing spaces. */
498 for (; *line
&& !spacep (line
); line
++)
500 while (spacep (line
))
504 for (; *line
&& !spacep (line
); line
++)
509 /* Now match it against our list. he second loop is todetect the
511 for (d
=definq_list
; d
; d
= d
->next
)
512 if (d
->name
&& !strcmp (d
->name
, name
))
515 for (d
=definq_list
; d
; d
= d
->next
)
521 log_info ("no handler for inquiry `%s' found\n", name
);
527 fp
= popen (d
->file
, "r");
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
);
535 fp
= fopen (d
->file
, "rb");
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",
545 while ( (n
= fread (buffer
, 1, sizeof buffer
, fp
)) )
547 rc
= assuan_send_data (ctx
, buffer
, n
);
550 log_error ("sending data back failed: %s\n", gpg_strerror (rc
) );
555 log_error ("error reading from `%s': %s\n", d
->file
, strerror (errno
));
557 rc
= assuan_send_data (ctx
, NULL
, 0);
559 log_error ("sending data back failed: %s\n", gpg_strerror (rc
) );
564 log_error ("error running `%s': %s\n", d
->file
, strerror (errno
));
572 /* Read all response lines from server and print them. Returns 0 on
573 success or an assuan error code. */
575 read_and_print_response (assuan_context_t ctx
)
587 rc
= assuan_read_line (ctx
, &line
, &linelen
);
591 if (opt
.verbose
> 1 && *line
== '#')
593 fwrite (line
, linelen
, 1, stdout
);
597 while (*line
== '#' || !linelen
);
600 && line
[0] == 'D' && line
[1] == ' ')
604 for (i
=2; i
< linelen
; )
608 printf ("D[%04X] ", i
-2);
609 for (j
=0; j
< 16 ; j
++, i
++)
614 printf (" %02X", ((unsigned char*)line
)[i
]);
620 for (j
=0; j
< 16; j
++, i
++)
622 unsigned int c
= ((unsigned char*)line
)[i
];
625 else if (isascii (c
) && isprint (c
) && !iscntrl (c
))
635 const unsigned char *s
;
639 for (j
=2, s
=(unsigned char*)line
+2; j
< linelen
; j
++, s
++ )
643 fputs ("D ", stdout
);
646 if (*s
== '%' && j
+2 < linelen
)
658 need_lf
= (c
!= '\n');
662 fwrite (line
, linelen
, 1, stdout
);
676 && (line
[1] == '\0' || line
[1] == ' '))
678 fwrite (line
, linelen
, 1, stdout
);
681 else if (linelen
>= 2
682 && line
[0] == 'O' && line
[1] == 'K'
683 && (line
[2] == '\0' || line
[2] == ' '))
685 fwrite (line
, linelen
, 1, stdout
);
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
);
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'
701 && (line
[7] == '\0' || line
[7] == ' '))
703 fwrite (line
, linelen
, 1, stdout
);
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
);
714 /* Received from server, thus more responses are expected. */
717 return gpg_error (GPG_ERR_ASS_INV_RESPONSE
);
725 /* Connect to the agent and send the standard options. */
726 static assuan_context_t
731 assuan_context_t ctx
;
733 infostr
= getenv ("GPG_AGENT_INFO");
734 if (!infostr
|| !*infostr
)
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);
748 infostr
= xstrdup (infostr
);
749 if ( !(p
= strchr (infostr
, PATHSEP_C
)) || p
== infostr
)
751 log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
757 while (*p
&& *p
!= PATHSEP_C
)
759 prot
= *p
? atoi (p
+1) : 0;
762 log_error (_("gpg-agent protocol version %d is not supported\n"),
768 rc
= assuan_socket_connect (&ctx
, infostr
, pid
);
774 log_error ("can't connect to the agent: %s\n", gpg_strerror (rc
));
779 log_info ("connection to agent established\n");
781 rc
= assuan_transact (ctx
, "RESET", NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
784 log_error (_("error sending %s command: %s\n"), "RESET",
789 rc
= send_pinentry_environment (ctx
, GPG_ERR_SOURCE_DEFAULT
,
790 NULL
, NULL
, NULL
, NULL
, NULL
);
793 log_error (_("error sending standard options: %s\n"), gpg_strerror (rc
));