2 /* vim:tw=78:ts=8:sw=4:set ft=c: */
4 Copyright (C) 2007-2009 Ben Kibbey <bjk@luxsci.net>
6 This program 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 This program 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
29 #include <sys/select.h>
41 #ifdef HAVE_GETOPT_LONG
46 #include "getopt_long.h"
50 #define N_(msgid) gettext(msgid)
54 #define DEFAULT_PORT 22
57 static void show_error(gpg_error_t error
)
59 fprintf(stderr
, "ERR %i %s\n", gpg_err_code(error
), pwmd_strerror(error
));
62 static void usage(const char *pn
, int status
)
64 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
65 "Read a PWMD protocol command from standard input.\n\n"
66 "Usage: pwmc [options] [file]\n"
69 " pinentry method (0=pwmd, 1=libpwmd, 2=pwmd async, "
73 " number of pinentry tries before failing (3)\n"
75 " --host, -h <hostname>\n"
76 " connect to the specified hostname\n"
78 " alterate port (22)\n"
80 " SSH username (default is the invoking user)\n"
81 " --identity, -i <filename>\n"
82 " SSH identity file\n"
83 " --known-hosts, -k <filename>\n"
84 " known host's file (for server validation)\n"
85 " --get-hostkey, -g\n"
86 " retrieve the remote SSH host key and exit\n"
88 " try connecting via IPv4 only\n"
90 " try connecting via IPv6 only\n"
92 " --timeout <seconds>\n"
95 " disable showing of status messages from the server\n"
97 " set the client name\n"
98 " --socket <filename>\n"
99 " local socket to connect to (~/.pwmd/socket)\n"
100 " --passphrase, -P <string>\n"
101 " passphrase to use (disables pinentry use)\n"
102 " --pinentry <path>\n"
103 " the full path to the pinentry binary (server default)\n"
104 " --ttyname, -y <path>\n"
105 " tty that pinentry will use\n"
106 " --ttytype, -t <string>\n"
107 " pinentry terminal type (default is TERM)\n"
109 " pinentry display (default is DISPLAY)\n"
110 " --lc-ctype <string>\n"
111 " locale setting for pinentry\n"
112 " --lc-messages <string>\n"
113 " locale setting for pinentry\n"
114 " --output-fd <FD>\n"
115 " redirect command output to the specified file descriptor\n"
116 " --inquire-fd <FD>\n"
117 " read inquire data from the specified file descriptor\n"
119 " send the SAVE command before exiting\n"
120 " --iterations, -I <N>\n"
121 " encrypt with the specified number of iterations when saving\n"
132 static gpg_error_t
do_inquire(void *data
, const char *keyword
, gpg_error_t rc
,
133 char **result
, size_t *result_len
)
136 static char buf
[ASSUAN_LINELENGTH
];
139 struct inquire_s
*inq
= (struct inquire_s
*)data
;
142 memset(buf
, 0, sizeof(buf
));
150 snprintf(buf
, sizeof(buf
), "%s", inq
->data
);
151 pwmd_free(inq
->data
);
157 while ((c
= fgetc(inq
->fp
)) != EOF
) {
158 if (len
== sizeof(buf
)) {
168 memset(buf
, 0, sizeof(buf
));
177 static int status_msg_cb(void *data
, const char *line
)
179 fprintf(stderr
, "%s\n", line
);
183 int main(int argc
, char *argv
[])
186 char *password
= NULL
;
187 char *filename
= NULL
;
188 char *socketpath
= NULL
;
189 char command
[ASSUAN_LINELENGTH
], *p
;
190 int ret
= EXIT_SUCCESS
;
194 char *pinentry_path
= NULL
;
195 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
197 int outfd
= STDOUT_FILENO
;
198 FILE *outfp
= stdout
;
199 int inquirefd
= STDIN_FILENO
;
200 FILE *inquirefp
= stdin
;
202 char *clientname
= NULL
;
203 char *inquire
= NULL
;
209 int port
= DEFAULT_PORT
;
210 char *username
= NULL
;
212 char *known_hosts
= NULL
;
214 int prot
= PWMD_IP_ANY
;
221 /* The order is important. */
227 OPT_HOST
, OPT_PORT
, OPT_IDENTITY
, OPT_KNOWN_HOSTS
, OPT_USER
,
228 OPT_GET_HOSTKEY
, OPT_IPV4
, OPT_IPV6
,
230 OPT_TTYNAME
, OPT_TTYTYPE
, OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
,
231 OPT_TIMEOUT
, OPT_TRIES
, OPT_PINENTRY
,
232 OPT_PASSPHRASE
, OPT_SOCKET
, OPT_SAVE
, OPT_ITERATIONS
, OPT_OUTPUT_FD
,
233 OPT_INQUIRE_FD
, OPT_NO_STATUS
, OPT_NAME
, OPT_VERSION
, OPT_HELP
,
235 const struct option long_opts
[] = {
237 { "debug", 1, 0, 0 },
240 { "host", 1, 0, 'h' },
241 { "port", 1, 0, 'p' },
242 { "identity", 1, 0, 'i' },
243 { "known-hosts", 1, 0, 'k' },
244 { "user", 1, 0, 'u' },
245 { "get-hostkey", 0, 0, 'g' },
246 { "ipv4", 0, 0, '4' },
247 { "ipv6", 0, 0, '6' },
249 { "ttyname", 1, 0, 'y' },
250 { "ttytype", 1, 0, 't' },
251 { "display", 1, 0, 'd' },
252 { "lc-ctype", 1, 0, 0 },
253 { "lc-messages", 1, 0, 0 },
254 { "timeout", 1, 0, 0 },
255 { "tries", 1, 0, 0 },
256 { "pinentry", 1, 0, 0 },
257 { "passphrase", 1, 0, 'P' },
258 { "socket", 1, 0, 0 },
259 { "save", 0, 0, 'S' },
260 { "iterations", 1, 0, 'I' },
261 { "output-fd", 1, 0, 0 },
262 { "inquire-fd", 1, 0, 0 },
263 { "no-status", 0, 0, 0 },
264 { "name", 1, 0, 'n' },
265 { "version", 0, 0, 0 },
270 const char *optstring
= "46h:p:i:k:u:gy:t:d:P:I:Sn:";
272 const char *optstring
= "y:t:d:P:I:Sn:";
277 setlocale(LC_ALL
, "");
278 bindtextdomain("libpwmd", LOCALEDIR
);
281 while ((opt
= getopt_long(argc
, argv
, optstring
, &long_opts
, &opt_index
)) != -1) {
283 /* Handle long options without a short option part. */
288 method
= atoi(optarg
);
295 lcctype
= pwmd_strdup(optarg
);
297 case OPT_LC_MESSAGES
:
298 lcmessages
= pwmd_strdup(optarg
);
301 timeout
= atoi(optarg
);
304 tries
= atoi(optarg
);
307 socketpath
= pwmd_strdup(optarg
);
310 inquirefd
= atoi(optarg
);
311 inquirefp
= fdopen(inquirefd
, "r");
315 err(EXIT_FAILURE
, "%i", inquirefd
);
319 outfd
= atoi(optarg
);
320 outfp
= fdopen(outfd
, "w");
324 err(EXIT_FAILURE
, "%i", outfd
);
332 printf("%s (pwmc)\n%s\n", PACKAGE_STRING
, PACKAGE_BUGREPORT
);
335 usage(argv
[0], EXIT_SUCCESS
);
337 usage(argv
[0], EXIT_FAILURE
);
349 host
= pwmd_strdup(optarg
);
355 ident
= pwmd_strdup(optarg
);
358 username
= pwmd_strdup(optarg
);
361 known_hosts
= pwmd_strdup(optarg
);
380 iter
= strtol(optarg
, NULL
, 10);
384 password
= pwmd_strdup(optarg
);
385 memset(optarg
, 0, strlen(optarg
));
388 clientname
= pwmd_strdup(optarg
);
392 usage(argv
[0], EXIT_FAILURE
);
397 if (host
&& !get
&& (!known_hosts
|| !ident
)) {
399 usage(argv
[0], EXIT_FAILURE
);
404 usage(argv
[0], EXIT_FAILURE
);
408 filename
= argv
[optind
];
410 pwm
= pwmd_new("pwmc");
414 if (prot
!= PWMD_IP_ANY
) {
415 error
= pwmd_setopt(pwm
, PWMD_OPTION_IP_VERSION
, prot
);
426 error
= pwmd_get_hostkey_async(pwm
, host
, port
);
429 errx(EXIT_FAILURE
, "%s: %s", host
, pwmd_strerror(error
));
432 s
= pwmd_process(pwm
, &error
, &hostkey
);
435 } while (s
== ASYNC_PROCESS
);
440 printf("%s\n", hostkey
);
447 error
= pwmd_ssh_connect_async(pwm
, host
, port
, ident
, username
, known_hosts
);
453 s
= pwmd_process(pwm
, &error
, &result
);
456 } while (s
== ASYNC_PROCESS
);
466 error
= pwmd_get_hostkey(pwm
, host
, port
, &hostkey
);
471 printf("%s\n", hostkey
);
478 error
= pwmd_ssh_connect(pwm
, host
, port
, ident
, username
, known_hosts
);
488 error
= pwmd_connect(pwm
, socketpath
);
497 error
= pwmd_command(pwm
, &result
, "VERSION");
499 if (error
&& error
!= GPG_ERR_ASS_UNKNOWN_CMD
)
504 if (error
== GPG_ERR_ASS_UNKNOWN_CMD
) {
508 usage(argv
[0], EXIT_FAILURE
);
512 /* pwmd version 2 or later. */
516 usage(argv
[0], EXIT_FAILURE
);
522 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
529 error
= pwmd_setopt(pwm
, PWMD_OPTION_PASSPHRASE
, password
);
536 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
543 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
550 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
557 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
564 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
571 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
579 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
587 error
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
597 error
= pwmd_open(pwm
, filename
);
600 error
= pwmd_open2(pwm
, filename
);
603 error
= pwmd_open_async(pwm
, filename
);
606 error
= pwmd_open_async2(pwm
, filename
);
610 if (!error
&& method
> 1) {
612 s
= pwmd_process(pwm
, &error
, &result
);
615 } while (s
== ASYNC_PROCESS
);
618 error
= pwmd_open(pwm
, filename
);
626 error
= pwmd_command(pwm
, &result
, "LOCK");
635 struct timeval tv
= {0, 100000};
640 FD_SET(STDIN_FILENO
, &rfds
);
641 n
= select(STDIN_FILENO
+1, &rfds
, NULL
, NULL
, &tv
);
644 s
= pwmd_process(pwm
, &error
, &result
);
649 fprintf(stderr
, ".");
654 error
= gpg_error_from_errno(errno
);
658 fprintf(stderr
, "\n");
659 n
= read(STDIN_FILENO
, command
, sizeof(command
));
662 error
= gpg_error_from_errno(errno
);
666 if (n
&& command
[strlen(command
)-1] == '\n')
667 command
[strlen(command
)-1] = 0;
675 p
= fgets(command
, sizeof(command
), stdin
);
677 p
= fgets(command
, sizeof(command
), stdin
);
684 * This is a known INQUIRE command. We use pwmd_inquire() to send the
685 * data from the do_inquire() callback function.
687 if (strncasecmp(p
, "STORE ", 6) == 0) {
689 inquire
= (char *)"STORE";
691 else if (strncasecmp(p
, "IMPORT ", 7) == 0) {
693 inquire
= (char *)"IMPORT";
697 struct inquire_s
*inq
= (struct inquire_s
*)pwmd_malloc(sizeof(struct inquire_s
));
700 error
= gpg_error_from_errno(ENOMEM
);
704 inq
->data
= pwmd_strdup(p
);
706 error
= pwmd_inquire(pwm
, inquire
, do_inquire
, inq
);
711 if (strcasecmp(p
, "BYE") == 0)
714 error
= pwmd_command(pwm
, &result
, command
);
715 memset(command
, 0, sizeof(command
));
721 fwrite(result
, 1, strlen(result
), outfp
);
726 memset(command
, 0, sizeof(command
));
729 if (!error
&& save
) {
731 error
= pwmd_command(pwm
, &result
, "OPTION ITERATIONS=%i", iter
);
740 error
= pwmd_save(pwm
);
743 error
= pwmd_save2(pwm
);
746 error
= pwmd_save_async(pwm
);
749 error
= pwmd_save_async2(pwm
);
753 if (!error
&& method
> 1) {
755 s
= pwmd_process(pwm
, &error
, &result
);
758 } while (s
== ASYNC_PROCESS
);
761 error
= pwmd_save(pwm
);
765 if (!error
&& filename
)
766 error
= pwmd_command(pwm
, &result
, "UNLOCK");
776 pwmd_free(socketpath
);