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
;
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 error
)
127 fprintf(stderr
, "ERR %i %s\n", error
, gpg_strerror(error
));
130 static void usage(const char *pn
, int status
)
132 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
133 "Read a PWMD protocol command from standard input.\n\n"
134 "Usage: pwmc [options] [file]\n"
136 " a url string to connect to (see below)\n"
139 " disable SSH agent use (enabled when SSH_AUTH_SOCK is set)\n"
140 " --identity, -i <filename>\n"
141 " the ssh identity file to use for authentication\n"
142 " --knownhosts, -k <filename>\n"
143 " the ssh knownhosts file to use (~/.ssh/knownhosts)\n"
144 " --ssh-timeout <seconds>\n"
145 " timeout before a remote command fails (0=disabled, default)\n"
146 " --ssh-keepalive <seconds>\n"
147 " interval to send a keepalive to the remote server (0=disabled, default)\n"
150 " --ca-cert <filename>\n"
151 " certificate authority (CA) used to sign the server cert\n"
152 " --client-cert <filename>\n"
153 " client certificate to use for authentication\n"
154 " --client-key <filename>\n"
155 " key file used to protect the client certificate\n"
156 " --tls-priority <string>\n"
157 " compression, cipher and hash algorithm string (SECURE256)\n"
159 " verify the hostname against the server certificate\n"
162 " do not lock the data file upon opening it\n"
163 " --lock-timeout <N>\n"
164 " time in tenths of a second to wait for a locked data file\n"
166 " disable showing of status messages from the server\n"
167 " --name, -n <string>\n"
168 " set the client name\n"
170 " do not require a passphrase when saving a new file\n"
171 " --keygrip <string>\n"
172 " the hex string of the keygrip to save to\n"
173 " --sign-keygrip <string>\n"
174 " the hex string of the keygrip to sign with\n"
175 " --key-file <filename>\n"
176 " obtain the passphrase from the specified filename\n"
177 " --new-key-file <filename>\n"
178 " obtain the passphrase to save with from the specified filename\n"
180 " the number of times to hash the passphrase for a new file\n"
181 " --cipher-iterations <N>\n"
182 " the number of times to encrypt the XML data (N+1)\n"
183 " --timeout <seconds>\n"
184 " pinentry timeout\n"
186 " number of pinentry tries before failing (3)\n"
188 " disable pinentry both remotely and locally\n"
189 " --pinentry <path>\n"
190 " the full path to the pinentry binary (server default)\n"
191 " --ttyname, -y <path>\n"
192 " tty that pinentry will use\n"
193 " --ttytype, -t <string>\n"
194 " pinentry terminal type (default is $TERM)\n"
196 " pinentry display (default is $DISPLAY)\n"
197 " --lc-ctype <string>\n"
198 " locale setting for pinentry\n"
199 " --lc-messages <string>\n"
200 " locale setting for pinentry\n"
201 " --local-pinentry\n"
202 " force using a local pinentry\n"
203 #ifdef HAVE_LIBREADLINE
205 " use a shell like interface to pwmd (allows more than one command)\n"
207 " --output-fd <FD>\n"
208 " redirect command output to the specified file descriptor\n"
209 " --inquire <COMMAND>\n"
210 " the specified command (with any options) uses a server inquire while\n"
211 " command data is read via the inquire file descriptor (stdin)\n"
212 " --inquire-fd <FD>\n"
213 " read inquire data from the specified file descriptor (stdin)\n"
214 " --inquire-file <filename>\n"
215 " read inquire data from the specified filename\n"
216 " --inquire-line, -L <STRING>\n"
217 " the initial line to send (i.e., element path) before the inquire data\n"
218 " --cipher <string>\n"
219 " the cipher to use when saving (see pwmd(1))\n"
221 " send the SAVE command before exiting\n"
222 " --key-params <string>\n"
223 " the key parameters to use when saving a new file (pwmd default)\n"
225 " like --save but always ask for a passphrase\n"
228 fprintf(status
== EXIT_FAILURE
? stderr
: stdout
, N_(
230 "An optional url may be in the form of:\n"
231 " --url /path/to/socket\n"
232 " --url file://[path/to/socket]\n"
235 " --url ssh[46]://[username@]hostname[:port]\n"
236 " --no-ssh-agent -i identity_file --url ssh[46]://[username@]hostname[:port]\n"
240 " --url tls[46]://hostname[:port]\n"
246 static gpg_error_t
inquire_cb(void *user
, const char *keyword
, gpg_error_t rc
,
247 char **data
, size_t *size
)
249 struct inquire_s
*inq
= user
;
251 int is_newpassword
= 0;
259 if (!strcmp(keyword
, "PASSPHRASE"))
261 else if (!strcmp(keyword
, "NEW_PASSPHRASE"))
263 #ifdef HAVE_LIBREADLINE
264 else if (!strcmp(keyword
, "KEYPARAM") && !interactive
) {
266 else if (!strcmp(keyword
, "KEYPARAM")) {
268 if (!keyparams
|| !*keyparams
)
269 return gpg_error(GPG_ERR_INV_PARAMETER
);
272 *size
= strlen(keyparams
);
273 return gpg_error(GPG_ERR_EOF
);
276 if ((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
)) {
277 int fd
= open(is_password
? keyfile
: new_keyfile
, O_RDONLY
);
280 fprintf(stderr
, "%s: %s\n", is_newpassword
? new_keyfile
: keyfile
,
282 return gpg_error_from_syserror();
285 rc
= set_inquire(fd
, NULL
, &inq
);
291 fprintf(stderr
, N_("Using keyfile '%s' as %s.\n"),
292 is_newpassword
? new_keyfile
: keyfile
, keyword
);
294 if (!new_keyfile
|| is_newpassword
) {
297 pwmd_socket_type(pwm
, &t
);
298 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
299 if (!no_pinentry
&& t
== PWMD_SOCKET_LOCAL
)
300 pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
303 else if (is_password
&& !keyfile
) {
306 rc
= pwmd_password(pwm
, keyword
, &tmp
, &inq
->len
);
307 if (rc
&& gpg_err_code(rc
) != GPG_ERR_EOF
)
310 pwmd_free(inq
->line
);
314 return gpg_error(GPG_ERR_EOF
);
316 #ifdef HAVE_LIBREADLINE
317 else if (!inq
->header
&& interactive
) {
319 "Press CTRL-D to send the current line. Press twice to end. %s:\n"), keyword
);
324 /* The first part of the command data. */
329 return inq
->fd
== -1 ? gpg_error(GPG_ERR_EOF
) : 0;
332 *size
= read(inq
->fd
, inq
->line
, ASSUAN_LINELENGTH
);
335 return gpg_error(gpg_error_from_syserror());
340 if (((is_newpassword
&& new_keyfile
) || (is_password
&& keyfile
))
341 && *size
== inq
->size
)
342 return gpg_error(GPG_ERR_EOF
);
344 return *size
? 0 : gpg_error(GPG_ERR_EOF
);
347 static int status_msg_cb(void *data
, const char *line
)
349 char *p
= strchr(line
, ' ');
351 if (!strcmp(line
, "KEEPALIVE"))
354 if (*line
!= '#' && p
&& strchr(p
, ' ') && *++p
) {
355 char *p1
= strchr(p
, ' ');
356 int a
= strtol(p
, NULL
, 10);
358 if (isdigit(*p
) && p1
) {
359 int b
= strtol(p1
, NULL
, 10);
361 int t
= a
&& b
? a
*100/b
: 0;
363 strncpy(l
, line
, strlen(line
)-strlen(p
)-1);
364 fprintf(stderr
, "\r%s %i/%i %i%%%s", l
, a
, b
, t
, a
== b
? "\n" : "");
370 fprintf(stderr
, "%s\n", line
);
376 static gpg_error_t
process_cmd()
378 return pwmd_process(pwm
);
382 static gpg_error_t
get_password(char **result
, pwmd_pinentry_t w
, int echo
)
384 char buf
[LINE_MAX
] = {0}, *p
;
385 struct termios told
, tnew
;
390 if (!isatty(STDIN_FILENO
)) {
391 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
392 return GPG_ERR_ENOTTY
;
396 if (tcgetattr(STDIN_FILENO
, &told
) == -1)
397 return gpg_error_from_syserror();
399 memcpy(&tnew
, &told
, sizeof(struct termios
));
400 tnew
.c_lflag
&= ~(ECHO
);
401 tnew
.c_lflag
|= ICANON
|ECHONL
;
403 if (tcsetattr(STDIN_FILENO
, TCSANOW
, &tnew
) == -1) {
406 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
407 return gpg_error_from_errno(n
);
412 case PWMD_PINENTRY_OPEN
:
413 fprintf(stderr
, N_("Password for %s: "), filename
);
415 case PWMD_PINENTRY_OPEN_FAILED
:
416 fprintf(stderr
, N_("Invalid password. Password for %s: "), filename
);
418 case PWMD_PINENTRY_SAVE
:
419 fprintf(stderr
, N_("New password for %s: "), filename
);
421 case PWMD_PINENTRY_SAVE_CONFIRM
:
422 fprintf(stderr
, N_("Confirm password: "));
428 if ((p
= fgets(buf
, sizeof(buf
), stdin
)) == NULL
) {
429 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
434 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
438 return GPG_ERR_CANCELED
;
441 p
[strlen(p
) - 1] = 0;
444 key
= pwmd_strdup_printf("%s", p
);
445 memset(&buf
, 0, sizeof(buf
));
448 return GPG_ERR_ENOMEM
;
455 static gpg_error_t
knownhost_cb(void *data
, const char *host
, const char *key
,
459 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
);
461 if (no_pinentry
&& !isatty(STDIN_FILENO
)) {
462 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
464 return GPG_ERR_ENOTTY
;
466 else if (no_pinentry
) {
467 for (char *p
= buf
, len
= 0; *p
; p
++, len
++) {
474 while (!isspace(*(--p
)));
481 fprintf(stderr
, "%s\n\n", buf
);
487 fprintf(stderr
, N_("Trust this connection [y/N]: "));
489 rc
= get_password(&result
, PWMD_PINENTRY_CONFIRM
, 1);
494 if (!result
|| !*result
|| *result
== 'n' || *result
== 'N') {
495 if (result
&& *result
)
498 return GPG_ERR_NOT_CONFIRMED
;
501 if ((*result
== 'y' || *result
== 'Y') && *(result
+1) == 0) {
508 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, buf
);
514 return pwmd_getpin(pwm
, NULL
, NULL
, NULL
, PWMD_PINENTRY_CONFIRM
);
518 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
519 static pwmd_socket_t
is_remote_url(const char *str
)
522 return PWMD_SOCKET_LOCAL
;
525 if (strstr(str
, "ssh://") || strstr(str
, "ssh4://")
526 || strstr(str
, "ssh6://"))
527 return PWMD_SOCKET_SSH
;
531 if (strstr(str
, "tls://") || strstr(str
, "tls4://")
532 || strstr(str
, "tls6://"))
533 return PWMD_SOCKET_TLS
;
536 return PWMD_SOCKET_LOCAL
;
540 static char *escape(const char *str
)
543 char *buf
= pwmd_malloc(ASSUAN_LINELENGTH
+1), *b
= buf
;
546 for (p
= str
; *p
; p
++, len
++) {
547 if (len
== ASSUAN_LINELENGTH
)
588 static void free_inquire(struct inquire_s
*inq
)
593 pwmd_free(inq
->line
);
595 if (inq
->fd
!= -1 && inq
->fd
!= STDIN_FILENO
)
601 /* When *result is not NULL it is updated to the new values and not
603 static gpg_error_t
set_inquire(int fd
, const char *line
,
604 struct inquire_s
**result
)
606 struct inquire_s inq
= {0};
607 struct stat st
= {0};
611 if (fstat(fd
, &st
) == -1)
612 return gpg_error_from_syserror();
614 inq
.size
= st
.st_size
;
618 inq
.line
= pwmd_calloc(1, ASSUAN_LINELENGTH
);
620 return GPG_ERR_ENOMEM
;
623 char *s
= escape(line
);
625 if (strlen(s
) >= ASSUAN_LINELENGTH
) {
627 return GPG_ERR_LINE_TOO_LONG
;
630 strncpy(inq
.line
, s
, ASSUAN_LINELENGTH
-1);
635 rc
= pwmd_setopt(pwm
, PWMD_OPTION_INQUIRE_TOTAL
,
636 st
.st_size
? st
.st_size
+strlen(inq
.line
) : 0);
643 *result
= pwmd_malloc(sizeof(struct inquire_s
));
645 if ((*result
)->fd
!= -1 && (*result
)->fd
!= STDIN_FILENO
)
646 close((*result
)->fd
);
648 pwmd_free((*result
)->line
);
649 (*result
)->line
= NULL
;
654 memcpy(*result
, &inq
, sizeof(struct inquire_s
));
655 memset(&inq
, 0, sizeof(struct inquire_s
));
659 #ifdef HAVE_LIBREADLINE
660 static int interactive_hook(void)
662 interactive_error
= process_cmd();
664 if (interactive_error
)
665 rl_event_hook
= NULL
;
670 static void print_help()
673 "------------------------------------------------------------------------------\n"
674 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
676 "Type HELP for protocol commands. Type .help for pwmc commands. Press CTRL-D\n"
678 "------------------------------------------------------------------------------\n"
683 static char *parse_arg(const char *src
, char *dst
, size_t len
)
689 for (; s
&& *s
&& *s
!= ' ' && n
< len
; s
++, n
++)
697 static gpg_error_t
read_command(const char *line
, char **result
, size_t *len
)
699 const char *p
= line
;
701 gpg_error_t rc
= GPG_ERR_SYNTAX
;
702 char filebuf
[ASSUAN_LINELENGTH
];
703 char *filename
= NULL
;
704 struct inquire_s
*inq
= NULL
;
706 if (p
&& *p
&& *++p
) {
707 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
708 if (filename
&& *filename
) {
709 p
+= strlen(filename
)+1;
711 while (*p
&& isspace(*p
))
720 fprintf(stderr
, N_("Usage: .read <filename> <command> [args]\n"));
724 fd
= open(filename
, O_RDONLY
);
726 return gpg_error_from_syserror();
728 rc
= set_inquire(fd
, NULL
, &inq
);
735 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, p
);
740 static gpg_error_t
redir_command(const char *line
)
742 const char *p
= line
;
744 gpg_error_t rc
= GPG_ERR_SYNTAX
;
745 char filebuf
[ASSUAN_LINELENGTH
];
746 char *filename
= NULL
;
747 struct inquire_s
*inq
= NULL
;
751 if (p
&& *p
&& *++p
) {
752 filename
= parse_arg(p
, filebuf
, sizeof(filebuf
));
753 if (filename
&& *filename
) {
754 p
+= strlen(filename
)+1;
756 while (*p
&& isspace(*p
))
765 fprintf(stderr
, N_("Usage: .redir <filename> <command> [args]\n"));
769 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
771 return gpg_error_from_syserror();
773 #ifdef HAVE_LIBREADLINE
774 rc
= set_inquire(interactive
? STDIN_FILENO
: inquirefd
, NULL
, &inq
);
776 rc
= set_inquire(inquirefd
, NULL
, &inq
);
783 rc
= parse_dotcommand(p
, &result
, &len
, inq
);
784 if (!rc
&& result
&& len
--) { // null byte which is always appended
785 if (write(fd
, result
, len
) != len
)
786 rc
= GPG_ERR_TOO_SHORT
;
795 static gpg_error_t
help_command(const char *line
)
797 fprintf(stderr
, N_("Type HELP for protocol commands. Available pwmc commands:\n\n"
798 " .redir <filename> <command>\n"
799 " redirect the output of a command to the specified file\n"
801 " .open <filename>\n"
802 " open the specified filename losing any changes to the current one\n"
804 " .read <filename> <command> [args]\n"
805 " obtain data from the specified filename for an inquire command\n"
807 " .set help | <name> [<value>]\n"
808 " set option <name> to <value>\n"
811 " this help text\n"));
815 static gpg_error_t
open_command(const char *line
)
817 struct inquire_s
*inq
= NULL
;
818 const char *filename
= line
;
821 while (filename
&& isspace(*filename
))
824 if (!filename
|| !*filename
) {
825 fprintf(stderr
, N_("Usage: .open <filename>\n"));
826 return GPG_ERR_SYNTAX
;
829 fprintf(stderr
, N_("Opening data file \"%s\" ...\n"), filename
);
830 #ifdef HAVE_LIBREADLINE
831 rc
= set_inquire(interactive
? STDIN_FILENO
: -1, NULL
, &inq
);
833 rc
= set_inquire(-1, NULL
, &inq
);
839 pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
841 #ifdef HAVE_LIBREADLINE
842 // For safety. This file may be a different one. Make the user set it
845 pwmd_free(new_keyfile
);
850 rc
= pwmd_open(pwm
, filename
, inquire_cb
, inq
);
855 static gpg_error_t
set_command(const char *line
)
858 char name
[256] = {0};
859 char value
[512] = {0};
862 const char *p
= line
;
864 while (p
&& *p
&& isspace(*p
))
867 namep
= parse_arg(p
, name
, sizeof(name
));
868 if (!namep
|| !*namep
) {
869 fprintf(stderr
, N_("Usage: .set help | <name> [<value>]\n"));
870 return GPG_ERR_SYNTAX
;
874 while (p
&& *p
&& isspace(*p
))
877 valuep
= parse_arg(p
, value
, sizeof(value
));
879 if (!strcmp(name
, "keyfile") || !strcmp(name
, "new-keyfile")) {
880 int is_newkeyfile
= 1;
882 if (!strcmp(name
, "keyfile"))
886 pwmd_free(new_keyfile
);
896 new_keyfile
= pwmd_strdup(value
);
898 keyfile
= pwmd_strdup(value
);
900 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "AGENT option pinentry-mode=loopback");
902 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 1);
903 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 1);
906 else if (!local_pin
&& !no_pinentry
) {
909 pwmd_socket_type(pwm
, &t
);
910 if (t
== PWMD_SOCKET_LOCAL
) {
911 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 0);
912 rc
= pwmd_setopt(pwm
, PWMD_OPTION_OVERRIDE_INQUIRE
, 0);
916 else if (!strcmp(name
, "help")) {
918 "Set a libpwmd or pwmc option. The option name and optional value is space\n"
919 "delimited. When no value is specified the option is unset.\n\n"
920 "keyfile [<filename>]\n"
921 " set or unset the keyfile to be used when a passphrase is required\n"
923 "new-keyfile [<filename>]\n"
924 " set or unset the keyfile to be used when a new passphrase is required\n"
928 rc
= GPG_ERR_UNKNOWN_OPTION
;
933 static gpg_error_t
parse_dotcommand(const char *line
, char **result
,
934 size_t *len
, struct inquire_s
*inq
)
936 const char *p
= line
;
939 if (!strncmp(p
, ".read", 5))
940 rc
= read_command(p
+5, result
, len
);
941 else if (!strncmp(p
, ".redir", 6))
942 rc
= redir_command(p
+6);
943 else if (!strncmp(p
, ".help", 5))
944 rc
= help_command(p
+5);
945 else if (!strncmp(p
, ".open", 5))
946 rc
= open_command(p
+5);
947 else if (!strncmp(p
, ".set", 4))
948 rc
= set_command(p
+4);
950 rc
= pwmd_command(pwm
, result
, len
, inquire_cb
, inq
, line
);
955 #ifdef HAVE_LIBREADLINE
956 static gpg_error_t
do_interactive()
959 struct inquire_s
*inq
= NULL
;
966 rc
= set_inquire(STDIN_FILENO
, NULL
, &inq
);
970 fprintf(stderr
, N_("WARNING: interactive mode doesn't use secure memory!\n"));
972 rl_event_hook
= &interactive_hook
;
973 rl_set_keyboard_input_timeout(100000);
980 line
= readline("pwm> ");
981 if (interactive_error
) {
982 rc
= interactive_error
;
991 if (gpg_err_code(rc
) != GPG_ERR_CANCELED
&&
992 gpg_err_code(rc
) != GPG_ERR_EOF
)
993 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1002 #ifdef HAVE_LIBREADLINE
1006 rc
= parse_dotcommand(line
, &result
, &len
, inq
);
1009 fprintf(stderr
, "ERR %i: %s\n", rc
, gpg_strerror(rc
));
1010 else if (result
&& len
)
1011 printf("%s%s", result
, result
[len
-1] != '\n' ? "\n" : "");
1021 static char *itoa(long long int n
)
1023 static char buf
[64];
1025 snprintf(buf
, sizeof(buf
), "%llu", n
);
1029 static gpg_error_t
finalize()
1032 #ifdef HAVE_LIBREADLINE
1035 if (!force_save
&& interactive
) {
1039 fprintf(stderr
, "\n");
1042 fprintf(stderr
, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
1043 p
= fgets(buf
, sizeof(buf
), stdin
);
1055 return GPG_ERR_CANCELED
;
1059 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1060 "CLEARCACHE %s", filename
);
1075 } while (!finished
);
1079 if (save
&& !filename
) {
1080 fprintf(stderr
, N_("No filename was specified on the command line. Aborting.\n"));
1081 return GPG_ERR_CANCELED
;
1084 if (save
&& filename
) {
1085 char *args
= pwmd_strdup_printf("%s%s %s%s %s %s%s %s%s --s2k-count=%lu",
1086 keygrip
? "--keygrip=" : "", keygrip
? keygrip
: "",
1087 sign_keygrip
? "--sign-keygrip=" : "", sign_keygrip
? sign_keygrip
: "",
1088 no_passphrase
? "--no-passphrase" : "",
1089 cipher
? "--cipher=" : "", cipher
? cipher
: "",
1090 iterations_arg
? "--cipher-iterations=" : "", iterations_arg
? itoa(iterations
) : "",
1093 fprintf(stderr
, "\n");
1094 fprintf(stderr
, N_("Saving changes ...\n"));
1095 rc
= pwmd_save(pwm
, args
, inquire_cb
, NULL
);
1099 no_passphrase
= 0; // reset to avoid usage error on the next SAVE
1102 #ifdef HAVE_LIBREADLINE
1104 return rc
? rc
: quit
? 0 : GPG_ERR_CANCELED
;
1110 int main(int argc
, char *argv
[])
1115 char command
[ASSUAN_LINELENGTH
], *p
= NULL
;
1116 char *result
= NULL
;
1118 char *pinentry_path
= NULL
;
1119 char *display
= NULL
, *tty
= NULL
, *ttytype
= NULL
, *lcctype
= NULL
,
1121 int outfd
= STDOUT_FILENO
;
1122 FILE *outfp
= stdout
;
1123 FILE *inquirefp
= stdin
;
1124 int show_status
= 1;
1125 char *clientname
= "pwmc";
1126 char *inquire
= NULL
;
1127 char *inquire_line
= NULL
;
1130 int use_ssh_agent
= 1;
1131 long ssh_timeout
= 0;
1132 int ssh_keepalive
= 0;
1133 char *knownhosts
= NULL
;
1134 char *identity
= NULL
;
1137 char *cacert
= NULL
;
1138 char *clientcert
= NULL
;
1139 char *clientkey
= NULL
;
1143 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1144 pwmd_socket_t socktype
;
1146 int lock_on_open
= 1;
1147 long lock_timeout
= 0;
1149 /* The order is important. */
1152 OPT_USE_SSH_AGENT
, OPT_IDENTITY
, OPT_KNOWNHOSTS
, OPT_SSH_TIMEOUT
,
1156 OPT_CACERT
, OPT_CLIENTCERT
, OPT_CLIENTKEY
, OPT_PRIORITY
, OPT_VERIFY
,
1158 OPT_URL
, OPT_LOCAL
, OPT_FORCE_SAVE
, OPT_TTYNAME
, OPT_TTYTYPE
,
1159 OPT_DISPLAY
, OPT_LC_CTYPE
, OPT_LC_MESSAGES
, OPT_TIMEOUT
, OPT_TRIES
,
1160 OPT_PINENTRY
, OPT_KEYFILE
, OPT_NEW_KEYFILE
, OPT_NOLOCK
,
1161 OPT_LOCK_TIMEOUT
, OPT_SAVE
, OPT_OUTPUT_FD
, OPT_INQUIRE
,
1162 OPT_INQUIRE_FD
, OPT_INQUIRE_FILE
, OPT_INQUIRE_LINE
, OPT_NO_STATUS
,
1163 OPT_NAME
, OPT_VERSION
, OPT_HELP
, OPT_KEYGRIP
, OPT_SIGN_KEYGRIP
,
1164 OPT_NOPASSPHRASE
, OPT_CIPHER
, OPT_KEYPARAMS
, OPT_NO_PINENTRY
,
1165 OPT_S2K_COUNT
, OPT_ITERATIONS
,
1166 #ifdef HAVE_LIBREADLINE
1170 const struct option long_opts
[] = {
1172 { "no-ssh-agent", 0, 0, 0 },
1173 { "identity", 1, 0, 'i' },
1174 { "knownhosts", 1, 0, 'k' },
1175 { "ssh-timeout", 1, 0, 0 },
1176 { "ssh-keepalive", 1, 0, 0 },
1179 { "ca-cert", 1, 0, 0 },
1180 { "client-cert", 1, 0, 0 },
1181 { "client-key", 1, 0, 0 },
1182 { "tls-priority", 1, 0, 0 },
1183 { "tls-verify", 0, 0, 0 },
1186 { "local-pinentry", 0, 0 },
1187 { "force-save", 0, 0 },
1188 { "ttyname", 1, 0, 'y' },
1189 { "ttytype", 1, 0, 't' },
1190 { "display", 1, 0, 'd' },
1191 { "lc-ctype", 1, 0, 0 },
1192 { "lc-messages", 1, 0, 0 },
1193 { "timeout", 1, 0, 0 },
1194 { "tries", 1, 0, 0 },
1195 { "pinentry", 1, 0, 0 },
1196 { "key-file", 1, 0, 0 },
1197 { "new-key-file", 1, 0, 0 },
1198 { "no-lock", 0, 0, 0 },
1199 { "lock-timeout", 1, 0, 0 },
1200 { "save", 0, 0, 'S' },
1201 { "output-fd", 1, 0, 0 },
1202 { "inquire", 1, 0, 0 },
1203 { "inquire-fd", 1, 0, 0 },
1204 { "inquire-file", 1, 0, 0 },
1205 { "inquire-line", 1, 0, 'L' },
1206 { "no-status", 0, 0, 0 },
1207 { "name", 1, 0, 'n' },
1208 { "version", 0, 0, 0 },
1209 { "help", 0, 0, 0 },
1210 { "keygrip", 1, 0, 0 },
1211 { "sign-keygrip", 1, 0, 0 },
1212 { "no-passphrase", 0, 0, 0 },
1213 { "cipher", 1, 0, 0 },
1214 { "key-params", 1, 0, 0 },
1215 { "no-pinentry", 0, 0, 0 },
1216 { "s2k-count", 1, 0, 0 },
1217 { "cipher-iterations", 1, 0, 0 },
1218 #ifdef HAVE_LIBREADLINE
1219 { "interactive", 0, 0 },
1224 const char *optstring
= "L:y:t:d:P:I:Sn:i:k:";
1226 const char *optstring
= "L:y:t:d:P:I:Sn:";
1231 setlocale(LC_ALL
, "");
1232 bindtextdomain("libpwmd", LOCALEDIR
);
1235 #ifdef HAVE_LIBREADLINE
1236 if (!strcmp(basename(argv
[0]), "pwmsh"))
1240 tries
= DEFAULT_PIN_TRIES
;
1241 inquirefd
= STDIN_FILENO
;
1243 while ((opt
= getopt_long(argc
, argv
, optstring
, long_opts
, &opt_index
)) != -1) {
1245 /* Handle long options without a short option part. */
1247 switch (opt_index
) {
1249 case OPT_USE_SSH_AGENT
:
1252 case OPT_SSH_TIMEOUT
:
1253 ssh_timeout
= strtol(optarg
, &p
, 10);
1255 case OPT_SSH_KEEPALIVE
:
1256 ssh_keepalive
= strtol(optarg
, &p
, 10);
1263 case OPT_CLIENTCERT
:
1264 clientcert
= optarg
;
1280 keyfile
= pwmd_strdup(optarg
);
1282 case OPT_NEW_KEYFILE
:
1283 new_keyfile
= pwmd_strdup(optarg
);
1288 case OPT_LOCK_TIMEOUT
:
1289 lock_timeout
= strtol(optarg
, &p
, 10);
1297 case OPT_FORCE_SAVE
:
1298 save
= force_save
= 1;
1301 lcctype
= pwmd_strdup(optarg
);
1303 case OPT_LC_MESSAGES
:
1304 lcmessages
= pwmd_strdup(optarg
);
1307 timeout
= strtol(optarg
, &p
, 10);
1310 tries
= strtol(optarg
, &p
, 10);
1315 case OPT_INQUIRE_FD
:
1316 inquirefd
= strtol(optarg
, &p
, 10);
1318 inquirefp
= fdopen(inquirefd
, "r");
1320 pwmd_free(password
);
1321 err(EXIT_FAILURE
, "%i", inquirefd
);
1325 case OPT_INQUIRE_FILE
:
1326 inquirefd
= open(optarg
, O_RDONLY
);
1327 if (!inquirefd
== -1) {
1328 pwmd_free(password
);
1329 err(EXIT_FAILURE
, "%s", optarg
);
1331 inquirefp
= fdopen(inquirefd
, "r");
1334 outfd
= strtol(optarg
, &p
, 10);
1336 outfp
= fdopen(outfd
, "w");
1338 pwmd_free(password
);
1339 err(EXIT_FAILURE
, "%i", outfd
);
1347 pwmd_free(password
);
1348 printf("%s (pwmc)\n\n"
1349 "Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012\n"
1351 "Released under the terms of the GPL v2. Use at your own risk.\n\n"
1352 "Compile-time features:\n"
1363 #ifdef WITH_PINENTRY
1378 #ifdef HAVE_LIBREADLINE
1384 , PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1387 pinentry_path
= optarg
;
1390 usage(argv
[0], EXIT_SUCCESS
);
1394 case OPT_SIGN_KEYGRIP
:
1395 sign_keygrip
= optarg
;
1398 s2k_count
= strtoul(optarg
, &p
, 10);
1400 case OPT_ITERATIONS
:
1401 iterations
= strtoull(optarg
, &p
, 10);
1404 case OPT_NOPASSPHRASE
:
1410 case OPT_NO_PINENTRY
:
1413 #ifdef HAVE_LIBREADLINE
1414 case OPT_INTERACTIVE
:
1419 usage(argv
[0], EXIT_FAILURE
);
1423 fprintf(stderr
, N_("%s: invalid argument for option '--%s'\n"),
1424 argv
[0], long_opts
[opt_index
].name
);
1425 usage(argv
[0], EXIT_FAILURE
);
1434 knownhosts
= optarg
;
1438 inquire_line
= optarg
;
1453 clientname
= optarg
;
1456 pwmd_free(password
);
1457 usage(argv
[0], EXIT_FAILURE
);
1461 #ifdef HAVE_LIBREADLINE
1462 if (interactive
&& !isatty(STDIN_FILENO
)) {
1463 pwmd_free(password
);
1464 usage(argv
[0], EXIT_FAILURE
);
1466 else if (isatty(STDIN_FILENO
) && !inquire
&& !inquire_line
)
1470 filename
= argv
[optind
];
1472 gnutls_global_set_mem_functions(pwmd_malloc
, pwmd_malloc
, NULL
,
1473 pwmd_realloc
, pwmd_free
);
1476 rc
= pwmd_new(clientname
, &pwm
);
1480 pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TRIES
, tries
);
1481 fprintf(stderr
, N_("Connecting ...\n"));
1483 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1484 socktype
= is_remote_url(url
);
1485 if (socktype
!= PWMD_SOCKET_LOCAL
) {
1487 if (socktype
== PWMD_SOCKET_SSH
) {
1489 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_CB
, knownhost_cb
);
1493 rc
= pwmd_setopt(pwm
, PWMD_OPTION_KNOWNHOST_DATA
, clientname
);
1497 if (!getenv("SSH_AUTH_SOCK") || identity
)
1500 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_AGENT
, use_ssh_agent
);
1504 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_TIMEOUT
, ssh_timeout
);
1508 rc
= pwmd_setopt(pwm
, PWMD_OPTION_SSH_KEEPALIVE
, ssh_keepalive
);
1512 rc
= pwmd_connect(pwm
, url
, identity
, knownhosts
);
1517 rc
= pwmd_setopt(pwm
, PWMD_OPTION_TLS_VERIFY
, tls_verify
);
1521 rc
= pwmd_connect(pwm
, url
, clientcert
, clientkey
, cacert
, prio
);
1526 rc
= pwmd_connect(pwm
, url
);
1528 rc
= pwmd_connect(pwm
, url
);
1533 fprintf(stderr
, N_("Connected.\n"));
1536 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
,
1537 "OPTION LOCK-TIMEOUT=%li", lock_timeout
);
1543 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCK_ON_OPEN
, 1);
1548 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DESC
, NULL
);
1553 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TIMEOUT
, timeout
);
1558 rc
= pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, no_pinentry
);
1562 rc
= pwmd_setopt(pwm
, PWMD_OPTION_LOCAL_PINENTRY
,
1563 (local_pin
|| keyfile
|| new_keyfile
));
1567 if (pinentry_path
) {
1568 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_PATH
, pinentry_path
);
1574 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_DISPLAY
, display
);
1580 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TTY
, tty
);
1586 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_TERM
, ttytype
);
1592 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_CTYPE
, lcctype
);
1598 rc
= pwmd_setopt(pwm
, PWMD_OPTION_PINENTRY_LC_MESSAGES
,
1605 rc
= pwmd_setopt(pwm
, PWMD_OPTION_STATUS_CB
, status_msg_cb
);
1611 rc
= open_command(filename
);
1616 #ifdef HAVE_LIBREADLINE
1621 rc
= do_interactive();
1627 struct inquire_s
*inq
= NULL
;
1629 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1631 rc
= pwmd_command(pwm
, &result
, &len
, inquire_cb
, inq
, inquire
);
1637 fcntl(STDIN_FILENO
, F_SETFL
, O_NONBLOCK
);
1646 n
= read(STDIN_FILENO
, command
, sizeof(command
));
1648 if (errno
== EAGAIN
) {
1653 rc
= gpg_error_from_errno(errno
);
1664 if (!p
|| !*p
|| !strcasecmp(p
, "BYE"))
1668 struct inquire_s
*inq
= NULL
;
1669 rc
= set_inquire(inquirefd
, inquire_line
, &inq
);
1671 rc
= parse_dotcommand(command
, &result
, &len
, inq
);
1680 fwrite(result
, 1, result
[len
-1] == 0 ? len
-1 : len
, outfp
);
1685 pwmd_free(password
);
1690 #ifdef HAVE_LIBREADLINE
1693 memset(command
, 0, sizeof(command
));
1696 pwmd_free(new_keyfile
);
1703 fprintf(stderr
, N_("Connection closed.\n"));
1705 exit(rc
? EXIT_FAILURE
: EXIT_SUCCESS
);