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 #ifdef HAVE_LIBREADLINE
904 if (interactive
|| !quiet
)
908 fprintf(stderr
, N_("Opening data file \"%s\" ...\n"), filename
);
910 #ifdef HAVE_LIBREADLINE
911 rc
= set_inquire(interactive
? STDIN_FILENO
: -1, NULL
, &inq
);
913 rc
= set_inquire(-1, NULL
, &inq
);
919 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
921 #ifdef HAVE_LIBREADLINE
922 // For safety. This file may be a different one. Make the user set it
925 pwmd_free(new_keyfile
);
930 rc
= pwmd_open(pwm
, filename
, inquire_cb
, inq
);
935 static gpg_error_t
set_command(const char *line
)
938 char name
[256] = {0};
939 char value
[512] = {0};
942 const char *p
= line
;
944 while (p
&& *p
&& isspace(*p
))
947 namep
= parse_arg(p
, name
, sizeof(name
));
948 if (!namep
|| !*namep
) {
949 fprintf(stderr
, N_("Usage: .set help | <name> [<value>]\n"));
950 return GPG_ERR_SYNTAX
;
954 while (p
&& *p
&& isspace(*p
))
957 valuep
= parse_arg(p
, value
, sizeof(value
));
959 if (!strcmp(name
, "keyfile") || !strcmp(name
, "new-keyfile")) {
960 int is_newkeyfile
= 1;
962 if (!strcmp(name
, "keyfile"))
966 pwmd_free(new_keyfile
);
976 new_keyfile
= pwmd_strdup(value
);
978 keyfile
= pwmd_strdup(value
);
980 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "AGENT option pinentry-mode=loopback");
982 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 1);
983 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
986 else if (!local_pin
&& !no_pinentry
) {
989 pwmd_socket_type(pwm
, &t
);
990 if (t
== PWMD_SOCKET_LOCAL
) {
991 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
992 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
996 else if (!strcmp(name
, "help")) {
998 "Set a libpwmd or pwmc option. The option name and optional value is space\n"
999 "delimited. When no value is specified the option is unset.\n\n"
1000 "keyfile [<filename>]\n"
1001 " set or unset the keyfile to be used when a passphrase is required\n"
1003 "new-keyfile [<filename>]\n"
1004 " set or unset the keyfile to be used when a new passphrase is required\n"
1008 rc
= GPG_ERR_UNKNOWN_OPTION
;
1013 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
1014 size_t *len
, struct inquire_s
*inq
)
1016 const char *p
= line
;
1019 if (!strncmp(p
, ".read", 5))
1020 rc
= read_command(p
+5, result
, len
);
1021 else if (!strncmp(p
, ".redir", 6))
1022 rc
= redir_command(p
+6);
1023 else if (!strncmp(p
, ".help", 5))
1024 rc
= help_command(p
+5);
1025 else if (!strncmp(p
, ".open", 5))
1026 rc
= open_command(p
+5);
1027 else if (!strncmp(p
, ".set", 4))
1028 rc
= set_command(p
+4);
1030 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, line
);
1035 #ifdef HAVE_LIBREADLINE
1036 static gpg_error_t
do_interactive()
1039 struct inquire_s
*inq
= NULL
;
1046 rc
= set_inquire(STDIN_FILENO
, NULL
, &inq
);
1050 fprintf(stderr
, N_("WARNING: interactive mode doesn't use secure memory!\n"));
1052 rl_event_hook
= &interactive_hook
;
1053 rl_set_keyboard_input_timeout(100000);
1057 char *result
= NULL
;
1060 line
= readline("pwm> ");
1061 if (interactive_error
) {
1062 rc
= interactive_error
;
1071 if (gpg_err_code(rc
) != GPG_ERR_CANCELED
&&
1072 gpg_err_code(rc
) != GPG_ERR_EOF
)
1073 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1082 #ifdef HAVE_READLINE_HISTORY
1086 rc
= parse_dotcommand(line
, &result
, &len
, inq
);
1089 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1090 else if (result
&& len
)
1091 printf("%s%s", result
, result
[len
-1] != '\n' ? "\n" : "");
1101 static char *itoa(long long int n
)
1103 static char buf
[64];
1105 snprintf(buf
, sizeof(buf
), "%llu", n
);
1109 static gpg_error_t
finalize()
1112 #ifdef HAVE_LIBREADLINE
1115 if (!force_save
&& interactive
) {
1119 fprintf(stderr
, "\n");
1122 fprintf(stderr
, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
1123 p
= fgets(buf
, sizeof(buf
), stdin
);
1135 return GPG_ERR_CANCELED
;
1139 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1140 "CLEARCACHE %s", filename
);
1155 } while (!finished
);
1159 if (save
&& !filename
) {
1160 fprintf(stderr
, N_("No filename was specified on the command line. Aborting.\n"));
1161 return GPG_ERR_CANCELED
;
1164 if (save
&& filename
) {
1165 char *args
= pwmd_strdup_printf("%s%s %s%s %s %s%s %s%s --s2k-count=%lu",
1166 keygrip
? "--keygrip=" : "", keygrip
? keygrip
: "",
1167 sign_keygrip
? "--sign-keygrip=" : "", sign_keygrip
? sign_keygrip
: "",
1168 no_passphrase
? "--no-passphrase" : "",
1169 cipher
? "--cipher=" : "", cipher
? cipher
: "",
1170 iterations_arg
? "--cipher-iterations=" : "", iterations_arg
? itoa(iterations
) : "",
1173 #ifdef HAVE_LIBREADLINE
1174 if (!quiet
|| interactive
) {
1178 fprintf(stderr
, "\n");
1179 fprintf(stderr
, N_("Saving changes ...\n"));
1182 rc
= pwmd_save(pwm
, args
, inquire_cb
, NULL
);
1186 no_passphrase
= 0; // reset to avoid usage error on the next SAVE
1189 #ifdef HAVE_LIBREADLINE
1191 return rc
? rc
: quit
? 0 : GPG_ERR_CANCELED
;
1197 int main(int argc
, char *argv
[])
1202 char command
[ASSUAN_LINELENGTH
], *p
= NULL
;
1203 char *result
= NULL
;
1205 char *pinentry_path
= NULL
;
1206 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
1208 int outfd
= STDOUT_FILENO
;
1209 FILE *outfp
= stdout
;
1210 FILE *inquirefp
= stdin
;
1211 int show_status
= 1;
1212 char *clientname
= "pwmc";
1213 char *inquire
= NULL
;
1214 char *inquire_line
= NULL
;
1217 int use_ssh_agent
= 1;
1218 long ssh_timeout
= 0;
1219 int ssh_keepalive
= 0;
1220 char *knownhosts
= NULL
;
1221 char *identity
= NULL
;
1224 char *cacert
= NULL
;
1225 char *clientcert
= NULL
;
1226 char *clientkey
= NULL
;
1230 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1231 pwmd_socket_t socktype
;
1233 int lock_on_open
= 1;
1234 long lock_timeout
= 0;
1236 /* The order is important. */
1239 OPT_USE_SSH_AGENT
, OPT_IDENTITY
, OPT_KNOWNHOSTS
, OPT_SSH_TIMEOUT
,
1243 OPT_CACERT
, OPT_CLIENTCERT
, OPT_CLIENTKEY
, OPT_PRIORITY
, OPT_VERIFY
,
1245 OPT_URL
, OPT_LOCAL
, OPT_FORCE_SAVE
, OPT_TTYNAME
, OPT_TTYTYPE
,
1246 OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
, OPT_TIMEOUT
, OPT_TRIES
,
1247 OPT_PINENTRY
, OPT_KEYFILE
, OPT_NEW_KEYFILE
, OPT_NOLOCK
,
1248 OPT_LOCK_TIMEOUT
, OPT_SAVE
, OPT_OUTPUT_FD
, OPT_INQUIRE
,
1249 OPT_INQUIRE_FD
, OPT_INQUIRE_FILE
, OPT_INQUIRE_LINE
, OPT_NO_STATUS
,
1250 OPT_NAME
, OPT_VERSION
, OPT_HELP
, OPT_KEYGRIP
, OPT_SIGN_KEYGRIP
,
1251 OPT_NOPASSPHRASE
, OPT_CIPHER
, OPT_KEYPARAMS
, OPT_NO_PINENTRY
,
1252 OPT_S2K_COUNT
, OPT_ITERATIONS
, OPT_QUIET
,
1253 #ifdef HAVE_LIBREADLINE
1257 const struct option long_opts
[] = {
1259 { "no-ssh-agent", 0, 0, 0 },
1260 { "identity", 1, 0, 'i' },
1261 { "knownhosts", 1, 0, 'k' },
1262 { "ssh-timeout", 1, 0, 0 },
1263 { "ssh-keepalive", 1, 0, 0 },
1266 { "ca-cert", 1, 0, 0 },
1267 { "client-cert", 1, 0, 0 },
1268 { "client-key", 1, 0, 0 },
1269 { "tls-priority", 1, 0, 0 },
1270 { "tls-verify", 0, 0, 0 },
1273 { "local-pinentry", 0, 0 },
1274 { "force-save", 0, 0 },
1275 { "ttyname", 1, 0, 'y' },
1276 { "ttytype", 1, 0, 't' },
1277 { "display", 1, 0, 'd' },
1278 { "lc-ctype", 1, 0, 0 },
1279 { "lc-messages", 1, 0, 0 },
1280 { "timeout", 1, 0, 0 },
1281 { "tries", 1, 0, 0 },
1282 { "pinentry", 1, 0, 0 },
1283 { "key-file", 1, 0, 0 },
1284 { "new-key-file", 1, 0, 0 },
1285 { "no-lock", 0, 0, 0 },
1286 { "lock-timeout", 1, 0, 0 },
1287 { "save", 0, 0, 'S' },
1288 { "output-fd", 1, 0, 0 },
1289 { "inquire", 1, 0, 0 },
1290 { "inquire-fd", 1, 0, 0 },
1291 { "inquire-file", 1, 0, 0 },
1292 { "inquire-line", 1, 0, 'L' },
1293 { "no-status", 0, 0, 0 },
1294 { "name", 1, 0, 'n' },
1295 { "version", 0, 0, 0 },
1296 { "help", 0, 0, 0 },
1297 { "keygrip", 1, 0, 0 },
1298 { "sign-keygrip", 1, 0, 0 },
1299 { "no-passphrase", 0, 0, 0 },
1300 { "cipher", 1, 0, 0 },
1301 { "key-params", 1, 0, 0 },
1302 { "no-pinentry", 0, 0, 0 },
1303 { "s2k-count", 1, 0, 0 },
1304 { "cipher-iterations", 1, 0, 0 },
1305 { "quiet", 0, 0, 0 },
1306 #ifdef HAVE_LIBREADLINE
1307 { "interactive", 0, 0 },
1312 const char *optstring
= "L:y:t:d:P:I:Sn:i:k:";
1314 const char *optstring
= "L:y:t:d:P:I:Sn:";
1319 setlocale(LC_ALL
, "");
1320 bindtextdomain("libpwmd", LOCALEDIR
);
1323 #ifdef HAVE_LIBREADLINE
1324 if (!strcmp(basename(argv
[0]), "pwmsh"))
1328 tries
= DEFAULT_PIN_TRIES
;
1329 inquirefd
= STDIN_FILENO
;
1331 while ((opt
= getopt_long(argc
, argv
, optstring
, long_opts
, &opt_index
)) != -1) {
1333 /* Handle long options without a short option part. */
1335 switch (opt_index
) {
1337 case OPT_USE_SSH_AGENT
:
1340 case OPT_SSH_TIMEOUT
:
1341 ssh_timeout
= strtol(optarg
, &p
, 10);
1343 case OPT_SSH_KEEPALIVE
:
1344 ssh_keepalive
= strtol(optarg
, &p
, 10);
1351 case OPT_CLIENTCERT
:
1352 clientcert
= optarg
;
1368 keyfile
= pwmd_strdup(optarg
);
1370 case OPT_NEW_KEYFILE
:
1371 new_keyfile
= pwmd_strdup(optarg
);
1376 case OPT_LOCK_TIMEOUT
:
1377 lock_timeout
= strtol(optarg
, &p
, 10);
1385 case OPT_FORCE_SAVE
:
1386 save
= force_save
= 1;
1389 lcctype
= pwmd_strdup(optarg
);
1391 case OPT_LC_MESSAGES
:
1392 lcmessages
= pwmd_strdup(optarg
);
1395 timeout
= strtol(optarg
, &p
, 10);
1398 tries
= strtol(optarg
, &p
, 10);
1403 case OPT_INQUIRE_FD
:
1404 inquirefd
= strtol(optarg
, &p
, 10);
1406 inquirefp
= fdopen(inquirefd
, "r");
1408 pwmd_free(password
);
1409 err(EXIT_FAILURE
, "%i", inquirefd
);
1413 case OPT_INQUIRE_FILE
:
1414 inquirefd
= open(optarg
, O_RDONLY
);
1415 if (!inquirefd
== -1) {
1416 pwmd_free(password
);
1417 err(EXIT_FAILURE
, "%s", optarg
);
1419 inquirefp
= fdopen(inquirefd
, "r");
1422 outfd
= strtol(optarg
, &p
, 10);
1424 outfp
= fdopen(outfd
, "w");
1426 pwmd_free(password
);
1427 err(EXIT_FAILURE
, "%i", outfd
);
1435 pwmd_free(password
);
1436 printf("%s (pwmc)\n\n"
1437 "Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012\n"
1439 "Released under the terms of the GPL v2. Use at your own risk.\n\n"
1440 "Compile-time features:\n"
1451 #ifdef WITH_PINENTRY
1466 #ifdef HAVE_LIBREADLINE
1472 , PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1475 pinentry_path
= optarg
;
1478 usage(argv
[0], EXIT_SUCCESS
);
1482 case OPT_SIGN_KEYGRIP
:
1483 sign_keygrip
= optarg
;
1486 s2k_count
= strtoul(optarg
, &p
, 10);
1488 case OPT_ITERATIONS
:
1489 iterations
= strtoull(optarg
, &p
, 10);
1496 case OPT_NOPASSPHRASE
:
1502 case OPT_NO_PINENTRY
:
1505 #ifdef HAVE_LIBREADLINE
1506 case OPT_INTERACTIVE
:
1511 usage(argv
[0], EXIT_FAILURE
);
1515 fprintf(stderr
, N_("%s: invalid argument for option '--%s'\n"),
1516 argv
[0], long_opts
[opt_index
].name
);
1517 usage(argv
[0], EXIT_FAILURE
);
1526 knownhosts
= optarg
;
1530 inquire_line
= optarg
;
1545 clientname
= optarg
;
1548 pwmd_free(password
);
1549 usage(argv
[0], EXIT_FAILURE
);
1553 #ifdef HAVE_LIBREADLINE
1554 if (interactive
&& !isatty(STDIN_FILENO
)) {
1555 pwmd_free(password
);
1556 usage(argv
[0], EXIT_FAILURE
);
1558 else if (isatty(STDIN_FILENO
) && !inquire
&& !inquire_line
)
1562 filename
= argv
[optind
];
1564 gnutls_global_set_mem_functions(pwmd_malloc
, pwmd_malloc
, NULL
,
1565 pwmd_realloc
, pwmd_free
);
1568 rc
= pwmd_new(clientname
, &pwm
);
1572 pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
1574 fprintf(stderr
, N_("Connecting ...\n"));
1576 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1577 socktype
= is_remote_url(url
);
1578 if (socktype
!= PWMD_SOCKET_LOCAL
) {
1580 if (socktype
== PWMD_SOCKET_SSH
) {
1582 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_CB
, knownhost_cb
);
1586 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_DATA
, clientname
);
1590 if (!getenv("SSH_AUTH_SOCK") || identity
)
1593 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_AGENT
, use_ssh_agent
);
1597 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_TIMEOUT
, ssh_timeout
);
1601 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_KEEPALIVE
, ssh_keepalive
);
1605 rc
= pwmd_connect(pwm
, url
, identity
, knownhosts
);
1610 rc
= pwmd_setopt(pwm
, PWMD_OPTION_TLS_VERIFY
, tls_verify
);
1614 rc
= pwmd_connect(pwm
, url
, clientcert
, clientkey
, cacert
, prio
);
1619 rc
= pwmd_connect(pwm
, url
);
1621 rc
= pwmd_connect(pwm
, url
);
1627 fprintf(stderr
, N_("Connected.\n"));
1631 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1632 "OPTION LOCK-TIMEOUT=%li", lock_timeout
);
1638 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCK_ON_OPEN
, 1);
1643 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, NULL
);
1648 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
1653 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, no_pinentry
);
1657 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCAL_PINENTRY
,
1658 (local_pin
|| keyfile
|| new_keyfile
));
1662 if (pinentry_path
) {
1663 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
1669 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
1675 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
1681 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
1687 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
1693 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
1700 rc
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
1706 rc
= open_command(filename
);
1711 #ifdef HAVE_LIBREADLINE
1716 rc
= do_interactive();
1722 struct inquire_s
*inq
= NULL
;
1724 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1726 rc
= pwmd_command(pwm
, &result
, &len
, inquire_cb
, inq
, inquire
);
1732 fcntl(STDIN_FILENO
, F_SETFL
, O_NONBLOCK
);
1741 n
= read(STDIN_FILENO
, command
, sizeof(command
));
1743 if (errno
== EAGAIN
) {
1748 rc
= gpg_error_from_errno(errno
);
1759 if (!p
|| !*p
|| !strcasecmp(p
, "BYE"))
1763 struct inquire_s
*inq
= NULL
;
1764 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1766 rc
= parse_dotcommand(command
, &result
, &len
, inq
);
1775 fwrite(result
, 1, result
[len
-1] == 0 ? len
-1 : len
, outfp
);
1780 pwmd_free(password
);
1785 #ifdef HAVE_LIBREADLINE
1788 memset(command
, 0, sizeof(command
));
1791 pwmd_free(new_keyfile
);
1797 if (connected
&& !quiet
)
1798 fprintf(stderr
, N_("Connection closed.\n"));
1800 exit(rc
? EXIT_FAILURE
: EXIT_SUCCESS
);