2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Libpwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
33 #include <sys/select.h>
35 #include <sys/types.h>
46 #ifdef HAVE_GETOPT_LONG
51 #include "getopt_long.h"
54 #ifdef HAVE_LIBREADLINE
55 # if defined(HAVE_READLINE_READLINE_H)
56 # include <readline/readline.h>
57 # elif defined(HAVE_READLINE_H)
58 # include <readline.h>
59 # endif /* !defined(HAVE_READLINE_H) */
60 static int interactive_error
;
61 static int interactive
;
62 #endif /* HAVE_LIBREADLINE */
64 #ifdef HAVE_READLINE_HISTORY
65 # if defined(HAVE_READLINE_HISTORY_H)
66 # include <readline/history.h>
67 # elif defined(HAVE_HISTORY_H)
70 #endif /* HAVE_READLINE_HISTORY */
77 #define N_(msgid) gettext(msgid)
81 #define DEFAULT_PORT 22
82 #define DEFAULT_PIN_TRIES 3
83 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
86 static int no_pinentry
;
88 static char *filename
;
90 static int force_save
;
91 static int no_passphrase
;
94 static char *sign_keygrip
;
95 static char *keyparams
;
96 static char *password
;
98 static char *new_keyfile
;
99 static unsigned long s2k_count
;
100 static uint64_t iterations
;
101 static int iterations_arg
;
103 static int local_pin
;
104 static int inquirefd
;
111 size_t size
; // from stat(2).
115 static gpg_error_t
finalize();
116 static gpg_error_t
set_inquire(int fd
, const char *line
,
117 struct inquire_s
**result
);
118 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
119 size_t *len
, struct inquire_s
*inq
);
120 static gpg_error_t
open_command(const char *line
);
122 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
);
125 static void show_error(gpg_error_t rc
, const char *str
)
127 fprintf(stderr
, "ERR %i: %s%s%s%s", rc
, gpg_strerror(rc
),
128 str
? ": " : "", str
? str
: "", str
? "" : "\n");
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"
161 " --tls-fingerprint <string>\n"
162 " a SHA-1 hash of the server fingerprint to verify against\n"
165 " do not lock the data file upon opening it\n"
166 " --lock-timeout <N>\n"
167 " time in tenths of a second to wait for a locked data file\n"
169 " disable showing of status messages from the server\n"
171 " disable showing of extra messages (implies --no-status)\n"
172 " --name, -n <string>\n"
173 " set the client name\n"
175 " do not require a passphrase when saving a new file\n"
176 " --keygrip <string>\n"
177 " the hex string of the keygrip to save to\n"
178 " --sign-keygrip <string>\n"
179 " the hex string of the keygrip to sign with\n"
180 " --key-file <filename>\n"
181 " obtain the passphrase from the specified filename\n"
182 " --new-key-file <filename>\n"
183 " obtain the passphrase to save with from the specified filename\n"
185 " the number of times to hash the passphrase for a new file\n"
186 " --cipher-iterations <N>\n"
187 " the number of times to encrypt the XML data (N+1)\n"
188 " --timeout <seconds>\n"
189 " pinentry timeout\n"
191 " number of pinentry tries before failing (3)\n"
193 " disable pinentry both remotely and locally\n"
194 " --pinentry <path>\n"
195 " the full path to the pinentry binary (server default)\n"
196 " --ttyname, -y <path>\n"
197 " tty that pinentry will use\n"
198 " --ttytype, -t <string>\n"
199 " pinentry terminal type (default is $TERM)\n"
201 " pinentry display (default is $DISPLAY)\n"
202 " --lc-ctype <string>\n"
203 " locale setting for pinentry\n"
204 " --lc-messages <string>\n"
205 " locale setting for pinentry\n"
206 " --local-pinentry\n"
207 " force using a local pinentry\n"
208 #ifdef HAVE_LIBREADLINE
210 " use a shell like interface to pwmd (allows more than one command)\n"
212 " --output-fd <FD>\n"
213 " redirect command output to the specified file descriptor\n"
214 " --inquire <COMMAND>\n"
215 " the specified command (with any options) uses a server inquire while\n"
216 " command data is read via the inquire file descriptor (stdin)\n"
217 " --inquire-fd <FD>\n"
218 " read inquire data from the specified file descriptor (stdin)\n"
219 " --inquire-file <filename>\n"
220 " read inquire data from the specified filename\n"
221 " --inquire-line, -L <STRING>\n"
222 " the initial line to send (i.e., element path) before the inquire data\n"
223 " --cipher <string>\n"
224 " the cipher to use when saving (see pwmd(1))\n"
226 " send the SAVE command before exiting\n"
227 " --key-params <string>\n"
228 " the key parameters to use when saving a new file (pwmd default)\n"
230 " like --save but always ask for a passphrase\n"
233 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
235 "An optional url may be in the form of:\n"
236 " --url /path/to/socket\n"
237 " --url file://[path/to/socket]\n"
240 " --url ssh[46]://[username@]hostname[:port]\n"
241 " --no-ssh-agent -i identity_file --url ssh[46]://[username@]hostname[:port]\n"
245 " --url tls[46]://hostname[:port] --ca-cert filename --client-cert filename\n"
246 " --client-key filename\n"
252 static gpg_error_t
inquire_cb(void *user
, const char *keyword
, gpg_error_t rc
,
253 char **data
, size_t *size
)
255 struct inquire_s
*inq
= user
;
257 int is_newpassword
= 0;
265 if (!strcmp(keyword
, "PASSPHRASE"))
267 else if (!strcmp(keyword
, "NEW_PASSPHRASE"))
269 #ifdef HAVE_LIBREADLINE
270 else if (!strcmp(keyword
, "KEYPARAM") && !interactive
) {
272 else if (!strcmp(keyword
, "KEYPARAM")) {
274 if (!keyparams
|| !*keyparams
)
275 return gpg_error(GPG_ERR_INV_PARAMETER
);
278 *size
= strlen(keyparams
);
279 return gpg_error(GPG_ERR_EOF
);
282 if ((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
)) {
283 int fd
= open(is_password
? keyfile
: new_keyfile
, O_RDONLY
);
286 fprintf(stderr
, "%s: %s\n", is_newpassword
? new_keyfile
: keyfile
,
288 return gpg_error_from_syserror();
291 rc
= set_inquire(fd
, NULL
, &inq
);
298 fprintf(stderr
, N_("Using keyfile '%s' as %s.\n"),
299 is_newpassword
? new_keyfile
: keyfile
, keyword
);
301 if (!new_keyfile
|| is_newpassword
) {
304 pwmd_socket_type(pwm
, &t
);
305 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
306 if (!no_pinentry
&& t
== PWMD_SOCKET_LOCAL
)
307 pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
310 else if (is_password
&& !keyfile
) {
313 rc
= pwmd_password(pwm
, keyword
, &tmp
, &inq
->len
);
314 if (rc
&& gpg_err_code(rc
) != GPG_ERR_EOF
)
317 pwmd_free(inq
->line
);
321 return gpg_error(GPG_ERR_EOF
);
323 #ifdef HAVE_LIBREADLINE
324 else if (!inq
->header
&& interactive
) {
326 "Press CTRL-D to send the current line. Press twice to end. %s:\n"), keyword
);
331 /* The first part of the command data. */
336 return inq
->fd
== -1 ? gpg_error(GPG_ERR_EOF
) : 0;
339 *size
= read(inq
->fd
, inq
->line
, ASSUAN_LINELENGTH
);
342 return gpg_error(gpg_error_from_syserror());
347 if (((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
))
348 && *size
== inq
->size
)
349 return gpg_error(GPG_ERR_EOF
);
351 return *size
? 0 : gpg_error(GPG_ERR_EOF
);
354 static int status_msg_cb(void *data
, const char *line
)
356 char *p
= strchr(line
, ' ');
358 if (!strcmp(line
, "KEEPALIVE"))
361 if (*line
!= '#' && p
&& strchr(p
, ' ') && *++p
) {
362 char *p1
= strchr(p
, ' ');
363 int a
= strtol(p
, NULL
, 10);
365 if (isdigit(*p
) && p1
) {
366 int b
= strtol(p1
, NULL
, 10);
368 int t
= a
&& b
? a
*100/b
: 0;
370 strncpy(l
, line
, strlen(line
)-strlen(p
)-1);
371 fprintf(stderr
, "\r%s %i/%i %i%%%s", l
, a
, b
, t
, a
== b
? "\n" : "");
377 fprintf(stderr
, "%s\n", line
);
379 #ifdef HAVE_LIBREADLINE
385 static gpg_error_t
process_cmd()
387 return pwmd_process(pwm
);
391 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
)
393 char buf
[LINE_MAX
] = {0}, *p
;
394 struct termios told
, tnew
;
399 if (!isatty(STDIN_FILENO
)) {
400 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
401 return GPG_ERR_ENOTTY
;
405 if (tcgetattr(STDIN_FILENO
, &told
) == -1)
406 return gpg_error_from_syserror();
408 memcpy(&tnew
, &told
, sizeof(struct termios
));
409 tnew
.c_lflag
&= ~(ECHO
);
410 tnew
.c_lflag
|= ICANON
|ECHONL
;
412 if (tcsetattr(STDIN_FILENO
, TCSANOW
, &tnew
) == -1) {
415 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
416 return gpg_error_from_errno(n
);
421 case PWMD_PINENTRY_OPEN
:
422 fprintf(stderr
, N_("Password for %s: "), filename
);
424 case PWMD_PINENTRY_OPEN_FAILED
:
425 fprintf(stderr
, N_("Invalid password. Password for %s: "), filename
);
427 case PWMD_PINENTRY_SAVE
:
428 fprintf(stderr
, N_("New password for %s: "), filename
);
430 case PWMD_PINENTRY_SAVE_CONFIRM
:
431 fprintf(stderr
, N_("Confirm password: "));
437 if ((p
= fgets(buf
, sizeof(buf
), stdin
)) == NULL
) {
438 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
443 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
447 return GPG_ERR_CANCELED
;
450 p
[strlen(p
) - 1] = 0;
453 key
= pwmd_strdup_printf("%s", p
);
454 memset(&buf
, 0, sizeof(buf
));
457 return GPG_ERR_ENOMEM
;
464 static gpg_error_t
knownhost_cb(void *data
, const char *host
, const char *key
,
468 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
);
470 if (no_pinentry
&& !isatty(STDIN_FILENO
)) {
471 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
473 return GPG_ERR_ENOTTY
;
475 else if (no_pinentry
) {
476 for (char *p
= buf
, len
= 0; *p
; p
++, len
++) {
483 while (!isspace(*(--p
)));
490 fprintf(stderr
, "%s\n\n", buf
);
496 fprintf(stderr
, N_("Trust this connection [y/N]: "));
498 rc
= get_password(&result
, PWMD_PINENTRY_CONFIRM
, 1);
503 if (!result
|| !*result
|| *result
== 'n' || *result
== 'N') {
504 if (result
&& *result
)
507 return GPG_ERR_NOT_CONFIRMED
;
510 if ((*result
== 'y' || *result
== 'Y') && *(result
+1) == 0) {
517 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, buf
);
523 return pwmd_getpin(pwm
, NULL
, NULL
, NULL
, PWMD_PINENTRY_CONFIRM
);
527 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
528 static pwmd_socket_t
is_remote_url(const char *str
)
531 return PWMD_SOCKET_LOCAL
;
534 if (strstr(str
, "ssh://") || strstr(str
, "ssh4://")
535 || strstr(str
, "ssh6://"))
536 return PWMD_SOCKET_SSH
;
540 if (strstr(str
, "tls://") || strstr(str
, "tls4://")
541 || strstr(str
, "tls6://"))
542 return PWMD_SOCKET_TLS
;
545 return PWMD_SOCKET_LOCAL
;
549 static char *escape(const char *str
)
552 char *buf
= pwmd_malloc(ASSUAN_LINELENGTH
+1), *b
= buf
;
555 for (p
= str
; *p
; p
++, len
++) {
556 if (len
== ASSUAN_LINELENGTH
)
597 static void free_inquire(struct inquire_s
*inq
)
602 pwmd_free(inq
->line
);
604 if (inq
->fd
!= -1 && inq
->fd
!= STDIN_FILENO
)
610 /* When *result is not NULL it is updated to the new values and not
612 static gpg_error_t
set_inquire(int fd
, const char *line
,
613 struct inquire_s
**result
)
615 struct inquire_s inq
= {0};
616 struct stat st
= {0};
620 if (fstat(fd
, &st
) == -1)
621 return gpg_error_from_syserror();
623 inq
.size
= st
.st_size
;
627 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
629 return GPG_ERR_ENOMEM
;
632 char *s
= escape(line
);
636 return GPG_ERR_ENOMEM
;
639 if (strlen(s
) >= ASSUAN_LINELENGTH
) {
642 return GPG_ERR_LINE_TOO_LONG
;
645 strncpy(inq
.line
, s
, ASSUAN_LINELENGTH
-1);
650 rc
= pwmd_setopt(pwm
, PWMD_OPTION_INQUIRE_TOTAL
,
651 st
.st_size
? st
.st_size
+strlen(inq
.line
) : 0);
658 *result
= pwmd_malloc(sizeof(struct inquire_s
));
660 if ((*result
)->fd
!= -1 && (*result
)->fd
!= STDIN_FILENO
)
661 close((*result
)->fd
);
663 pwmd_free((*result
)->line
);
664 (*result
)->line
= NULL
;
669 memcpy(*result
, &inq
, sizeof(struct inquire_s
));
670 memset(&inq
, 0, sizeof(struct inquire_s
));
674 #ifdef HAVE_LIBREADLINE
675 static int interactive_hook(void)
677 interactive_error
= process_cmd();
679 if (interactive_error
)
680 rl_event_hook
= NULL
;
685 static void print_help()
688 "------------------------------------------------------------------------------\n"
689 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
691 "Type HELP for protocol commands. Type .help for pwmc commands. Press CTRL-D\n"
693 "------------------------------------------------------------------------------\n"
698 static char *parse_arg(const char *src
, char *dst
, size_t len
)
704 for (; s
&& *s
&& *s
!= ' ' && n
< len
; s
++, n
++)
711 static char *parse_opt(char **line
, const char *opt
, gpg_error_t
*rc
)
713 static char result
[ASSUAN_LINELENGTH
] = {0}, *r
= result
;
715 char *s
= strstr(*line
, opt
);
723 size_t rlen
= strlen(opt
);
727 while (*p
&& *p
== ' ') {
732 for (; *p
&& len
< sizeof(result
)-1; p
++, rlen
++) {
733 if (isspace(*p
) && !quote
)
736 if (*p
== '\"' && lastc
!= '\\') {
748 if (len
>= sizeof(result
)-1)
749 *rc
= GPG_ERR_LINE_TOO_LONG
;
753 while (*p
&& *p
== ' ')
763 static gpg_error_t
read_command(const char *line
, char **result
, size_t *len
)
767 char filebuf
[ASSUAN_LINELENGTH
];
768 char *filename
= NULL
;
769 struct inquire_s
*inq
= NULL
;
770 char *p
= (char *)line
;
771 const char *prefix
= parse_opt(&p
, "--prefix", &rc
);
779 while (*p
&& isspace(*p
))
782 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
783 if (filename
&& *filename
) {
784 p
+= strlen(filename
)+1;
786 while (*p
&& isspace(*p
))
795 fprintf(stderr
, N_("Usage: .read [--prefix <string>] <filename> <command> [args]\n"));
796 fprintf(stderr
, N_("Use '\\' to escape special characters in the --prefix (\\t = TAB, \\\" = \")\n"));
800 fd
= open(filename
, O_RDONLY
);
802 return gpg_error_from_syserror();
804 rc
= set_inquire(fd
, prefix
&& *prefix
? prefix
: NULL
, &inq
);
811 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, p
);
816 static gpg_error_t
redir_command(const char *line
)
818 const char *p
= line
;
820 gpg_error_t rc
= GPG_ERR_SYNTAX
;
821 char filebuf
[ASSUAN_LINELENGTH
];
822 char *filename
= NULL
;
823 struct inquire_s
*inq
= NULL
;
827 if (p
&& *p
&& *++p
) {
828 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
829 if (filename
&& *filename
) {
830 p
+= strlen(filename
)+1;
832 while (*p
&& isspace(*p
))
841 fprintf(stderr
, N_("Usage: .redir <filename> <command> [args]\n"));
845 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
847 return gpg_error_from_syserror();
849 #ifdef HAVE_LIBREADLINE
850 rc
= set_inquire(interactive
? STDIN_FILENO
: inquirefd
, NULL
, &inq
);
852 rc
= set_inquire(inquirefd
, NULL
, &inq
);
859 rc
= parse_dotcommand(p
, &result
, &len
, inq
);
860 if (!rc
&& result
&& len
--) { // null byte which is always appended
861 if (write(fd
, result
, len
) != len
)
862 rc
= GPG_ERR_TOO_SHORT
;
871 static gpg_error_t
help_command(const char *line
)
873 fprintf(stderr
, N_("Type HELP for protocol commands. Available pwmc commands:\n\n"
874 " .redir <filename> <command>\n"
875 " redirect the output of a command to the specified file\n"
877 " .open <filename>\n"
878 " open the specified filename losing any changes to the current one\n"
880 " .read [--prefix <string>] <filename> <command> [args]\n"
881 " obtain data from the specified filename for an inquire command\n"
883 " .set help | <name> [<value>]\n"
884 " set option <name> to <value>\n"
887 " this help text\n"));
891 static gpg_error_t
open_command(const char *line
)
893 struct inquire_s
*inq
= NULL
;
894 const char *filename
= line
;
897 while (filename
&& isspace(*filename
))
900 if (!filename
|| !*filename
) {
901 fprintf(stderr
, N_("Usage: .open <filename>\n"));
902 return GPG_ERR_SYNTAX
;
905 #ifdef HAVE_LIBREADLINE
906 if (interactive
|| !quiet
)
910 fprintf(stderr
, N_("Opening data file \"%s\" ...\n"), filename
);
912 #ifdef HAVE_LIBREADLINE
913 rc
= set_inquire(interactive
? STDIN_FILENO
: -1, NULL
, &inq
);
915 rc
= set_inquire(-1, NULL
, &inq
);
921 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
923 #ifdef HAVE_LIBREADLINE
924 // For safety. This file may be a different one. Make the user set it
927 pwmd_free(new_keyfile
);
932 rc
= pwmd_open(pwm
, filename
, inquire_cb
, inq
);
937 static gpg_error_t
set_command(const char *line
)
940 char name
[256] = {0};
941 char value
[512] = {0};
944 const char *p
= line
;
946 while (p
&& *p
&& isspace(*p
))
949 namep
= parse_arg(p
, name
, sizeof(name
));
950 if (!namep
|| !*namep
) {
951 fprintf(stderr
, N_("Usage: .set help | <name> [<value>]\n"));
952 return GPG_ERR_SYNTAX
;
956 while (p
&& *p
&& isspace(*p
))
959 valuep
= parse_arg(p
, value
, sizeof(value
));
961 if (!strcmp(name
, "keyfile") || !strcmp(name
, "new-keyfile")) {
962 int is_newkeyfile
= 1;
966 if (!strcmp(name
, "keyfile"))
970 pwmd_free(new_keyfile
);
978 p
+= strlen (valuep
);
979 while (p
&& *p
&& isspace(*p
))
985 memcpy (datafile
, value
, sizeof(datafile
));
986 valuep
= parse_arg (p
, value
, sizeof(value
));
987 rc
= pwmd_command (pwm
, NULL
, NULL
, NULL
, NULL
, "KEYGRIP %s", datafile
);
988 if (gpg_err_code (rc
) == GPG_ERR_NOT_SUPPORTED
)
997 if (!rc
&& *valuep
) {
999 new_keyfile
= pwmd_strdup(value
);
1001 keyfile
= pwmd_strdup(value
);
1006 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1007 "AGENT option pinentry-mode=loopback");
1010 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 1);
1011 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
1014 else if (!local_pin
&& !no_pinentry
) {
1017 pwmd_socket_type(pwm
, &t
);
1018 if (t
== PWMD_SOCKET_LOCAL
) {
1019 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
1020 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
1024 else if (!strcmp(name
, "help")) {
1026 "Set a libpwmd or pwmc option. The option name and optional value is space\n"
1027 "delimited. When no value is specified the option is unset.\n\n"
1028 "keyfile <datafile> [<filename>]\n"
1029 " set or unset the keyfile to be used when a passphrase is required\n"
1031 "new-keyfile <datafile> [<filename>]\n"
1032 " set or unset the keyfile to be used when a new passphrase is required\n"
1036 rc
= GPG_ERR_UNKNOWN_OPTION
;
1041 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
1042 size_t *len
, struct inquire_s
*inq
)
1044 const char *p
= line
;
1047 if (!strncmp(p
, ".read", 5))
1048 rc
= read_command(p
+5, result
, len
);
1049 else if (!strncmp(p
, ".redir", 6))
1050 rc
= redir_command(p
+6);
1051 else if (!strncmp(p
, ".help", 5))
1052 rc
= help_command(p
+5);
1053 else if (!strncmp(p
, ".open", 5))
1054 rc
= open_command(p
+5);
1055 else if (!strncmp(p
, ".set", 4))
1056 rc
= set_command(p
+4);
1058 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, line
);
1063 #ifdef HAVE_LIBREADLINE
1064 static gpg_error_t
do_interactive()
1067 struct inquire_s
*inq
= NULL
;
1074 rc
= set_inquire(STDIN_FILENO
, NULL
, &inq
);
1078 fprintf(stderr
, N_("WARNING: interactive mode doesn't use secure memory!\n"));
1080 rl_event_hook
= &interactive_hook
;
1081 rl_set_keyboard_input_timeout(100000);
1085 char *result
= NULL
;
1088 line
= readline("pwm> ");
1089 if (interactive_error
) {
1090 rc
= interactive_error
;
1099 if (gpg_err_code(rc
) != GPG_ERR_CANCELED
&&
1100 gpg_err_code(rc
) != GPG_ERR_EOF
)
1101 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1110 #ifdef HAVE_READLINE_HISTORY
1114 rc
= parse_dotcommand(line
, &result
, &len
, inq
);
1119 if (gpg_err_code(rc
) == GPG_ERR_BAD_DATA
)
1120 (void)pwmd_command(pwm
, &tmp
, NULL
, NULL
, NULL
,
1121 "GETINFO last_error");
1123 show_error(rc
, tmp
);
1126 else if (result
&& len
)
1127 printf("%s%s", result
, result
[len
-1] != '\n' ? "\n" : "");
1137 static char *itoa(long long int n
)
1139 static char buf
[64];
1141 snprintf(buf
, sizeof(buf
), "%llu", n
);
1145 static gpg_error_t
finalize()
1148 #ifdef HAVE_LIBREADLINE
1151 if (!force_save
&& interactive
) {
1155 fprintf(stderr
, "\n");
1158 fprintf(stderr
, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
1159 p
= fgets(buf
, sizeof(buf
), stdin
);
1171 return GPG_ERR_CANCELED
;
1175 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1176 "CLEARCACHE %s", filename
);
1191 } while (!finished
);
1195 if (save
&& !filename
) {
1196 fprintf(stderr
, N_("No filename was specified on the command line. Aborting.\n"));
1197 return GPG_ERR_CANCELED
;
1200 if (save
&& filename
) {
1201 char *args
= pwmd_strdup_printf("%s%s %s%s %s %s%s %s%s --s2k-count=%lu",
1202 keygrip
? "--keygrip=" : "", keygrip
? keygrip
: "",
1203 sign_keygrip
? "--sign-keygrip=" : "", sign_keygrip
? sign_keygrip
: "",
1204 no_passphrase
? "--no-passphrase" : "",
1205 cipher
? "--cipher=" : "", cipher
? cipher
: "",
1206 iterations_arg
? "--cipher-iterations=" : "", iterations_arg
? itoa(iterations
) : "",
1209 #ifdef HAVE_LIBREADLINE
1210 if (!quiet
|| interactive
) {
1214 fprintf(stderr
, "\n");
1215 fprintf(stderr
, N_("Saving changes ...\n"));
1218 rc
= pwmd_save(pwm
, args
, inquire_cb
, NULL
);
1222 no_passphrase
= 0; // reset to avoid usage error on the next SAVE
1225 #ifdef HAVE_LIBREADLINE
1227 return rc
? rc
: quit
? 0 : GPG_ERR_CANCELED
;
1233 int main(int argc
, char *argv
[])
1238 char command
[ASSUAN_LINELENGTH
], *p
= NULL
;
1239 char *result
= NULL
;
1241 char *pinentry_path
= NULL
;
1242 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
1244 int outfd
= STDOUT_FILENO
;
1245 FILE *outfp
= stdout
;
1246 FILE *inquirefp
= stdin
;
1247 int show_status
= 1;
1248 char *clientname
= "pwmc";
1249 char *inquire
= NULL
;
1250 char *inquire_line
= NULL
;
1253 int use_ssh_agent
= 1;
1254 long ssh_timeout
= 0;
1255 int ssh_keepalive
= 0;
1256 char *knownhosts
= NULL
;
1257 char *identity
= NULL
;
1260 char *cacert
= NULL
;
1261 char *clientcert
= NULL
;
1262 char *clientkey
= NULL
;
1265 char *tls_fingerprint
= NULL
;
1267 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1268 pwmd_socket_t socktype
;
1270 int lock_on_open
= 1;
1271 long lock_timeout
= 0;
1273 /* The order is important. */
1276 OPT_USE_SSH_AGENT
, OPT_IDENTITY
, OPT_KNOWNHOSTS
, OPT_SSH_TIMEOUT
,
1280 OPT_CACERT
, OPT_CLIENTCERT
, OPT_CLIENTKEY
, OPT_PRIORITY
, OPT_VERIFY
,
1283 OPT_URL
, OPT_LOCAL
, OPT_FORCE_SAVE
, OPT_TTYNAME
, OPT_TTYTYPE
,
1284 OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
, OPT_TIMEOUT
, OPT_TRIES
,
1285 OPT_PINENTRY
, OPT_KEYFILE
, OPT_NEW_KEYFILE
, OPT_NOLOCK
,
1286 OPT_LOCK_TIMEOUT
, OPT_SAVE
, OPT_OUTPUT_FD
, OPT_INQUIRE
,
1287 OPT_INQUIRE_FD
, OPT_INQUIRE_FILE
, OPT_INQUIRE_LINE
, OPT_NO_STATUS
,
1288 OPT_NAME
, OPT_VERSION
, OPT_HELP
, OPT_KEYGRIP
, OPT_SIGN_KEYGRIP
,
1289 OPT_NOPASSPHRASE
, OPT_CIPHER
, OPT_KEYPARAMS
, OPT_NO_PINENTRY
,
1290 OPT_S2K_COUNT
, OPT_ITERATIONS
, OPT_QUIET
,
1291 #ifdef HAVE_LIBREADLINE
1295 const struct option long_opts
[] = {
1297 { "no-ssh-agent", 0, 0, 0 },
1298 { "identity", 1, 0, 'i' },
1299 { "knownhosts", 1, 0, 'k' },
1300 { "ssh-timeout", 1, 0, 0 },
1301 { "ssh-keepalive", 1, 0, 0 },
1304 { "ca-cert", 1, 0, 0 },
1305 { "client-cert", 1, 0, 0 },
1306 { "client-key", 1, 0, 0 },
1307 { "tls-priority", 1, 0, 0 },
1308 { "tls-verify", 0, 0, 0 },
1309 { "tls-fingerprint", 1, 0, 0 },
1312 { "local-pinentry", 0, 0 },
1313 { "force-save", 0, 0 },
1314 { "ttyname", 1, 0, 'y' },
1315 { "ttytype", 1, 0, 't' },
1316 { "display", 1, 0, 'd' },
1317 { "lc-ctype", 1, 0, 0 },
1318 { "lc-messages", 1, 0, 0 },
1319 { "timeout", 1, 0, 0 },
1320 { "tries", 1, 0, 0 },
1321 { "pinentry", 1, 0, 0 },
1322 { "key-file", 1, 0, 0 },
1323 { "new-key-file", 1, 0, 0 },
1324 { "no-lock", 0, 0, 0 },
1325 { "lock-timeout", 1, 0, 0 },
1326 { "save", 0, 0, 'S' },
1327 { "output-fd", 1, 0, 0 },
1328 { "inquire", 1, 0, 0 },
1329 { "inquire-fd", 1, 0, 0 },
1330 { "inquire-file", 1, 0, 0 },
1331 { "inquire-line", 1, 0, 'L' },
1332 { "no-status", 0, 0, 0 },
1333 { "name", 1, 0, 'n' },
1334 { "version", 0, 0, 0 },
1335 { "help", 0, 0, 0 },
1336 { "keygrip", 1, 0, 0 },
1337 { "sign-keygrip", 1, 0, 0 },
1338 { "no-passphrase", 0, 0, 0 },
1339 { "cipher", 1, 0, 0 },
1340 { "key-params", 1, 0, 0 },
1341 { "no-pinentry", 0, 0, 0 },
1342 { "s2k-count", 1, 0, 0 },
1343 { "cipher-iterations", 1, 0, 0 },
1344 { "quiet", 0, 0, 0 },
1345 #ifdef HAVE_LIBREADLINE
1346 { "interactive", 0, 0 },
1351 const char *optstring
= "L:y:t:d:P:I:Sn:i:k:";
1353 const char *optstring
= "L:y:t:d:P:I:Sn:";
1358 setlocale(LC_ALL
, "");
1359 bindtextdomain("libpwmd", LOCALEDIR
);
1362 #ifdef HAVE_LIBREADLINE
1363 if (!strcmp(basename(argv
[0]), "pwmsh"))
1367 tries
= DEFAULT_PIN_TRIES
;
1368 inquirefd
= STDIN_FILENO
;
1370 while ((opt
= getopt_long(argc
, argv
, optstring
, long_opts
, &opt_index
)) != -1) {
1372 /* Handle long options without a short option part. */
1374 switch (opt_index
) {
1376 case OPT_USE_SSH_AGENT
:
1379 case OPT_SSH_TIMEOUT
:
1380 ssh_timeout
= strtol(optarg
, &p
, 10);
1382 case OPT_SSH_KEEPALIVE
:
1383 ssh_keepalive
= strtol(optarg
, &p
, 10);
1390 case OPT_CLIENTCERT
:
1391 clientcert
= optarg
;
1403 tls_fingerprint
= optarg
;
1410 keyfile
= pwmd_strdup(optarg
);
1412 case OPT_NEW_KEYFILE
:
1413 new_keyfile
= pwmd_strdup(optarg
);
1418 case OPT_LOCK_TIMEOUT
:
1419 lock_timeout
= strtol(optarg
, &p
, 10);
1427 case OPT_FORCE_SAVE
:
1428 save
= force_save
= 1;
1431 lcctype
= pwmd_strdup(optarg
);
1433 case OPT_LC_MESSAGES
:
1434 lcmessages
= pwmd_strdup(optarg
);
1437 timeout
= strtol(optarg
, &p
, 10);
1440 tries
= strtol(optarg
, &p
, 10);
1445 case OPT_INQUIRE_FD
:
1446 inquirefd
= strtol(optarg
, &p
, 10);
1448 inquirefp
= fdopen(inquirefd
, "r");
1450 pwmd_free(password
);
1451 err(EXIT_FAILURE
, "%i", inquirefd
);
1455 case OPT_INQUIRE_FILE
:
1456 inquirefd
= open(optarg
, O_RDONLY
);
1457 if (!inquirefd
== -1) {
1458 pwmd_free(password
);
1459 err(EXIT_FAILURE
, "%s", optarg
);
1461 inquirefp
= fdopen(inquirefd
, "r");
1464 outfd
= strtol(optarg
, &p
, 10);
1466 outfp
= fdopen(outfd
, "w");
1468 pwmd_free(password
);
1469 err(EXIT_FAILURE
, "%i", outfd
);
1477 pwmd_free(password
);
1478 printf("%s (pwmc)\n\n"
1479 "Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012\n"
1481 "Released under the terms of the GPL v2. Use at your own risk.\n\n"
1482 "Compile-time features:\n"
1483 #ifdef HAVE_LIBREADLINE
1498 #ifdef WITH_PINENTRY
1514 , PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1517 pinentry_path
= optarg
;
1520 usage(argv
[0], EXIT_SUCCESS
);
1524 case OPT_SIGN_KEYGRIP
:
1525 sign_keygrip
= optarg
;
1528 s2k_count
= strtoul(optarg
, &p
, 10);
1530 case OPT_ITERATIONS
:
1531 iterations
= strtoull(optarg
, &p
, 10);
1538 case OPT_NOPASSPHRASE
:
1544 case OPT_NO_PINENTRY
:
1547 #ifdef HAVE_LIBREADLINE
1548 case OPT_INTERACTIVE
:
1553 usage(argv
[0], EXIT_FAILURE
);
1557 fprintf(stderr
, N_("%s: invalid argument for option '--%s'\n"),
1558 argv
[0], long_opts
[opt_index
].name
);
1559 usage(argv
[0], EXIT_FAILURE
);
1568 knownhosts
= optarg
;
1572 inquire_line
= optarg
;
1587 clientname
= optarg
;
1590 pwmd_free(password
);
1591 usage(argv
[0], EXIT_FAILURE
);
1595 #ifdef HAVE_LIBREADLINE
1596 if (interactive
&& !isatty(STDIN_FILENO
)) {
1597 pwmd_free(password
);
1598 usage(argv
[0], EXIT_FAILURE
);
1600 else if (isatty(STDIN_FILENO
) && !inquire
&& !inquire_line
)
1604 filename
= argv
[optind
];
1606 gnutls_global_set_mem_functions(pwmd_malloc
, pwmd_malloc
, NULL
,
1607 pwmd_realloc
, pwmd_free
);
1610 rc
= pwmd_new(clientname
, &pwm
);
1614 pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
1616 fprintf(stderr
, N_("Connecting ...\n"));
1618 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1619 socktype
= is_remote_url(url
);
1620 if (socktype
!= PWMD_SOCKET_LOCAL
) {
1622 if (socktype
== PWMD_SOCKET_SSH
) {
1624 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_CB
, knownhost_cb
);
1628 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_DATA
, clientname
);
1632 if (!getenv("SSH_AUTH_SOCK") || identity
)
1635 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_AGENT
, use_ssh_agent
);
1639 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_TIMEOUT
, ssh_timeout
);
1643 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_KEEPALIVE
, ssh_keepalive
);
1647 rc
= pwmd_connect(pwm
, url
, identity
, knownhosts
);
1652 rc
= pwmd_setopt(pwm
, PWMD_OPTION_TLS_VERIFY
, tls_verify
);
1656 rc
= pwmd_connect(pwm
, url
, clientcert
, clientkey
, cacert
, prio
,
1662 rc
= pwmd_connect(pwm
, url
);
1664 rc
= pwmd_connect(pwm
, url
);
1670 fprintf(stderr
, N_("Connected.\n"));
1674 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1675 "OPTION LOCK-TIMEOUT=%li", lock_timeout
);
1681 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCK_ON_OPEN
, 1);
1686 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, NULL
);
1691 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
1696 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, no_pinentry
);
1700 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCAL_PINENTRY
,
1701 (local_pin
|| keyfile
|| new_keyfile
));
1705 if (pinentry_path
) {
1706 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
1712 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
1718 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
1724 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
1730 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
1736 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
1743 rc
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
1749 rc
= open_command(filename
);
1754 #ifdef HAVE_LIBREADLINE
1759 rc
= do_interactive();
1766 struct inquire_s
*inq
= NULL
;
1768 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1770 rc
= pwmd_command(pwm
, &result
, &len
, inquire_cb
, inq
, inquire
);
1776 fcntl(STDIN_FILENO
, F_SETFL
, O_NONBLOCK
);
1785 n
= read(STDIN_FILENO
, command
, sizeof(command
));
1787 if (errno
== EAGAIN
) {
1792 rc
= gpg_error_from_errno(errno
);
1803 if (!p
|| !*p
|| !strcasecmp(p
, "BYE"))
1807 struct inquire_s
*inq
= NULL
;
1808 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1810 rc
= parse_dotcommand(command
, &result
, &len
, inq
);
1819 fwrite(result
, 1, result
[len
-1] == 0 ? len
-1 : len
, outfp
);
1824 pwmd_free(password
);
1825 password
= result
= NULL
;
1828 else if (gpg_err_code(rc
) == GPG_ERR_BAD_DATA
)
1829 (void)pwmd_command(pwm
, &result
, NULL
, NULL
, NULL
, "GETINFO last_error");
1831 #ifdef HAVE_LIBREADLINE
1834 memset(command
, 0, sizeof(command
));
1837 pwmd_free(new_keyfile
);
1841 show_error(rc
, result
);
1844 if (connected
&& !quiet
)
1845 fprintf(stderr
, N_("Connection closed.\n"));
1847 exit(rc
? EXIT_FAILURE
: EXIT_SUCCESS
);