1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of libpwmd.
8 Libpwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Libpwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
34 #include <sys/select.h>
36 #include <sys/types.h>
47 #ifdef HAVE_GETOPT_LONG
52 #include "getopt_long.h"
55 #ifdef HAVE_LIBREADLINE
56 # if defined(HAVE_READLINE_READLINE_H)
57 # include <readline/readline.h>
58 # elif defined(HAVE_READLINE_H)
59 # include <readline.h>
60 # endif /* !defined(HAVE_READLINE_H) */
61 static int interactive_error
;
62 static int interactive
;
63 #endif /* HAVE_LIBREADLINE */
65 #ifdef HAVE_READLINE_HISTORY
66 # if defined(HAVE_READLINE_HISTORY_H)
67 # include <readline/history.h>
68 # elif defined(HAVE_HISTORY_H)
71 #endif /* HAVE_READLINE_HISTORY */
78 #define N_(msgid) gettext(msgid)
82 #define DEFAULT_PORT 22
83 #define DEFAULT_PIN_TRIES 3
84 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
87 static int no_pinentry
;
89 static char *filename
;
91 static int force_save
;
92 static int no_passphrase
;
95 static char *sign_keygrip
;
96 static char *keyparams
;
97 static char *password
;
99 static char *new_keyfile
;
100 static unsigned long s2k_count
;
101 static uint64_t iterations
;
102 static int iterations_arg
;
104 static int local_pin
;
105 static int inquirefd
;
112 size_t size
; // from stat(2).
116 static gpg_error_t
finalize();
117 static gpg_error_t
set_inquire(int fd
, const char *line
,
118 struct inquire_s
**result
);
119 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
120 size_t *len
, struct inquire_s
*inq
);
121 static gpg_error_t
open_command(const char *line
);
123 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
);
126 static void show_error(gpg_error_t error
)
128 fprintf(stderr
, "ERR %i %s\n", error
, gpg_strerror(error
));
131 static void usage(const char *pn
, int status
)
133 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
134 "Read a PWMD protocol command from standard input.\n\n"
135 "Usage: pwmc [options] [file]\n"
137 " a url string to connect to (see below)\n"
140 " disable SSH agent use (enabled when SSH_AUTH_SOCK is set)\n"
141 " --identity, -i <filename>\n"
142 " the ssh identity file to use for authentication\n"
143 " --knownhosts, -k <filename>\n"
144 " the ssh knownhosts file to use (~/.ssh/knownhosts)\n"
145 " --ssh-timeout <seconds>\n"
146 " timeout before a remote command fails (0=disabled, default)\n"
147 " --ssh-keepalive <seconds>\n"
148 " interval to send a keepalive to the remote server (0=disabled, default)\n"
151 " --ca-cert <filename>\n"
152 " certificate authority (CA) used to sign the server cert\n"
153 " --client-cert <filename>\n"
154 " client certificate to use for authentication\n"
155 " --client-key <filename>\n"
156 " key file used to protect the client certificate\n"
157 " --tls-priority <string>\n"
158 " compression, cipher and hash algorithm string (SECURE256)\n"
160 " verify the hostname against the server certificate\n"
163 " do not lock the data file upon opening it\n"
164 " --lock-timeout <N>\n"
165 " time in tenths of a second to wait for a locked data file\n"
167 " disable showing of status messages from the server\n"
169 " disable showing of extra messages (implies --no-status)\n"
170 " --name, -n <string>\n"
171 " set the client name\n"
173 " do not require a passphrase when saving a new file\n"
174 " --keygrip <string>\n"
175 " the hex string of the keygrip to save to\n"
176 " --sign-keygrip <string>\n"
177 " the hex string of the keygrip to sign with\n"
178 " --key-file <filename>\n"
179 " obtain the passphrase from the specified filename\n"
180 " --new-key-file <filename>\n"
181 " obtain the passphrase to save with from the specified filename\n"
183 " the number of times to hash the passphrase for a new file\n"
184 " --cipher-iterations <N>\n"
185 " the number of times to encrypt the XML data (N+1)\n"
186 " --timeout <seconds>\n"
187 " pinentry timeout\n"
189 " number of pinentry tries before failing (3)\n"
191 " disable pinentry both remotely and locally\n"
192 " --pinentry <path>\n"
193 " the full path to the pinentry binary (server default)\n"
194 " --ttyname, -y <path>\n"
195 " tty that pinentry will use\n"
196 " --ttytype, -t <string>\n"
197 " pinentry terminal type (default is $TERM)\n"
199 " pinentry display (default is $DISPLAY)\n"
200 " --lc-ctype <string>\n"
201 " locale setting for pinentry\n"
202 " --lc-messages <string>\n"
203 " locale setting for pinentry\n"
204 " --local-pinentry\n"
205 " force using a local pinentry\n"
206 #ifdef HAVE_LIBREADLINE
208 " use a shell like interface to pwmd (allows more than one command)\n"
210 " --output-fd <FD>\n"
211 " redirect command output to the specified file descriptor\n"
212 " --inquire <COMMAND>\n"
213 " the specified command (with any options) uses a server inquire while\n"
214 " command data is read via the inquire file descriptor (stdin)\n"
215 " --inquire-fd <FD>\n"
216 " read inquire data from the specified file descriptor (stdin)\n"
217 " --inquire-file <filename>\n"
218 " read inquire data from the specified filename\n"
219 " --inquire-line, -L <STRING>\n"
220 " the initial line to send (i.e., element path) before the inquire data\n"
221 " --cipher <string>\n"
222 " the cipher to use when saving (see pwmd(1))\n"
224 " send the SAVE command before exiting\n"
225 " --key-params <string>\n"
226 " the key parameters to use when saving a new file (pwmd default)\n"
228 " like --save but always ask for a passphrase\n"
231 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
233 "An optional url may be in the form of:\n"
234 " --url /path/to/socket\n"
235 " --url file://[path/to/socket]\n"
238 " --url ssh[46]://[username@]hostname[:port]\n"
239 " --no-ssh-agent -i identity_file --url ssh[46]://[username@]hostname[:port]\n"
243 " --url tls[46]://hostname[:port] --ca-cert filename --client-cert filename\n"
244 " --client-key filename\n"
250 static gpg_error_t
inquire_cb(void *user
, const char *keyword
, gpg_error_t rc
,
251 char **data
, size_t *size
)
253 struct inquire_s
*inq
= user
;
255 int is_newpassword
= 0;
263 if (!strcmp(keyword
, "PASSPHRASE"))
265 else if (!strcmp(keyword
, "NEW_PASSPHRASE"))
267 #ifdef HAVE_LIBREADLINE
268 else if (!strcmp(keyword
, "KEYPARAM") && !interactive
) {
270 else if (!strcmp(keyword
, "KEYPARAM")) {
272 if (!keyparams
|| !*keyparams
)
273 return gpg_error(GPG_ERR_INV_PARAMETER
);
276 *size
= strlen(keyparams
);
277 return gpg_error(GPG_ERR_EOF
);
280 if ((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
)) {
281 int fd
= open(is_password
? keyfile
: new_keyfile
, O_RDONLY
);
284 fprintf(stderr
, "%s: %s\n", is_newpassword
? new_keyfile
: keyfile
,
286 return gpg_error_from_syserror();
289 rc
= set_inquire(fd
, NULL
, &inq
);
296 fprintf(stderr
, N_("Using keyfile '%s' as %s.\n"),
297 is_newpassword
? new_keyfile
: keyfile
, keyword
);
299 if (!new_keyfile
|| is_newpassword
) {
302 pwmd_socket_type(pwm
, &t
);
303 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
304 if (!no_pinentry
&& t
== PWMD_SOCKET_LOCAL
)
305 pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
308 else if (is_password
&& !keyfile
) {
311 rc
= pwmd_password(pwm
, keyword
, &tmp
, &inq
->len
);
312 if (rc
&& gpg_err_code(rc
) != GPG_ERR_EOF
)
315 pwmd_free(inq
->line
);
319 return gpg_error(GPG_ERR_EOF
);
321 #ifdef HAVE_LIBREADLINE
322 else if (!inq
->header
&& interactive
) {
324 "Press CTRL-D to send the current line. Press twice to end. %s:\n"), keyword
);
329 /* The first part of the command data. */
334 return inq
->fd
== -1 ? gpg_error(GPG_ERR_EOF
) : 0;
337 *size
= read(inq
->fd
, inq
->line
, ASSUAN_LINELENGTH
);
340 return gpg_error(gpg_error_from_syserror());
345 if (((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
))
346 && *size
== inq
->size
)
347 return gpg_error(GPG_ERR_EOF
);
349 return *size
? 0 : gpg_error(GPG_ERR_EOF
);
352 static int status_msg_cb(void *data
, const char *line
)
354 char *p
= strchr(line
, ' ');
356 if (!strcmp(line
, "KEEPALIVE"))
359 if (*line
!= '#' && p
&& strchr(p
, ' ') && *++p
) {
360 char *p1
= strchr(p
, ' ');
361 int a
= strtol(p
, NULL
, 10);
363 if (isdigit(*p
) && p1
) {
364 int b
= strtol(p1
, NULL
, 10);
366 int t
= a
&& b
? a
*100/b
: 0;
368 strncpy(l
, line
, strlen(line
)-strlen(p
)-1);
369 fprintf(stderr
, "\r%s %i/%i %i%%%s", l
, a
, b
, t
, a
== b
? "\n" : "");
375 fprintf(stderr
, "%s\n", line
);
377 #ifdef HAVE_LIBREADLINE
383 static gpg_error_t
process_cmd()
385 return pwmd_process(pwm
);
389 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
)
391 char buf
[LINE_MAX
] = {0}, *p
;
392 struct termios told
, tnew
;
397 if (!isatty(STDIN_FILENO
)) {
398 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
399 return GPG_ERR_ENOTTY
;
403 if (tcgetattr(STDIN_FILENO
, &told
) == -1)
404 return gpg_error_from_syserror();
406 memcpy(&tnew
, &told
, sizeof(struct termios
));
407 tnew
.c_lflag
&= ~(ECHO
);
408 tnew
.c_lflag
|= ICANON
|ECHONL
;
410 if (tcsetattr(STDIN_FILENO
, TCSANOW
, &tnew
) == -1) {
413 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
414 return gpg_error_from_errno(n
);
419 case PWMD_PINENTRY_OPEN
:
420 fprintf(stderr
, N_("Password for %s: "), filename
);
422 case PWMD_PINENTRY_OPEN_FAILED
:
423 fprintf(stderr
, N_("Invalid password. Password for %s: "), filename
);
425 case PWMD_PINENTRY_SAVE
:
426 fprintf(stderr
, N_("New password for %s: "), filename
);
428 case PWMD_PINENTRY_SAVE_CONFIRM
:
429 fprintf(stderr
, N_("Confirm password: "));
435 if ((p
= fgets(buf
, sizeof(buf
), stdin
)) == NULL
) {
436 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
441 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
445 return GPG_ERR_CANCELED
;
448 p
[strlen(p
) - 1] = 0;
451 key
= pwmd_strdup_printf("%s", p
);
452 memset(&buf
, 0, sizeof(buf
));
455 return GPG_ERR_ENOMEM
;
462 static gpg_error_t
knownhost_cb(void *data
, const char *host
, const char *key
,
466 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
);
468 if (no_pinentry
&& !isatty(STDIN_FILENO
)) {
469 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
471 return GPG_ERR_ENOTTY
;
473 else if (no_pinentry
) {
474 for (char *p
= buf
, len
= 0; *p
; p
++, len
++) {
481 while (!isspace(*(--p
)));
488 fprintf(stderr
, "%s\n\n", buf
);
494 fprintf(stderr
, N_("Trust this connection [y/N]: "));
496 rc
= get_password(&result
, PWMD_PINENTRY_CONFIRM
, 1);
501 if (!result
|| !*result
|| *result
== 'n' || *result
== 'N') {
502 if (result
&& *result
)
505 return GPG_ERR_NOT_CONFIRMED
;
508 if ((*result
== 'y' || *result
== 'Y') && *(result
+1) == 0) {
515 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, buf
);
521 return pwmd_getpin(pwm
, NULL
, NULL
, NULL
, PWMD_PINENTRY_CONFIRM
);
525 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
526 static pwmd_socket_t
is_remote_url(const char *str
)
529 return PWMD_SOCKET_LOCAL
;
532 if (strstr(str
, "ssh://") || strstr(str
, "ssh4://")
533 || strstr(str
, "ssh6://"))
534 return PWMD_SOCKET_SSH
;
538 if (strstr(str
, "tls://") || strstr(str
, "tls4://")
539 || strstr(str
, "tls6://"))
540 return PWMD_SOCKET_TLS
;
543 return PWMD_SOCKET_LOCAL
;
547 static char *escape(const char *str
)
550 char *buf
= pwmd_malloc(ASSUAN_LINELENGTH
+1), *b
= buf
;
553 for (p
= str
; *p
; p
++, len
++) {
554 if (len
== ASSUAN_LINELENGTH
)
595 static void free_inquire(struct inquire_s
*inq
)
600 pwmd_free(inq
->line
);
602 if (inq
->fd
!= -1 && inq
->fd
!= STDIN_FILENO
)
608 /* When *result is not NULL it is updated to the new values and not
610 static gpg_error_t
set_inquire(int fd
, const char *line
,
611 struct inquire_s
**result
)
613 struct inquire_s inq
= {0};
614 struct stat st
= {0};
618 if (fstat(fd
, &st
) == -1)
619 return gpg_error_from_syserror();
621 inq
.size
= st
.st_size
;
625 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
627 return GPG_ERR_ENOMEM
;
630 char *s
= escape(line
);
634 return GPG_ERR_ENOMEM
;
637 if (strlen(s
) >= ASSUAN_LINELENGTH
) {
640 return GPG_ERR_LINE_TOO_LONG
;
643 strncpy(inq
.line
, s
, ASSUAN_LINELENGTH
-1);
648 rc
= pwmd_setopt(pwm
, PWMD_OPTION_INQUIRE_TOTAL
,
649 st
.st_size
? st
.st_size
+strlen(inq
.line
) : 0);
656 *result
= pwmd_malloc(sizeof(struct inquire_s
));
658 if ((*result
)->fd
!= -1 && (*result
)->fd
!= STDIN_FILENO
)
659 close((*result
)->fd
);
661 pwmd_free((*result
)->line
);
662 (*result
)->line
= NULL
;
667 memcpy(*result
, &inq
, sizeof(struct inquire_s
));
668 memset(&inq
, 0, sizeof(struct inquire_s
));
672 #ifdef HAVE_LIBREADLINE
673 static int interactive_hook(void)
675 interactive_error
= process_cmd();
677 if (interactive_error
)
678 rl_event_hook
= NULL
;
683 static void print_help()
686 "------------------------------------------------------------------------------\n"
687 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
689 "Type HELP for protocol commands. Type .help for pwmc commands. Press CTRL-D\n"
691 "------------------------------------------------------------------------------\n"
696 static char *parse_arg(const char *src
, char *dst
, size_t len
)
702 for (; s
&& *s
&& *s
!= ' ' && n
< len
; s
++, n
++)
709 static char *parse_opt(char **line
, const char *opt
, gpg_error_t
*rc
)
711 static char result
[ASSUAN_LINELENGTH
] = {0}, *r
= result
;
713 char *s
= strstr(*line
, opt
);
721 size_t rlen
= strlen(opt
);
725 while (*p
&& *p
== ' ') {
730 for (; *p
&& len
< sizeof(result
)-1; p
++, rlen
++) {
731 if (isspace(*p
) && !quote
)
734 if (*p
== '\"' && lastc
!= '\\') {
746 if (len
>= sizeof(result
)-1)
747 *rc
= GPG_ERR_LINE_TOO_LONG
;
751 while (*p
&& *p
== ' ')
761 static gpg_error_t
read_command(const char *line
, char **result
, size_t *len
)
765 char filebuf
[ASSUAN_LINELENGTH
];
766 char *filename
= NULL
;
767 struct inquire_s
*inq
= NULL
;
768 char *p
= (char *)line
;
769 const char *prefix
= parse_opt(&p
, "--prefix", &rc
);
777 while (*p
&& isspace(*p
))
780 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
781 if (filename
&& *filename
) {
782 p
+= strlen(filename
)+1;
784 while (*p
&& isspace(*p
))
793 fprintf(stderr
, N_("Usage: .read [--prefix <string>] <filename> <command> [args]\n"));
794 fprintf(stderr
, N_("Use '\\' to escape special characters in the --prefix (\\t = TAB, \\\" = \")\n"));
798 fd
= open(filename
, O_RDONLY
);
800 return gpg_error_from_syserror();
802 rc
= set_inquire(fd
, prefix
&& *prefix
? prefix
: NULL
, &inq
);
809 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, p
);
814 static gpg_error_t
redir_command(const char *line
)
816 const char *p
= line
;
818 gpg_error_t rc
= GPG_ERR_SYNTAX
;
819 char filebuf
[ASSUAN_LINELENGTH
];
820 char *filename
= NULL
;
821 struct inquire_s
*inq
= NULL
;
825 if (p
&& *p
&& *++p
) {
826 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
827 if (filename
&& *filename
) {
828 p
+= strlen(filename
)+1;
830 while (*p
&& isspace(*p
))
839 fprintf(stderr
, N_("Usage: .redir <filename> <command> [args]\n"));
843 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
845 return gpg_error_from_syserror();
847 #ifdef HAVE_LIBREADLINE
848 rc
= set_inquire(interactive
? STDIN_FILENO
: inquirefd
, NULL
, &inq
);
850 rc
= set_inquire(inquirefd
, NULL
, &inq
);
857 rc
= parse_dotcommand(p
, &result
, &len
, inq
);
858 if (!rc
&& result
&& len
--) { // null byte which is always appended
859 if (write(fd
, result
, len
) != len
)
860 rc
= GPG_ERR_TOO_SHORT
;
869 static gpg_error_t
help_command(const char *line
)
871 fprintf(stderr
, N_("Type HELP for protocol commands. Available pwmc commands:\n\n"
872 " .redir <filename> <command>\n"
873 " redirect the output of a command to the specified file\n"
875 " .open <filename>\n"
876 " open the specified filename losing any changes to the current one\n"
878 " .read [--prefix <string>] <filename> <command> [args]\n"
879 " obtain data from the specified filename for an inquire command\n"
881 " .set help | <name> [<value>]\n"
882 " set option <name> to <value>\n"
885 " this help text\n"));
889 static gpg_error_t
open_command(const char *line
)
891 struct inquire_s
*inq
= NULL
;
892 const char *filename
= line
;
895 while (filename
&& isspace(*filename
))
898 if (!filename
|| !*filename
) {
899 fprintf(stderr
, N_("Usage: .open <filename>\n"));
900 return GPG_ERR_SYNTAX
;
903 if (interactive
|| !quiet
)
904 fprintf(stderr
, N_("Opening data file \"%s\" ...\n"), filename
);
906 #ifdef HAVE_LIBREADLINE
907 rc
= set_inquire(interactive
? STDIN_FILENO
: -1, NULL
, &inq
);
909 rc
= set_inquire(-1, NULL
, &inq
);
915 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
917 #ifdef HAVE_LIBREADLINE
918 // For safety. This file may be a different one. Make the user set it
921 pwmd_free(new_keyfile
);
926 rc
= pwmd_open(pwm
, filename
, inquire_cb
, inq
);
931 static gpg_error_t
set_command(const char *line
)
934 char name
[256] = {0};
935 char value
[512] = {0};
938 const char *p
= line
;
940 while (p
&& *p
&& isspace(*p
))
943 namep
= parse_arg(p
, name
, sizeof(name
));
944 if (!namep
|| !*namep
) {
945 fprintf(stderr
, N_("Usage: .set help | <name> [<value>]\n"));
946 return GPG_ERR_SYNTAX
;
950 while (p
&& *p
&& isspace(*p
))
953 valuep
= parse_arg(p
, value
, sizeof(value
));
955 if (!strcmp(name
, "keyfile") || !strcmp(name
, "new-keyfile")) {
956 int is_newkeyfile
= 1;
958 if (!strcmp(name
, "keyfile"))
962 pwmd_free(new_keyfile
);
972 new_keyfile
= pwmd_strdup(value
);
974 keyfile
= pwmd_strdup(value
);
976 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "AGENT option pinentry-mode=loopback");
978 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 1);
979 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
982 else if (!local_pin
&& !no_pinentry
) {
985 pwmd_socket_type(pwm
, &t
);
986 if (t
== PWMD_SOCKET_LOCAL
) {
987 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
988 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
992 else if (!strcmp(name
, "help")) {
994 "Set a libpwmd or pwmc option. The option name and optional value is space\n"
995 "delimited. When no value is specified the option is unset.\n\n"
996 "keyfile [<filename>]\n"
997 " set or unset the keyfile to be used when a passphrase is required\n"
999 "new-keyfile [<filename>]\n"
1000 " set or unset the keyfile to be used when a new passphrase is required\n"
1004 rc
= GPG_ERR_UNKNOWN_OPTION
;
1009 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
1010 size_t *len
, struct inquire_s
*inq
)
1012 const char *p
= line
;
1015 if (!strncmp(p
, ".read", 5))
1016 rc
= read_command(p
+5, result
, len
);
1017 else if (!strncmp(p
, ".redir", 6))
1018 rc
= redir_command(p
+6);
1019 else if (!strncmp(p
, ".help", 5))
1020 rc
= help_command(p
+5);
1021 else if (!strncmp(p
, ".open", 5))
1022 rc
= open_command(p
+5);
1023 else if (!strncmp(p
, ".set", 4))
1024 rc
= set_command(p
+4);
1026 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, line
);
1031 #ifdef HAVE_LIBREADLINE
1032 static gpg_error_t
do_interactive()
1035 struct inquire_s
*inq
= NULL
;
1042 rc
= set_inquire(STDIN_FILENO
, NULL
, &inq
);
1046 fprintf(stderr
, N_("WARNING: interactive mode doesn't use secure memory!\n"));
1048 rl_event_hook
= &interactive_hook
;
1049 rl_set_keyboard_input_timeout(100000);
1053 char *result
= NULL
;
1056 line
= readline("pwm> ");
1057 if (interactive_error
) {
1058 rc
= interactive_error
;
1067 if (gpg_err_code(rc
) != GPG_ERR_CANCELED
&&
1068 gpg_err_code(rc
) != GPG_ERR_EOF
)
1069 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1078 #ifdef HAVE_READLINE_HISTORY
1082 rc
= parse_dotcommand(line
, &result
, &len
, inq
);
1085 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1086 else if (result
&& len
)
1087 printf("%s%s", result
, result
[len
-1] != '\n' ? "\n" : "");
1097 static char *itoa(long long int n
)
1099 static char buf
[64];
1101 snprintf(buf
, sizeof(buf
), "%llu", n
);
1105 static gpg_error_t
finalize()
1108 #ifdef HAVE_LIBREADLINE
1111 if (!force_save
&& interactive
) {
1115 fprintf(stderr
, "\n");
1118 fprintf(stderr
, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
1119 p
= fgets(buf
, sizeof(buf
), stdin
);
1131 return GPG_ERR_CANCELED
;
1135 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1136 "CLEARCACHE %s", filename
);
1151 } while (!finished
);
1155 if (save
&& !filename
) {
1156 fprintf(stderr
, N_("No filename was specified on the command line. Aborting.\n"));
1157 return GPG_ERR_CANCELED
;
1160 if (save
&& filename
) {
1161 char *args
= pwmd_strdup_printf("%s%s %s%s %s %s%s %s%s --s2k-count=%lu",
1162 keygrip
? "--keygrip=" : "", keygrip
? keygrip
: "",
1163 sign_keygrip
? "--sign-keygrip=" : "", sign_keygrip
? sign_keygrip
: "",
1164 no_passphrase
? "--no-passphrase" : "",
1165 cipher
? "--cipher=" : "", cipher
? cipher
: "",
1166 iterations_arg
? "--cipher-iterations=" : "", iterations_arg
? itoa(iterations
) : "",
1169 if (!quiet
|| interactive
) {
1170 fprintf(stderr
, "\n");
1171 fprintf(stderr
, N_("Saving changes ...\n"));
1174 rc
= pwmd_save(pwm
, args
, inquire_cb
, NULL
);
1178 no_passphrase
= 0; // reset to avoid usage error on the next SAVE
1181 #ifdef HAVE_LIBREADLINE
1183 return rc
? rc
: quit
? 0 : GPG_ERR_CANCELED
;
1189 int main(int argc
, char *argv
[])
1194 char command
[ASSUAN_LINELENGTH
], *p
= NULL
;
1195 char *result
= NULL
;
1197 char *pinentry_path
= NULL
;
1198 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
1200 int outfd
= STDOUT_FILENO
;
1201 FILE *outfp
= stdout
;
1202 FILE *inquirefp
= stdin
;
1203 int show_status
= 1;
1204 char *clientname
= "pwmc";
1205 char *inquire
= NULL
;
1206 char *inquire_line
= NULL
;
1209 int use_ssh_agent
= 1;
1210 long ssh_timeout
= 0;
1211 int ssh_keepalive
= 0;
1212 char *knownhosts
= NULL
;
1213 char *identity
= NULL
;
1216 char *cacert
= NULL
;
1217 char *clientcert
= NULL
;
1218 char *clientkey
= NULL
;
1222 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1223 pwmd_socket_t socktype
;
1225 int lock_on_open
= 1;
1226 long lock_timeout
= 0;
1228 /* The order is important. */
1231 OPT_USE_SSH_AGENT
, OPT_IDENTITY
, OPT_KNOWNHOSTS
, OPT_SSH_TIMEOUT
,
1235 OPT_CACERT
, OPT_CLIENTCERT
, OPT_CLIENTKEY
, OPT_PRIORITY
, OPT_VERIFY
,
1237 OPT_URL
, OPT_LOCAL
, OPT_FORCE_SAVE
, OPT_TTYNAME
, OPT_TTYTYPE
,
1238 OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
, OPT_TIMEOUT
, OPT_TRIES
,
1239 OPT_PINENTRY
, OPT_KEYFILE
, OPT_NEW_KEYFILE
, OPT_NOLOCK
,
1240 OPT_LOCK_TIMEOUT
, OPT_SAVE
, OPT_OUTPUT_FD
, OPT_INQUIRE
,
1241 OPT_INQUIRE_FD
, OPT_INQUIRE_FILE
, OPT_INQUIRE_LINE
, OPT_NO_STATUS
,
1242 OPT_NAME
, OPT_VERSION
, OPT_HELP
, OPT_KEYGRIP
, OPT_SIGN_KEYGRIP
,
1243 OPT_NOPASSPHRASE
, OPT_CIPHER
, OPT_KEYPARAMS
, OPT_NO_PINENTRY
,
1244 OPT_S2K_COUNT
, OPT_ITERATIONS
, OPT_QUIET
,
1245 #ifdef HAVE_LIBREADLINE
1249 const struct option long_opts
[] = {
1251 { "no-ssh-agent", 0, 0, 0 },
1252 { "identity", 1, 0, 'i' },
1253 { "knownhosts", 1, 0, 'k' },
1254 { "ssh-timeout", 1, 0, 0 },
1255 { "ssh-keepalive", 1, 0, 0 },
1258 { "ca-cert", 1, 0, 0 },
1259 { "client-cert", 1, 0, 0 },
1260 { "client-key", 1, 0, 0 },
1261 { "tls-priority", 1, 0, 0 },
1262 { "tls-verify", 0, 0, 0 },
1265 { "local-pinentry", 0, 0 },
1266 { "force-save", 0, 0 },
1267 { "ttyname", 1, 0, 'y' },
1268 { "ttytype", 1, 0, 't' },
1269 { "display", 1, 0, 'd' },
1270 { "lc-ctype", 1, 0, 0 },
1271 { "lc-messages", 1, 0, 0 },
1272 { "timeout", 1, 0, 0 },
1273 { "tries", 1, 0, 0 },
1274 { "pinentry", 1, 0, 0 },
1275 { "key-file", 1, 0, 0 },
1276 { "new-key-file", 1, 0, 0 },
1277 { "no-lock", 0, 0, 0 },
1278 { "lock-timeout", 1, 0, 0 },
1279 { "save", 0, 0, 'S' },
1280 { "output-fd", 1, 0, 0 },
1281 { "inquire", 1, 0, 0 },
1282 { "inquire-fd", 1, 0, 0 },
1283 { "inquire-file", 1, 0, 0 },
1284 { "inquire-line", 1, 0, 'L' },
1285 { "no-status", 0, 0, 0 },
1286 { "name", 1, 0, 'n' },
1287 { "version", 0, 0, 0 },
1288 { "help", 0, 0, 0 },
1289 { "keygrip", 1, 0, 0 },
1290 { "sign-keygrip", 1, 0, 0 },
1291 { "no-passphrase", 0, 0, 0 },
1292 { "cipher", 1, 0, 0 },
1293 { "key-params", 1, 0, 0 },
1294 { "no-pinentry", 0, 0, 0 },
1295 { "s2k-count", 1, 0, 0 },
1296 { "cipher-iterations", 1, 0, 0 },
1297 { "quiet", 0, 0, 0 },
1298 #ifdef HAVE_LIBREADLINE
1299 { "interactive", 0, 0 },
1304 const char *optstring
= "L:y:t:d:P:I:Sn:i:k:";
1306 const char *optstring
= "L:y:t:d:P:I:Sn:";
1311 setlocale(LC_ALL
, "");
1312 bindtextdomain("libpwmd", LOCALEDIR
);
1315 #ifdef HAVE_LIBREADLINE
1316 if (!strcmp(basename(argv
[0]), "pwmsh"))
1320 tries
= DEFAULT_PIN_TRIES
;
1321 inquirefd
= STDIN_FILENO
;
1323 while ((opt
= getopt_long(argc
, argv
, optstring
, long_opts
, &opt_index
)) != -1) {
1325 /* Handle long options without a short option part. */
1327 switch (opt_index
) {
1329 case OPT_USE_SSH_AGENT
:
1332 case OPT_SSH_TIMEOUT
:
1333 ssh_timeout
= strtol(optarg
, &p
, 10);
1335 case OPT_SSH_KEEPALIVE
:
1336 ssh_keepalive
= strtol(optarg
, &p
, 10);
1343 case OPT_CLIENTCERT
:
1344 clientcert
= optarg
;
1360 keyfile
= pwmd_strdup(optarg
);
1362 case OPT_NEW_KEYFILE
:
1363 new_keyfile
= pwmd_strdup(optarg
);
1368 case OPT_LOCK_TIMEOUT
:
1369 lock_timeout
= strtol(optarg
, &p
, 10);
1377 case OPT_FORCE_SAVE
:
1378 save
= force_save
= 1;
1381 lcctype
= pwmd_strdup(optarg
);
1383 case OPT_LC_MESSAGES
:
1384 lcmessages
= pwmd_strdup(optarg
);
1387 timeout
= strtol(optarg
, &p
, 10);
1390 tries
= strtol(optarg
, &p
, 10);
1395 case OPT_INQUIRE_FD
:
1396 inquirefd
= strtol(optarg
, &p
, 10);
1398 inquirefp
= fdopen(inquirefd
, "r");
1400 pwmd_free(password
);
1401 err(EXIT_FAILURE
, "%i", inquirefd
);
1405 case OPT_INQUIRE_FILE
:
1406 inquirefd
= open(optarg
, O_RDONLY
);
1407 if (!inquirefd
== -1) {
1408 pwmd_free(password
);
1409 err(EXIT_FAILURE
, "%s", optarg
);
1411 inquirefp
= fdopen(inquirefd
, "r");
1414 outfd
= strtol(optarg
, &p
, 10);
1416 outfp
= fdopen(outfd
, "w");
1418 pwmd_free(password
);
1419 err(EXIT_FAILURE
, "%i", outfd
);
1427 pwmd_free(password
);
1428 printf("%s (pwmc)\n\n"
1429 "Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012\n"
1431 "Released under the terms of the GPL v2. Use at your own risk.\n\n"
1432 "Compile-time features:\n"
1443 #ifdef WITH_PINENTRY
1458 #ifdef HAVE_LIBREADLINE
1464 , PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1467 pinentry_path
= optarg
;
1470 usage(argv
[0], EXIT_SUCCESS
);
1474 case OPT_SIGN_KEYGRIP
:
1475 sign_keygrip
= optarg
;
1478 s2k_count
= strtoul(optarg
, &p
, 10);
1480 case OPT_ITERATIONS
:
1481 iterations
= strtoull(optarg
, &p
, 10);
1488 case OPT_NOPASSPHRASE
:
1494 case OPT_NO_PINENTRY
:
1497 #ifdef HAVE_LIBREADLINE
1498 case OPT_INTERACTIVE
:
1503 usage(argv
[0], EXIT_FAILURE
);
1507 fprintf(stderr
, N_("%s: invalid argument for option '--%s'\n"),
1508 argv
[0], long_opts
[opt_index
].name
);
1509 usage(argv
[0], EXIT_FAILURE
);
1518 knownhosts
= optarg
;
1522 inquire_line
= optarg
;
1537 clientname
= optarg
;
1540 pwmd_free(password
);
1541 usage(argv
[0], EXIT_FAILURE
);
1545 #ifdef HAVE_LIBREADLINE
1546 if (interactive
&& !isatty(STDIN_FILENO
)) {
1547 pwmd_free(password
);
1548 usage(argv
[0], EXIT_FAILURE
);
1550 else if (isatty(STDIN_FILENO
) && !inquire
&& !inquire_line
)
1554 filename
= argv
[optind
];
1556 gnutls_global_set_mem_functions(pwmd_malloc
, pwmd_malloc
, NULL
,
1557 pwmd_realloc
, pwmd_free
);
1560 rc
= pwmd_new(clientname
, &pwm
);
1564 pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
1566 fprintf(stderr
, N_("Connecting ...\n"));
1568 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1569 socktype
= is_remote_url(url
);
1570 if (socktype
!= PWMD_SOCKET_LOCAL
) {
1572 if (socktype
== PWMD_SOCKET_SSH
) {
1574 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_CB
, knownhost_cb
);
1578 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_DATA
, clientname
);
1582 if (!getenv("SSH_AUTH_SOCK") || identity
)
1585 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_AGENT
, use_ssh_agent
);
1589 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_TIMEOUT
, ssh_timeout
);
1593 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_KEEPALIVE
, ssh_keepalive
);
1597 rc
= pwmd_connect(pwm
, url
, identity
, knownhosts
);
1602 rc
= pwmd_setopt(pwm
, PWMD_OPTION_TLS_VERIFY
, tls_verify
);
1606 rc
= pwmd_connect(pwm
, url
, clientcert
, clientkey
, cacert
, prio
);
1611 rc
= pwmd_connect(pwm
, url
);
1613 rc
= pwmd_connect(pwm
, url
);
1619 fprintf(stderr
, N_("Connected.\n"));
1623 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1624 "OPTION LOCK-TIMEOUT=%li", lock_timeout
);
1630 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCK_ON_OPEN
, 1);
1635 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, NULL
);
1640 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
1645 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, no_pinentry
);
1649 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCAL_PINENTRY
,
1650 (local_pin
|| keyfile
|| new_keyfile
));
1654 if (pinentry_path
) {
1655 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
1661 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
1667 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
1673 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
1679 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
1685 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
1692 rc
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
1698 rc
= open_command(filename
);
1703 #ifdef HAVE_LIBREADLINE
1708 rc
= do_interactive();
1714 struct inquire_s
*inq
= NULL
;
1716 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1718 rc
= pwmd_command(pwm
, &result
, &len
, inquire_cb
, inq
, inquire
);
1724 fcntl(STDIN_FILENO
, F_SETFL
, O_NONBLOCK
);
1733 n
= read(STDIN_FILENO
, command
, sizeof(command
));
1735 if (errno
== EAGAIN
) {
1740 rc
= gpg_error_from_errno(errno
);
1751 if (!p
|| !*p
|| !strcasecmp(p
, "BYE"))
1755 struct inquire_s
*inq
= NULL
;
1756 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1758 rc
= parse_dotcommand(command
, &result
, &len
, inq
);
1767 fwrite(result
, 1, result
[len
-1] == 0 ? len
-1 : len
, outfp
);
1772 pwmd_free(password
);
1777 #ifdef HAVE_LIBREADLINE
1780 memset(command
, 0, sizeof(command
));
1783 pwmd_free(new_keyfile
);
1789 if (connected
&& !quiet
)
1790 fprintf(stderr
, N_("Connection closed.\n"));
1792 exit(rc
? EXIT_FAILURE
: EXIT_SUCCESS
);