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 #include <readline/readline.h>
57 #include <readline/history.h>
59 static int interactive_error
;
60 static int interactive
;
68 #define N_(msgid) gettext(msgid)
72 #define DEFAULT_PORT 22
73 #define DEFAULT_PIN_TRIES 3
74 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
77 static int no_pinentry
;
79 static char *filename
;
81 static int force_save
;
82 static int no_passphrase
;
85 static char *sign_keygrip
;
86 static char *keyparams
;
87 static char *password
;
89 static char *new_keyfile
;
90 static unsigned long s2k_count
;
91 static uint64_t iterations
;
92 static int iterations_arg
;
101 size_t size
; // from stat(2).
105 static gpg_error_t
finalize();
106 static gpg_error_t
set_inquire(int fd
, const char *line
,
107 struct inquire_s
**result
);
108 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
109 size_t *len
, struct inquire_s
*inq
);
110 static gpg_error_t
open_command(const char *line
);
112 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
);
115 static void show_error(gpg_error_t error
)
117 fprintf(stderr
, "ERR %i %s\n", error
, gpg_strerror(error
));
120 static void usage(const char *pn
, int status
)
122 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
123 "Read a PWMD protocol command from standard input.\n\n"
124 "Usage: pwmc [options] [file]\n"
126 " a url string to connect to (see below)\n"
129 " disable SSH agent use (enabled when SSH_AUTH_SOCK is set)\n"
130 " --identity, -i <filename>\n"
131 " the ssh identity file to use for authentication\n"
132 " --knownhosts, -k <filename>\n"
133 " the ssh knownhosts file to use (~/.ssh/knownhosts)\n"
134 " --ssh-timeout <seconds>\n"
135 " timeout before a remote command fails (0=disabled, default)\n"
136 " --ssh-keepalive <seconds>\n"
137 " interval to send a keepalive to the remote server (0=disabled, default)\n"
140 " --ca-cert <filename>\n"
141 " certificate authority (CA) used to sign the server cert\n"
142 " --client-cert <filename>\n"
143 " client certificate to use for authentication\n"
144 " --client-key <filename>\n"
145 " key file used to protect the client certificate\n"
146 " --tls-priority <string>\n"
147 " compression, cipher and hash algorithm string (SECURE256)\n"
149 " verify the hostname against the server certificate\n"
152 " do not lock the data file upon opening it\n"
154 " disable showing of status messages from the server\n"
155 " --name, -n <string>\n"
156 " set the client name\n"
158 " do not require a passphrase when saving a new file\n"
159 " --keygrip <string>\n"
160 " the hex string of the keygrip to save to\n"
161 " --sign-keygrip <string>\n"
162 " the hex string of the keygrip to sign with\n"
163 " --key-file <filename>\n"
164 " obtain the passphrase from the specified filename\n"
165 " --new-key-file <filename>\n"
166 " obtain the passphrase to save with from the specified filename\n"
168 " the number of times to hash the passphrase for a new file\n"
169 " --cipher-iterations <N>\n"
170 " the number of times to encrypt the XML data (N+1)\n"
171 " --timeout <seconds>\n"
172 " pinentry timeout\n"
174 " number of pinentry tries before failing (3)\n"
176 " disable pinentry both remotely and locally\n"
177 " --pinentry <path>\n"
178 " the full path to the pinentry binary (server default)\n"
179 " --ttyname, -y <path>\n"
180 " tty that pinentry will use\n"
181 " --ttytype, -t <string>\n"
182 " pinentry terminal type (default is $TERM)\n"
184 " pinentry display (default is $DISPLAY)\n"
185 " --lc-ctype <string>\n"
186 " locale setting for pinentry\n"
187 " --lc-messages <string>\n"
188 " locale setting for pinentry\n"
189 " --local-pinentry\n"
190 " force using a local pinentry\n"
191 #ifdef HAVE_LIBREADLINE
193 " use a shell like interface to pwmd (allows more than one command)\n"
195 " --output-fd <FD>\n"
196 " redirect command output to the specified file descriptor\n"
197 " --inquire <COMMAND>\n"
198 " the specified command (with any options) uses a server inquire while\n"
199 " command data is read via the inquire file descriptor (stdin)\n"
200 " --inquire-fd <FD>\n"
201 " read inquire data from the specified file descriptor (stdin)\n"
202 " --inquire-file <filename>\n"
203 " read inquire data from the specified filename\n"
204 " --inquire-line, -L <STRING>\n"
205 " the initial line to send (i.e., element path) before the inquire data\n"
206 " --cipher <string>\n"
207 " the cipher to use when saving (see pwmd(1))\n"
209 " send the SAVE command before exiting\n"
210 " --key-params <string>\n"
211 " the key parameters to use when saving a new file (pwmd default)\n"
213 " like --save but always ask for a passphrase\n"
216 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
218 "An optional url may be in the form of:\n"
219 " --url /path/to/socket\n"
220 " --url file://[path/to/socket]\n"
223 " --url ssh[46]://[username@]hostname[:port]\n"
224 " --no-ssh-agent -i identity_file --url ssh[46]://[username@]hostname[:port]\n"
228 " --url tls[46]://hostname[:port]\n"
234 static gpg_error_t
inquire_cb(void *user
, const char *keyword
, gpg_error_t rc
,
235 char **data
, size_t *size
)
237 struct inquire_s
*inq
= user
;
239 int is_newpassword
= 0;
247 if (!strcmp(keyword
, "PASSPHRASE"))
249 else if (!strcmp(keyword
, "NEW_PASSPHRASE"))
251 #ifdef HAVE_LIBREADLINE
252 else if (!strcmp(keyword
, "KEYPARAM") && !interactive
) {
254 else if (!strcmp(keyword
, "KEYPARAM")) {
256 if (!keyparams
|| !*keyparams
)
257 return gpg_error(GPG_ERR_INV_PARAMETER
);
260 *size
= strlen(keyparams
);
261 return gpg_error(GPG_ERR_EOF
);
264 if ((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
)) {
265 int fd
= open(is_password
? keyfile
: new_keyfile
, O_RDONLY
);
268 fprintf(stderr
, "%s: %s\n", is_newpassword
? new_keyfile
: keyfile
,
270 return gpg_error_from_syserror();
273 rc
= set_inquire(fd
, NULL
, &inq
);
279 fprintf(stderr
, N_("Using keyfile '%s' as %s.\n"),
280 is_newpassword
? new_keyfile
: keyfile
, keyword
);
282 if (!new_keyfile
|| is_newpassword
) {
285 pwmd_socket_type(pwm
, &t
);
286 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
287 if (!no_pinentry
&& t
== PWMD_SOCKET_LOCAL
)
288 pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
291 else if (is_password
&& !keyfile
) {
294 rc
= pwmd_password(pwm
, keyword
, &tmp
, &inq
->len
);
295 if (rc
&& gpg_err_code(rc
) != GPG_ERR_EOF
)
298 pwmd_free(inq
->line
);
302 return gpg_error(GPG_ERR_EOF
);
304 #ifdef HAVE_LIBREADLINE
305 else if (!inq
->header
&& interactive
) {
307 "Press CTRL-D to send the current line. Press twice to end. %s:\n"), keyword
);
312 /* The first part of the command data. */
317 return inq
->fd
== -1 ? gpg_error(GPG_ERR_EOF
) : 0;
320 *size
= read(inq
->fd
, inq
->line
, ASSUAN_LINELENGTH
);
323 return gpg_error(gpg_error_from_syserror());
328 if (((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
))
329 && *size
== inq
->size
)
330 return gpg_error(GPG_ERR_EOF
);
332 return *size
? 0 : gpg_error(GPG_ERR_EOF
);
335 static int status_msg_cb(void *data
, const char *line
)
337 char *p
= strchr(line
, ' ');
339 if (!strcmp(line
, "KEEPALIVE"))
342 if (*line
!= '#' && p
&& strchr(p
, ' ') && *++p
) {
343 char *p1
= strchr(p
, ' ');
344 int a
= strtol(p
, NULL
, 10);
346 if (isdigit(*p
) && p1
) {
347 int b
= strtol(p1
, NULL
, 10);
349 int t
= a
&& b
? a
*100/b
: 0;
351 strncpy(l
, line
, strlen(line
)-strlen(p
)-1);
352 fprintf(stderr
, "\r%s %i/%i %i%%%s", l
, a
, b
, t
, a
== b
? "\n" : "");
358 fprintf(stderr
, "%s\n", line
);
363 static gpg_error_t
process_cmd()
365 return pwmd_process(pwm
);
369 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
)
371 char buf
[LINE_MAX
] = {0}, *p
;
372 struct termios told
, tnew
;
377 if (!isatty(STDIN_FILENO
)) {
378 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
379 return GPG_ERR_ENOTTY
;
383 if (tcgetattr(STDIN_FILENO
, &told
) == -1)
384 return gpg_error_from_syserror();
386 memcpy(&tnew
, &told
, sizeof(struct termios
));
387 tnew
.c_lflag
&= ~(ECHO
);
388 tnew
.c_lflag
|= ICANON
|ECHONL
;
390 if (tcsetattr(STDIN_FILENO
, TCSANOW
, &tnew
) == -1) {
393 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
394 return gpg_error_from_errno(n
);
399 case PWMD_PINENTRY_OPEN
:
400 fprintf(stderr
, N_("Password for %s: "), filename
);
402 case PWMD_PINENTRY_OPEN_FAILED
:
403 fprintf(stderr
, N_("Invalid password. Password for %s: "), filename
);
405 case PWMD_PINENTRY_SAVE
:
406 fprintf(stderr
, N_("New password for %s: "), filename
);
408 case PWMD_PINENTRY_SAVE_CONFIRM
:
409 fprintf(stderr
, N_("Confirm password: "));
415 if ((p
= fgets(buf
, sizeof(buf
), stdin
)) == NULL
) {
416 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
421 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
425 return GPG_ERR_CANCELED
;
428 p
[strlen(p
) - 1] = 0;
431 key
= pwmd_strdup_printf("%s", p
);
432 memset(&buf
, 0, sizeof(buf
));
435 return GPG_ERR_ENOMEM
;
442 static gpg_error_t
knownhost_cb(void *data
, const char *host
, const char *key
,
446 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
);
448 if (no_pinentry
&& !isatty(STDIN_FILENO
)) {
449 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
451 return GPG_ERR_ENOTTY
;
453 else if (no_pinentry
) {
454 for (char *p
= buf
, len
= 0; *p
; p
++, len
++) {
461 while (!isspace(*(--p
)));
468 fprintf(stderr
, "%s\n\n", buf
);
474 fprintf(stderr
, N_("Trust this connection [y/N]: "));
476 rc
= get_password(&result
, PWMD_PINENTRY_CONFIRM
, 1);
481 if (!result
|| !*result
|| *result
== 'n' || *result
== 'N') {
482 if (result
&& *result
)
485 return GPG_ERR_NOT_CONFIRMED
;
488 if ((*result
== 'y' || *result
== 'Y') && *(result
+1) == 0) {
495 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, buf
);
501 return pwmd_getpin(pwm
, NULL
, NULL
, NULL
, PWMD_PINENTRY_CONFIRM
);
505 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
506 static pwmd_socket_t
is_remote_url(const char *str
)
509 return PWMD_SOCKET_LOCAL
;
512 if (strstr(str
, "ssh://") || strstr(str
, "ssh4://")
513 || strstr(str
, "ssh6://"))
514 return PWMD_SOCKET_SSH
;
518 if (strstr(str
, "tls://") || strstr(str
, "tls4://")
519 || strstr(str
, "tls6://"))
520 return PWMD_SOCKET_TLS
;
523 return PWMD_SOCKET_LOCAL
;
527 static char *escape(const char *str
)
530 char *buf
= pwmd_malloc(ASSUAN_LINELENGTH
+1), *b
= buf
;
533 for (p
= str
; *p
; p
++, len
++) {
534 if (len
== ASSUAN_LINELENGTH
)
575 static void free_inquire(struct inquire_s
*inq
)
580 pwmd_free(inq
->line
);
582 if (inq
->fd
!= -1 && inq
->fd
!= STDIN_FILENO
)
588 /* When *result is not NULL it is updated to the new values and not
590 static gpg_error_t
set_inquire(int fd
, const char *line
,
591 struct inquire_s
**result
)
593 struct inquire_s inq
= {0};
594 struct stat st
= {0};
598 if (fstat(fd
, &st
) == -1)
599 return gpg_error_from_syserror();
601 inq
.size
= st
.st_size
;
605 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
607 return GPG_ERR_ENOMEM
;
610 char *s
= escape(line
);
612 if (strlen(s
) >= ASSUAN_LINELENGTH
) {
614 return GPG_ERR_LINE_TOO_LONG
;
617 strncpy(inq
.line
, s
, ASSUAN_LINELENGTH
-1);
622 rc
= pwmd_setopt(pwm
, PWMD_OPTION_INQUIRE_TOTAL
,
623 st
.st_size
? st
.st_size
+strlen(inq
.line
) : 0);
630 *result
= pwmd_malloc(sizeof(struct inquire_s
));
632 if ((*result
)->fd
!= -1 && (*result
)->fd
!= STDIN_FILENO
)
633 close((*result
)->fd
);
635 pwmd_free((*result
)->line
);
636 (*result
)->line
= NULL
;
641 memcpy(*result
, &inq
, sizeof(struct inquire_s
));
642 memset(&inq
, 0, sizeof(struct inquire_s
));
646 #ifdef HAVE_LIBREADLINE
647 static int interactive_hook(void)
649 interactive_error
= process_cmd();
651 if (interactive_error
)
652 rl_event_hook
= NULL
;
657 static void print_help()
660 "------------------------------------------------------------------------------\n"
661 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
663 "Type HELP for protocol commands. Type .help for pwmc commands. Press CTRL-D\n"
665 "------------------------------------------------------------------------------\n"
670 static char *parse_arg(const char *src
, char *dst
, size_t len
)
676 for (; s
&& *s
&& *s
!= ' ' && n
< len
; s
++, n
++)
684 static gpg_error_t
read_command(const char *line
, char **result
, size_t *len
)
686 const char *p
= line
;
688 gpg_error_t rc
= GPG_ERR_SYNTAX
;
689 char filebuf
[ASSUAN_LINELENGTH
];
690 char *filename
= NULL
;
691 struct inquire_s
*inq
= NULL
;
693 if (p
&& *p
&& *++p
) {
694 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
695 if (filename
&& *filename
) {
696 p
+= strlen(filename
)+1;
698 while (*p
&& isspace(*p
))
707 fprintf(stderr
, N_("Usage: .read <filename> <command> [args]\n"));
711 fd
= open(filename
, O_RDONLY
);
713 return gpg_error_from_syserror();
715 rc
= set_inquire(fd
, NULL
, &inq
);
722 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, p
);
727 static gpg_error_t
redir_command(const char *line
)
729 const char *p
= line
;
731 gpg_error_t rc
= GPG_ERR_SYNTAX
;
732 char filebuf
[ASSUAN_LINELENGTH
];
733 char *filename
= NULL
;
734 struct inquire_s
*inq
= NULL
;
738 if (p
&& *p
&& *++p
) {
739 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
740 if (filename
&& *filename
) {
741 p
+= strlen(filename
)+1;
743 while (*p
&& isspace(*p
))
752 fprintf(stderr
, N_("Usage: .redir <filename> <command> [args]\n"));
756 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
758 return gpg_error_from_syserror();
760 #ifdef HAVE_LIBREADLINE
761 rc
= set_inquire(interactive
? STDIN_FILENO
: inquirefd
, NULL
, &inq
);
763 rc
= set_inquire(inquirefd
, NULL
, &inq
);
770 rc
= parse_dotcommand(p
, &result
, &len
, inq
);
771 if (!rc
&& result
&& len
--) { // null byte which is always appended
772 if (write(fd
, result
, len
) != len
)
773 rc
= GPG_ERR_TOO_SHORT
;
782 static gpg_error_t
help_command(const char *line
)
784 fprintf(stderr
, N_("Type HELP for protocol commands. Available pwmc commands:\n\n"
785 " .redir <filename> <command>\n"
786 " redirect the output of a command to the specified file\n"
788 " .open <filename>\n"
789 " open the specified filename losing any changes to the current one\n"
791 " .read <filename> <command> [args]\n"
792 " obtain data from the specified filename for an inquire command\n"
794 " .set help | <name> [<value>]\n"
795 " set option <name> to <value>\n"
798 " this help text\n"));
802 static gpg_error_t
open_command(const char *line
)
804 struct inquire_s
*inq
= NULL
;
805 const char *filename
= line
;
808 while (filename
&& isspace(*filename
))
811 if (!filename
|| !*filename
) {
812 fprintf(stderr
, N_("Usage: .open <filename>\n"));
813 return GPG_ERR_SYNTAX
;
816 fprintf(stderr
, N_("Opening data file \"%s\" ...\n"), filename
);
817 #ifdef HAVE_LIBREADLINE
818 rc
= set_inquire(interactive
? STDIN_FILENO
: -1, NULL
, &inq
);
820 rc
= set_inquire(-1, NULL
, &inq
);
826 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
828 #ifdef HAVE_LIBREADLINE
829 // For safety. This file may be a different one. Make the user set it
832 pwmd_free(new_keyfile
);
837 rc
= pwmd_open(pwm
, filename
, inquire_cb
, inq
);
842 static gpg_error_t
set_command(const char *line
)
845 char name
[256] = {0};
846 char value
[512] = {0};
849 const char *p
= line
;
851 while (p
&& *p
&& isspace(*p
))
854 namep
= parse_arg(p
, name
, sizeof(name
));
855 if (!namep
|| !*namep
) {
856 fprintf(stderr
, N_("Usage: .set help | <name> [<value>]\n"));
857 return GPG_ERR_SYNTAX
;
861 while (p
&& *p
&& isspace(*p
))
864 valuep
= parse_arg(p
, value
, sizeof(value
));
866 if (!strcmp(name
, "keyfile") || !strcmp(name
, "new-keyfile")) {
867 int is_newkeyfile
= 1;
869 if (!strcmp(name
, "keyfile"))
873 pwmd_free(new_keyfile
);
883 new_keyfile
= pwmd_strdup(value
);
885 keyfile
= pwmd_strdup(value
);
887 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "AGENT option pinentry-mode=loopback");
889 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 1);
890 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
893 else if (!local_pin
&& !no_pinentry
) {
896 pwmd_socket_type(pwm
, &t
);
897 if (t
== PWMD_SOCKET_LOCAL
) {
898 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
899 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
903 else if (!strcmp(name
, "help")) {
905 "Set a libpwmd or pwmc option. The option name and optional value is space\n"
906 "delimited. When no value is specified the option is unset.\n\n"
907 "keyfile [<filename>]\n"
908 " set or unset the keyfile to be used when a passphrase is required\n"
910 "new-keyfile [<filename>]\n"
911 " set or unset the keyfile to be used when a new passphrase is required\n"
915 rc
= GPG_ERR_UNKNOWN_OPTION
;
920 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
921 size_t *len
, struct inquire_s
*inq
)
923 const char *p
= line
;
926 if (!strncmp(p
, ".read", 5))
927 rc
= read_command(p
+5, result
, len
);
928 else if (!strncmp(p
, ".redir", 6))
929 rc
= redir_command(p
+6);
930 else if (!strncmp(p
, ".help", 5))
931 rc
= help_command(p
+5);
932 else if (!strncmp(p
, ".open", 5))
933 rc
= open_command(p
+5);
934 else if (!strncmp(p
, ".set", 4))
935 rc
= set_command(p
+4);
937 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, line
);
942 #ifdef HAVE_LIBREADLINE
943 static gpg_error_t
do_interactive()
945 gpg_error_t rc
= process_cmd();
946 struct inquire_s
*inq
= NULL
;
951 rc
= set_inquire(STDIN_FILENO
, NULL
, &inq
);
955 fprintf(stderr
, N_("WARNING: interactive mode doesn't use secure memory!\n"));
958 rl_event_hook
= &interactive_hook
;
959 rl_set_keyboard_input_timeout(100000);
966 line
= readline("pwm> ");
967 if (interactive_error
) {
968 rc
= interactive_error
;
977 if (gpg_err_code(rc
) != GPG_ERR_CANCELED
&&
978 gpg_err_code(rc
) != GPG_ERR_EOF
)
979 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
988 #ifdef HAVE_LIBREADLINE
992 rc
= parse_dotcommand(line
, &result
, &len
, inq
);
995 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
996 else if (result
&& len
)
997 printf("%s%s", result
, result
[len
-1] != '\n' ? "\n" : "");
1007 static char *itoa(long long int n
)
1009 static char buf
[64];
1011 snprintf(buf
, sizeof(buf
), "%llu", n
);
1015 static gpg_error_t
finalize()
1018 #ifdef HAVE_LIBREADLINE
1021 if (!force_save
&& interactive
) {
1025 fprintf(stderr
, "\n");
1028 fprintf(stderr
, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
1029 p
= fgets(buf
, sizeof(buf
), stdin
);
1041 return GPG_ERR_CANCELED
;
1045 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1046 "CLEARCACHE %s", filename
);
1061 } while (!finished
);
1065 if (save
&& !filename
) {
1066 fprintf(stderr
, N_("No filename was specified on the command line. Aborting.\n"));
1067 return GPG_ERR_CANCELED
;
1070 if (save
&& filename
) {
1071 char *args
= pwmd_strdup_printf("%s%s %s%s %s %s%s %s%s --s2k-count=%lu",
1072 keygrip
? "--keygrip=" : "", keygrip
? keygrip
: "",
1073 sign_keygrip
? "--sign-keygrip=" : "", sign_keygrip
? sign_keygrip
: "",
1074 no_passphrase
? "--no-passphrase" : "",
1075 cipher
? "--cipher=" : "", cipher
? cipher
: "",
1076 iterations_arg
? "--cipher-iterations=" : "", iterations_arg
? itoa(iterations
) : "",
1079 fprintf(stderr
, "\n");
1080 fprintf(stderr
, N_("Saving changes ...\n"));
1081 rc
= pwmd_save(pwm
, args
, inquire_cb
, NULL
);
1085 no_passphrase
= 0; // reset to avoid usage error on the next SAVE
1088 #ifdef HAVE_LIBREADLINE
1090 return rc
? rc
: quit
? 0 : GPG_ERR_CANCELED
;
1096 int main(int argc
, char *argv
[])
1101 char command
[ASSUAN_LINELENGTH
], *p
= NULL
;
1102 char *result
= NULL
;
1104 char *pinentry_path
= NULL
;
1105 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
1107 int outfd
= STDOUT_FILENO
;
1108 FILE *outfp
= stdout
;
1109 FILE *inquirefp
= stdin
;
1110 int show_status
= 1;
1111 char *clientname
= "pwmc";
1112 char *inquire
= NULL
;
1113 char *inquire_line
= NULL
;
1116 int use_ssh_agent
= 1;
1117 long ssh_timeout
= 0;
1118 int ssh_keepalive
= 0;
1119 char *knownhosts
= NULL
;
1120 char *identity
= NULL
;
1123 char *cacert
= NULL
;
1124 char *clientcert
= NULL
;
1125 char *clientkey
= NULL
;
1129 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1130 pwmd_socket_t socktype
;
1132 int lock_on_open
= 1;
1134 /* The order is important. */
1137 OPT_USE_SSH_AGENT
, OPT_IDENTITY
, OPT_KNOWNHOSTS
, OPT_SSH_TIMEOUT
,
1141 OPT_CACERT
, OPT_CLIENTCERT
, OPT_CLIENTKEY
, OPT_PRIORITY
, OPT_VERIFY
,
1143 OPT_URL
, OPT_LOCAL
, OPT_FORCE_SAVE
, OPT_TTYNAME
, OPT_TTYTYPE
,
1144 OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
, OPT_TIMEOUT
, OPT_TRIES
,
1145 OPT_PINENTRY
, OPT_KEYFILE
, OPT_NEW_KEYFILE
, OPT_NOLOCK
, OPT_SAVE
,
1146 OPT_OUTPUT_FD
, OPT_INQUIRE
, OPT_INQUIRE_FD
, OPT_INQUIRE_FILE
,
1147 OPT_INQUIRE_LINE
, OPT_NO_STATUS
, OPT_NAME
, OPT_VERSION
, OPT_HELP
,
1148 OPT_KEYGRIP
, OPT_SIGN_KEYGRIP
, OPT_NOPASSPHRASE
, OPT_CIPHER
,
1149 OPT_KEYPARAMS
, OPT_NO_PINENTRY
, OPT_S2K_COUNT
, OPT_ITERATIONS
,
1150 #ifdef HAVE_LIBREADLINE
1154 const struct option long_opts
[] = {
1156 { "no-ssh-agent", 0, 0, 0 },
1157 { "identity", 1, 0, 'i' },
1158 { "knownhosts", 1, 0, 'k' },
1159 { "ssh-timeout", 1, 0, 0 },
1160 { "ssh-keepalive", 1, 0, 0 },
1163 { "ca-cert", 1, 0, 0 },
1164 { "client-cert", 1, 0, 0 },
1165 { "client-key", 1, 0, 0 },
1166 { "tls-priority", 1, 0, 0 },
1167 { "tls-verify", 0, 0, 0 },
1170 { "local-pinentry", 0, 0 },
1171 { "force-save", 0, 0 },
1172 { "ttyname", 1, 0, 'y' },
1173 { "ttytype", 1, 0, 't' },
1174 { "display", 1, 0, 'd' },
1175 { "lc-ctype", 1, 0, 0 },
1176 { "lc-messages", 1, 0, 0 },
1177 { "timeout", 1, 0, 0 },
1178 { "tries", 1, 0, 0 },
1179 { "pinentry", 1, 0, 0 },
1180 { "key-file", 1, 0, 0 },
1181 { "new-key-file", 1, 0, 0 },
1182 { "no-lock", 0, 0, 0 },
1183 { "save", 0, 0, 'S' },
1184 { "output-fd", 1, 0, 0 },
1185 { "inquire", 1, 0, 0 },
1186 { "inquire-fd", 1, 0, 0 },
1187 { "inquire-file", 1, 0, 0 },
1188 { "inquire-line", 1, 0, 'L' },
1189 { "no-status", 0, 0, 0 },
1190 { "name", 1, 0, 'n' },
1191 { "version", 0, 0, 0 },
1192 { "help", 0, 0, 0 },
1193 { "keygrip", 1, 0, 0 },
1194 { "sign-keygrip", 1, 0, 0 },
1195 { "no-passphrase", 0, 0, 0 },
1196 { "cipher", 1, 0, 0 },
1197 { "key-params", 1, 0, 0 },
1198 { "no-pinentry", 0, 0, 0 },
1199 { "s2k-count", 1, 0, 0 },
1200 { "cipher-iterations", 1, 0, 0 },
1201 #ifdef HAVE_LIBREADLINE
1202 { "interactive", 0, 0 },
1207 const char *optstring
= "L:y:t:d:P:I:Sn:i:k:";
1209 const char *optstring
= "L:y:t:d:P:I:Sn:";
1214 setlocale(LC_ALL
, "");
1215 bindtextdomain("libpwmd", LOCALEDIR
);
1218 #ifdef HAVE_LIBREADLINE
1219 if (!strcmp(basename(argv
[0]), "pwmsh"))
1223 tries
= DEFAULT_PIN_TRIES
;
1224 inquirefd
= STDIN_FILENO
;
1226 while ((opt
= getopt_long(argc
, argv
, optstring
, long_opts
, &opt_index
)) != -1) {
1228 /* Handle long options without a short option part. */
1230 switch (opt_index
) {
1232 case OPT_USE_SSH_AGENT
:
1235 case OPT_SSH_TIMEOUT
:
1236 ssh_timeout
= strtol(optarg
, &p
, 10);
1238 case OPT_SSH_KEEPALIVE
:
1239 ssh_keepalive
= strtol(optarg
, &p
, 10);
1246 case OPT_CLIENTCERT
:
1247 clientcert
= optarg
;
1263 keyfile
= pwmd_strdup(optarg
);
1265 case OPT_NEW_KEYFILE
:
1266 new_keyfile
= pwmd_strdup(optarg
);
1277 case OPT_FORCE_SAVE
:
1278 save
= force_save
= 1;
1281 lcctype
= pwmd_strdup(optarg
);
1283 case OPT_LC_MESSAGES
:
1284 lcmessages
= pwmd_strdup(optarg
);
1287 timeout
= strtol(optarg
, &p
, 10);
1290 tries
= strtol(optarg
, &p
, 10);
1295 case OPT_INQUIRE_FD
:
1296 inquirefd
= strtol(optarg
, &p
, 10);
1298 inquirefp
= fdopen(inquirefd
, "r");
1300 pwmd_free(password
);
1301 err(EXIT_FAILURE
, "%i", inquirefd
);
1305 case OPT_INQUIRE_FILE
:
1306 inquirefd
= open(optarg
, O_RDONLY
);
1307 if (!inquirefd
== -1) {
1308 pwmd_free(password
);
1309 err(EXIT_FAILURE
, "%s", optarg
);
1311 inquirefp
= fdopen(inquirefd
, "r");
1314 outfd
= strtol(optarg
, &p
, 10);
1316 outfp
= fdopen(outfd
, "w");
1318 pwmd_free(password
);
1319 err(EXIT_FAILURE
, "%i", outfd
);
1327 pwmd_free(password
);
1328 printf("%s (pwmc)\n\n"
1329 "Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012\n"
1331 "Released under the terms of the GPL v2. Use at your own risk.\n\n"
1332 "Compile-time features:\n"
1343 #ifdef WITH_PINENTRY
1358 #ifdef HAVE_LIBREADLINE
1364 , PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1367 pinentry_path
= optarg
;
1370 usage(argv
[0], EXIT_SUCCESS
);
1374 case OPT_SIGN_KEYGRIP
:
1375 sign_keygrip
= optarg
;
1378 s2k_count
= strtoul(optarg
, &p
, 10);
1380 case OPT_ITERATIONS
:
1381 iterations
= strtoull(optarg
, &p
, 10);
1384 case OPT_NOPASSPHRASE
:
1390 case OPT_NO_PINENTRY
:
1393 #ifdef HAVE_LIBREADLINE
1394 case OPT_INTERACTIVE
:
1399 usage(argv
[0], EXIT_FAILURE
);
1403 fprintf(stderr
, N_("%s: invalid argument for option '--%s'\n"),
1404 argv
[0], long_opts
[opt_index
].name
);
1405 usage(argv
[0], EXIT_FAILURE
);
1414 knownhosts
= optarg
;
1418 inquire_line
= optarg
;
1433 clientname
= optarg
;
1436 pwmd_free(password
);
1437 usage(argv
[0], EXIT_FAILURE
);
1441 #ifdef HAVE_LIBREADLINE
1442 if (interactive
&& !isatty(STDIN_FILENO
)) {
1443 pwmd_free(password
);
1444 usage(argv
[0], EXIT_FAILURE
);
1446 else if (isatty(STDIN_FILENO
) && !inquire
&& !inquire_line
)
1450 filename
= argv
[optind
];
1452 gnutls_global_set_mem_functions(pwmd_malloc
, pwmd_malloc
, NULL
,
1453 pwmd_realloc
, pwmd_free
);
1456 rc
= pwmd_new(clientname
, &pwm
);
1460 pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
1461 fprintf(stderr
, N_("Connecting ...\n"));
1463 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1464 socktype
= is_remote_url(url
);
1465 if (socktype
!= PWMD_SOCKET_LOCAL
) {
1467 if (socktype
== PWMD_SOCKET_SSH
) {
1469 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_CB
, knownhost_cb
);
1473 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_DATA
, clientname
);
1477 if (!getenv("SSH_AUTH_SOCK") || identity
)
1480 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_AGENT
, use_ssh_agent
);
1484 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_TIMEOUT
, ssh_timeout
);
1488 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_KEEPALIVE
, ssh_keepalive
);
1492 rc
= pwmd_connect(pwm
, url
, identity
, knownhosts
);
1497 rc
= pwmd_setopt(pwm
, PWMD_OPTION_TLS_VERIFY
, tls_verify
);
1501 rc
= pwmd_connect(pwm
, url
, clientcert
, clientkey
, cacert
, prio
);
1506 rc
= pwmd_connect(pwm
, url
);
1508 rc
= pwmd_connect(pwm
, url
);
1513 fprintf(stderr
, N_("Connected.\n"));
1516 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCK_ON_OPEN
, 1);
1521 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, NULL
);
1526 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
1531 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, no_pinentry
);
1535 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCAL_PINENTRY
,
1536 (local_pin
|| keyfile
|| new_keyfile
));
1540 if (pinentry_path
) {
1541 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
1547 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
1553 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
1559 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
1565 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
1571 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
1578 rc
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
1584 rc
= open_command(filename
);
1589 #ifdef HAVE_LIBREADLINE
1594 rc
= do_interactive();
1600 struct inquire_s
*inq
= NULL
;
1602 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1604 rc
= pwmd_command(pwm
, &result
, &len
, inquire_cb
, inq
, inquire
);
1610 fcntl(STDIN_FILENO
, F_SETFL
, O_NONBLOCK
);
1619 n
= read(STDIN_FILENO
, command
, sizeof(command
));
1621 if (errno
== EAGAIN
) {
1626 rc
= gpg_error_from_errno(errno
);
1637 if (!p
|| !*p
|| !strcasecmp(p
, "BYE"))
1641 struct inquire_s
*inq
= NULL
;
1642 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1644 rc
= parse_dotcommand(command
, &result
, &len
, inq
);
1653 fwrite(result
, 1, result
[len
-1] == 0 ? len
-1 : len
, outfp
);
1658 pwmd_free(password
);
1663 #ifdef HAVE_LIBREADLINE
1666 memset(command
, 0, sizeof(command
));
1669 pwmd_free(new_keyfile
);
1676 fprintf(stderr
, N_("Connection closed.\n"));
1678 exit(rc
? EXIT_FAILURE
: EXIT_SUCCESS
);