1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2007-2009 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
28 #include <sys/select.h>
31 #include <sys/types.h>
42 #ifdef HAVE_GETOPT_LONG
47 #include "getopt_long.h"
51 #define N_(msgid) gettext(msgid)
55 #define DEFAULT_PORT 22
59 static void show_error(gpg_error_t error
)
61 fprintf(stderr
, "ERR %i %s\n", gpg_err_code(error
), pwmd_strerror(error
));
64 static void usage(const char *pn
, int status
)
66 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
67 "Read a PWMD protocol command from standard input.\n\n"
68 "Usage: pwmc [options] [file]\n"
71 " pinentry method (0=pwmd, 1=libpwmd, 2=pwmd async, "
76 " --host, -h <hostname>\n"
77 " connect to the specified hostname\n"
80 " alterate port (22)\n"
83 " SSH username (default is the invoking user)\n"
85 " --identity, -i <filename>\n"
86 " SSH identity file\n"
88 " --known-hosts, -k <filename>\n"
89 " known host's file (for server validation)\n"
91 " --get-hostkey, -g\n"
92 " retrieve the remote SSH host key and exit\n"
95 " try connecting via IPv4 only\n"
98 " try connecting via IPv6 only\n"
102 " a url string to parse\n"
105 " disable showing of status messages from the server\n"
107 " --name, -n <string>\n"
108 " set the client name\n"
110 " --socket <filename>\n"
111 " local socket to connect to (~/.pwmd/socket)\n"
113 " --passphrase, -P <string>\n"
114 " passphrase to use (disables pinentry use)\n"
116 " --key-file <filename>\n"
117 " obtain the passphrase from the specified filename\n"
120 " the passphrase is base64 encoded\n"
122 " --timeout <seconds>\n"
123 " pinentry timeout\n"
126 " number of pinentry tries before failing (3)\n"
128 " --pinentry <path>\n"
129 " the full path to the pinentry binary (server default)\n"
131 " --ttyname, -y <path>\n"
132 " tty that pinentry will use\n"
134 " --ttytype, -t <string>\n"
135 " pinentry terminal type (default is TERM)\n"
138 " pinentry display (default is DISPLAY)\n"
140 " --lc-ctype <string>\n"
141 " locale setting for pinentry\n"
143 " --lc-messages <string>\n"
144 " locale setting for pinentry\n"
147 " --local-pinentry\n"
148 " force using a local pinentry\n"
150 " --output-fd <FD>\n"
151 " redirect command output to the specified file descriptor\n"
153 " --inquire-fd <FD>\n"
154 " read inquire data from the specified file descriptor\n"
156 " --cipher <string>\n"
157 " the cipher to use when saving\n"
160 " send the SAVE command before exiting\n"
163 " like --save, but ask for a passphrase\n"
165 " --iterations, -I <N>\n"
166 " encrypt with the specified number of iterations when saving\n"
170 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
172 "A url string (specified with --url) may be in the form of:\n"
173 " file://[path/to/socket]\n"
175 " ssh[46]://[username@]hostname[:port],identity,known_hosts\n"
187 static gpg_error_t
inquire_cb(void *user
, const char *cmd
, gpg_error_t rc
,
188 char **data
, size_t *len
)
190 struct inquire_s
*inq
= user
;
198 /* The first part of the command data. */
206 *len
= read(inq
->fd
, inq
->line
, ASSUAN_LINELENGTH
);
209 return gpg_error_from_syserror();
214 return *len
? 0 : GPG_ERR_EOF
;
217 static int status_msg_cb(void *data
, const char *line
)
219 fprintf(stderr
, "%s\n", line
);
223 static gpg_error_t
process_cmd(pwm_t
*pwm
, char **result
, int input
)
232 pwmd_fd_t pfds
[nfds
];
235 rc
= pwmd_get_fds(pwm
, pfds
, &nfds
);
241 s
= pwmd_process(pwm
, &rc
, result
);
245 for (i
= 0, n
= 0; i
< nfds
; i
++) {
246 FD_SET(pfds
[i
].fd
, &rfds
);
247 n
= pfds
[i
].fd
> n
? pfds
[i
].fd
: n
;
251 FD_SET(STDIN_FILENO
, &rfds
);
253 nfds
= select(n
+1, &rfds
, NULL
, NULL
, NULL
);
256 rc
= gpg_error_from_errno(errno
);
260 if (input
&& FD_ISSET(STDIN_FILENO
, &rfds
))
263 s
= pwmd_process(pwm
, &rc
, result
);
264 } while (s
== ASYNC_PROCESS
);
269 static gpg_error_t
knownhost_cb(void *data
, const char *host
, const char *key
,
273 char *buf
= pwmd_strdup_printf(N_("Password Manager Daemon: %s\n\nWhile attempting an SSH connection to %s there was a problem verifying it's hostkey against the known and trusted hosts file because it's hostkey was not found.\n\nWould you like to treat this connection as trusted for this and future connections by adding %s's hostkey to the known hosts file?"), (char *)data
, host
, host
);
275 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TITLE
, buf
);
281 return pwmd_getpin(pwm
, NULL
, NULL
, PWMD_PINENTRY_CONFIRM
);
284 int main(int argc
, char *argv
[])
287 char *password
= NULL
;
288 char *keyfile
= NULL
;
290 char *filename
= NULL
;
291 char *socketpath
= NULL
;
292 char command
[ASSUAN_LINELENGTH
], *p
;
293 int ret
= EXIT_SUCCESS
;
297 char *pinentry_path
= NULL
;
298 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
300 int outfd
= STDOUT_FILENO
;
301 FILE *outfp
= stdout
;
302 int inquirefd
= STDIN_FILENO
;
303 FILE *inquirefp
= stdin
;
305 char *clientname
= "pwmc";
306 char *inquire
= NULL
;
315 int port
= DEFAULT_PORT
;
316 char *username
= NULL
;
318 char *known_hosts
= NULL
;
320 int prot
= PWMD_IP_ANY
;
326 int lock_on_open
= 1;
331 char *url_string
= NULL
;
332 /* The order is important. */
338 OPT_HOST
, OPT_PORT
, OPT_IDENTITY
, OPT_KNOWN_HOSTS
, OPT_USER
,
339 OPT_GET_HOSTKEY
, OPT_IPV4
, OPT_IPV6
,
341 OPT_URL
, OPT_LOCAL
, OPT_FORCE_SAVE
, OPT_TTYNAME
, OPT_TTYTYPE
,
342 OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
, OPT_TIMEOUT
, OPT_TRIES
,
343 OPT_PINENTRY
, OPT_PASSPHRASE
, OPT_KEYFILE
, OPT_BASE64
, OPT_SOCKET
,
344 OPT_NOLOCK
, OPT_SAVE
, OPT_ITERATIONS
, OPT_OUTPUT_FD
, OPT_INQUIRE_FD
,
345 OPT_NO_STATUS
, OPT_NAME
, OPT_VERSION
, OPT_HELP
, OPT_CIPHER
,
347 const struct option long_opts
[] = {
349 { "debug", 1, 0, 0 },
352 { "host", 1, 0, 'h' },
353 { "port", 1, 0, 'p' },
354 { "identity", 1, 0, 'i' },
355 { "known-hosts", 1, 0, 'k' },
356 { "user", 1, 0, 'u' },
357 { "get-hostkey", 0, 0, 'g' },
358 { "ipv4", 0, 0, '4' },
359 { "ipv6", 0, 0, '6' },
362 { "local-pinentry", 0, 0 },
363 { "force-save", 0, 0 },
364 { "ttyname", 1, 0, 'y' },
365 { "ttytype", 1, 0, 't' },
366 { "display", 1, 0, 'd' },
367 { "lc-ctype", 1, 0, 0 },
368 { "lc-messages", 1, 0, 0 },
369 { "timeout", 1, 0, 0 },
370 { "tries", 1, 0, 0 },
371 { "pinentry", 1, 0, 0 },
372 { "passphrase", 1, 0, 'P' },
373 { "key-file", 1, 0, 0 },
374 { "base64", 0, 0, 0 },
375 { "socket", 1, 0, 0 },
376 { "no-lock", 0, 0, 0 },
377 { "save", 0, 0, 'S' },
378 { "iterations", 1, 0, 'I' },
379 { "output-fd", 1, 0, 0 },
380 { "inquire-fd", 1, 0, 0 },
381 { "no-status", 0, 0, 0 },
382 { "name", 1, 0, 'n' },
383 { "version", 0, 0, 0 },
385 { "cipher", 1, 0, 0 },
389 const char *optstring
= "46h:p:i:k:u:gy:t:d:P:I:Sn:";
391 const char *optstring
= "y:t:d:P:I:Sn:";
396 setlocale(LC_ALL
, "");
397 bindtextdomain("libpwmd", LOCALEDIR
);
400 while ((opt
= getopt_long(argc
, argv
, optstring
, long_opts
, &opt_index
)) != -1) {
402 /* Handle long options without a short option part. */
407 method
= atoi(optarg
);
429 save
= force_save
= 1;
432 lcctype
= pwmd_strdup(optarg
);
434 case OPT_LC_MESSAGES
:
435 lcmessages
= pwmd_strdup(optarg
);
438 timeout
= atoi(optarg
);
441 tries
= atoi(optarg
);
445 socketpath
= pwmd_strdup(optarg
);
448 inquirefd
= atoi(optarg
);
449 inquirefp
= fdopen(inquirefd
, "r");
453 err(EXIT_FAILURE
, "%i", inquirefd
);
457 outfd
= atoi(optarg
);
458 outfp
= fdopen(outfd
, "w");
462 err(EXIT_FAILURE
, "%i", outfd
);
470 printf("%s (pwmc)\n%s\n\n"
471 "Compile-time features:\n"
493 , PACKAGE_STRING
, PACKAGE_BUGREPORT
);
496 pinentry_path
= optarg
;
499 usage(argv
[0], EXIT_SUCCESS
);
504 usage(argv
[0], EXIT_FAILURE
);
516 host
= pwmd_strdup(optarg
);
522 ident
= pwmd_strdup(optarg
);
525 username
= pwmd_strdup(optarg
);
528 known_hosts
= pwmd_strdup(optarg
);
547 iter
= strtol(optarg
, NULL
, 10);
551 password
= pwmd_strdup(optarg
);
552 memset(optarg
, 0, strlen(optarg
));
559 usage(argv
[0], EXIT_FAILURE
);
567 if (host
&& !get
&& !ident
) {
569 usage(argv
[0], EXIT_FAILURE
);
574 usage(argv
[0], EXIT_FAILURE
);
581 filename
= argv
[optind
];
583 pwm
= pwmd_new(clientname
);
589 if (host
|| url_string
) {
592 if (prot
!= PWMD_IP_ANY
) {
593 error
= pwmd_setopt(pwm
, PWMD_OPTION_IP_VERSION
, prot
);
599 error
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_CB
, knownhost_cb
);
604 error
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_DATA
, clientname
);
614 error
= pwmd_get_hostkey_async(pwm
, host
, port
);
617 errx(EXIT_FAILURE
, "%s: %s", host
, pwmd_strerror(error
));
619 error
= process_cmd(pwm
, &hostkey
, 0);
624 printf("%s", hostkey
);
632 error
= pwmd_connect_url_async(pwm
, url_string
);
634 error
= pwmd_ssh_connect_async(pwm
, host
, port
, ident
, username
,
640 error
= process_cmd(pwm
, NULL
, 0);
650 error
= pwmd_get_hostkey(pwm
, host
, port
, &hostkey
);
655 printf("%s", hostkey
);
663 error
= pwmd_connect_url(pwm
, url_string
);
665 error
= pwmd_ssh_connect(pwm
, host
, port
, ident
, username
, known_hosts
);
676 error
= pwmd_connect_url(pwm
, url_string
);
678 error
= pwmd_connect(pwm
, socketpath
);
686 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TITLE
, NULL
);
687 error
= pwmd_socket_type(pwm
, &s
);
692 if (s
== PWMD_SOCKET_SSH
&& force_save
&& !local_pin
) {
693 error
= GPG_ERR_WRONG_KEY_USAGE
;
698 error
= pwmd_command(pwm
, &result
, "VERSION");
708 usage(argv
[0], EXIT_FAILURE
);
712 if (filename
&& lock_on_open
) {
713 error
= pwmd_setopt(pwm
, PWMD_OPTION_LOCK_ON_OPEN
, 1);
720 error
= pwmd_setopt(pwm
, PWMD_OPTION_BASE64
, 1);
727 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
733 if (local_pin
&& filename
&& !keyfile
) {
734 error
= pwmd_command(pwm
, NULL
, "ISCACHED %s", filename
);
736 if (error
== GPG_ERR_NOT_FOUND
) {
738 if (try++ == local_tries
)
747 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, 0);
749 error
= pwmd_getpin(pwm
, filename
, &password
,
750 try > 1 ? PWMD_PINENTRY_OPEN_FAILED
: PWMD_PINENTRY_OPEN
);
755 error
= pwmd_setopt(pwm
, PWMD_OPTION_PASSPHRASE
, password
);
763 else if (error
&& error
!= GPG_ERR_ENOENT
)
768 error
= pwmd_setopt(pwm
, PWMD_OPTION_PASSPHRASE
, password
);
775 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
782 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
789 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
796 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
803 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
810 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
818 error
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
826 error
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
834 if (keyfile
&& (!local_pin
|| host
|| url_string
)) {
835 struct inquire_s inq
;
837 memset(&inq
, 0, sizeof(inq
));
838 inq
.fd
= open(keyfile
, O_RDONLY
);
841 error
= gpg_error_from_syserror();
845 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
848 error
= GPG_ERR_ENOMEM
;
852 error
= pwmd_open_inquire(pwm
, filename
, inquire_cb
, &inq
);
864 error
= pwmd_open(pwm
, filename
);
867 error
= pwmd_open2(pwm
, filename
);
870 error
= pwmd_open_async(pwm
, filename
);
874 error
= pwmd_open_async2(pwm
, filename
);
878 if (error
&& local_pin
&& error
== GPG_ERR_INV_PASSPHRASE
)
885 error
= process_cmd(pwm
, &result
, 0);
889 error
= pwmd_open2(pwm
, filename
);
892 error
= pwmd_open(pwm
, filename
);
894 if (error
&& local_pin
&& error
== GPG_ERR_INV_PASSPHRASE
)
903 fcntl(STDIN_FILENO
, F_SETFL
, O_NONBLOCK
);
907 error
= process_cmd(pwm
, NULL
, 1);
912 n
= read(STDIN_FILENO
, command
, sizeof(command
));
918 error
= gpg_error_from_errno(errno
);
933 * This is a known INQUIRE command. We use pwmd_inquire() to send the
934 * data from inquire_cb().
936 if (n
> 6 && (!memcmp(p
, "STORE ", 6) || !memcmp(p
, "store ", 6))) {
939 inquire
= (char *)"STORE";
941 else if (n
> 7 && (!memcmp(p
, "IMPORT ", 7) || !memcmp(p
, "import ", 7))) {
944 inquire
= (char *)"IMPORT";
948 struct inquire_s inq
;
951 memset(&inq
, 0, sizeof(inq
));
952 inq
.fd
= fileno(inquirefp
);
955 error
= gpg_error_from_syserror();
959 if (fstat(inq
.fd
, &st
) == -1) {
960 error
= gpg_error_from_syserror();
964 error
= pwmd_setopt(pwm
, PWMD_OPTION_INQUIRE_TOTAL
,
965 st
.st_size
? (size_t)st
.st_size
+strlen(p
) : 0);
970 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
973 error
= GPG_ERR_ENOMEM
;
977 memcpy(inq
.line
, p
, n
);
979 error
= pwmd_inquire(pwm
, inquire
, inquire_cb
, &inq
);
985 if (strcasecmp(p
, "BYE") == 0)
988 error
= pwmd_command(pwm
, &result
, command
);
989 memset(command
, 0, sizeof(command
));
995 fwrite(result
, 1, strlen(result
), outfp
);
1000 memset(command
, 0, sizeof(command
));
1003 pwmd_free(password
);
1007 if (!error
&& save
&& filename
) {
1009 error
= pwmd_setopt(pwm
, PWMD_OPTION_ITERATIONS
, iter
);
1016 error
= pwmd_setopt(pwm
, PWMD_OPTION_CIPHER
, cipher
);
1026 error
= pwmd_command(pwm
, NULL
, "ISCACHED %s", filename
);
1028 if (error
&& error
!= GPG_ERR_NOT_FOUND
&&
1029 error
!= GPG_ERR_ENOENT
)
1035 error
= pwmd_getpin(pwm
, filename
, &p1
, PWMD_PINENTRY_SAVE
);
1040 error
= pwmd_getpin(pwm
, filename
, &password
,
1041 PWMD_PINENTRY_SAVE_CONFIRM
);
1048 if ((p1
|| password
) && ((!p1
&& password
) || (!password
&& p1
) ||
1049 strcmp(p1
, password
))) {
1051 pwmd_free(password
);
1059 error
= pwmd_setopt(pwm
, PWMD_OPTION_PASSPHRASE
, password
);
1062 pwmd_free(password
);
1069 error
= pwmd_command(pwm
, NULL
, "CLEARCACHE %s", filename
);
1075 error
= pwmd_setopt(pwm
, PWMD_OPTION_PASSPHRASE
, NULL
);
1083 if (keyfile
&& !local_pin
) {
1084 struct inquire_s inq
;
1086 memset(&inq
, 0, sizeof(inq
));
1087 inq
.fd
= open(keyfile
, O_RDONLY
);
1090 error
= gpg_error_from_syserror();
1094 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
1097 error
= GPG_ERR_ENOMEM
;
1101 error
= pwmd_save_inquire(pwm
, inquire_cb
, &inq
);
1102 pwmd_free(inq
.line
);
1109 error
= pwmd_save(pwm
);
1112 error
= pwmd_save2(pwm
);
1115 error
= pwmd_save_async(pwm
);
1118 error
= pwmd_save_async2(pwm
);
1122 if (!error
&& method
>= 2)
1123 error
= process_cmd(pwm
, NULL
, 0);
1128 error
= pwmd_save2(pwm
);
1131 error
= pwmd_save(pwm
);
1145 pwmd_free(socketpath
);