From 3f04c3eff5ea349d342e17e882cd2d6f44d08ac7 Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Tue, 9 Nov 2010 21:43:18 -0500 Subject: [PATCH] pwmc: added --interactive. --- configure.ac | 2 + doc/pwmc.1.in | 67 ++++++++++++++--- src/pwmc.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 241 insertions(+), 57 deletions(-) diff --git a/configure.ac b/configure.ac index e42922e3..cd3d2205 100644 --- a/configure.ac +++ b/configure.ac @@ -125,6 +125,8 @@ AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) +VL_LIB_READLINE() + dnl Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC diff --git a/doc/pwmc.1.in b/doc/pwmc.1.in index 3825d766..758b5606 100644 --- a/doc/pwmc.1.in +++ b/doc/pwmc.1.in @@ -15,13 +15,16 @@ \\$2 \(laURL: \\$1 \(ra\\$3 .. .if \n[.g] .mso www.tmac -.TH PWMD 1 "6 Nov 2010" "Password Manager Client" "Password Manager Client" +.TH PWMD 1 "9 Nov 2010" "Password Manager Client" "Password Manager Client" .SH NAME pwmc \- send a command to a pwmd server .SH SYNOPSIS .B pwmc [options] [file] +.P +.B pwmsh +[options] [file] .SH DESCRIPTION .B pwmc @@ -112,6 +115,13 @@ Don't show server status messages. By default, status messages are written to stderr. .TP +.I "\--interactive" +Use an interactive pwmc shell. This will let you send more than one command +per connection. See +.B INTERACTIVE +below for commands. + +.TP .I "\--inquire " Use this option to send commands that use a server inquire to retrieve data. Only the command name and any command options should be specified. The @@ -289,6 +299,45 @@ The terminal type of the tty (i.e., vt100) which is required if DISPLAY is not set. +.SH INTERACTIVE +When passed the +.B --interactive +option or started as +.B pwmsh +a shell like interface can be used. This allows sending more than one command +during a connection. It's a little tricky to get server inquires to work right +so two special commands were added to the shell: +.P +.B INQUIRE +.I +.P +.B INQUIRE_FILE +.I +.I +.P +The +.I COMMAND +is the inquire command to send with any needed command options. The data is +read from stdin or from the specified +.I FILENAME. +All other commands are sent directly to +.B pwmd(1) +via +.B libpwmd(3) +.I pwmd_command() +without modification. +.P +Since interactive mode uses the +.B readline(3) +library, the TAB character is normally interpreted as the line completion +character. This conflicts with the +.B pwmd(1) +element separation character. In order to insert a TAB you'd first need to +press CTRL-V then press TAB. See +.B readline(3) +for more information about line history and completion. + + .SH EXAMPLES To list the available accounts and use .BR pinentry (1) @@ -299,16 +348,16 @@ echo list | pwmc filename .P To store an element path and save the file afterwards: .RS -echo -ne 'store isp\\tsmtp\\thostname\\tsomehost.com' | pwmc -S filename -.RE -.P -Store a large file: -.RS -echo -en 'store blah\\tstuff\\t' | pwmc -S -I 3 filename 3 +#endif + #include #include #include @@ -31,10 +35,6 @@ #include #include -#ifdef HAVE_CONFIG_H -#include -#endif - #ifdef HAVE_LOCALE_H #include #endif @@ -47,6 +47,22 @@ #include "getopt_long.h" #endif +#ifdef HAVE_LIBREADLINE +# if defined(HAVE_READLINE_READLINE_H) +# include +# elif defined(HAVE_READLINE_H) +# include +# endif /* !defined(HAVE_READLINE_H) */ +#endif /* HAVE_LIBREADLINE */ + +#ifdef HAVE_READLINE_HISTORY +# if defined(HAVE_READLINE_HISTORY_H) +# include +# elif defined(HAVE_HISTORY_H) +# include +# endif +#endif /* HAVE_READLINE_HISTORY */ + #include "gettext.h" #define N_(msgid) gettext(msgid) @@ -54,7 +70,13 @@ #define DEFAULT_PORT 22 -pwm_t *pwm; +static pwm_t *pwm; + +struct inquire_s { + int fd; + char *line; + size_t len; +}; static void show_error(gpg_error_t error) { @@ -147,6 +169,9 @@ static void usage(const char *pn, int status) " --local-pinentry\n" " force using a local pinentry\n" "\n" + " --interactive\n" + " use a shell like interface to pwmd (allows more than one command)\n" + "\n" " --output-fd \n" " redirect command output to the specified file descriptor\n" "\n" @@ -182,12 +207,6 @@ static void usage(const char *pn, int status) exit(status); } -struct inquire_s { - int fd; - char *line; - size_t len; -}; - static gpg_error_t inquire_cb(void *user, const char *cmd, gpg_error_t rc, char **data, size_t *len) { @@ -224,7 +243,7 @@ static int status_msg_cb(void *data, const char *line) return 0; } -static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input) +static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input, int once) { gpg_error_t rc; pwmd_async_t s; @@ -234,6 +253,7 @@ static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input) fd_set rfds; int nfds = 5; pwmd_fd_t pfds[nfds]; + struct timeval tv = {0, 0}; FD_ZERO(&rfds); rc = pwmd_get_fds(pwm, pfds, &nfds); @@ -254,7 +274,7 @@ static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input) if (input) FD_SET(STDIN_FILENO, &rfds); - nfds = select(n+1, &rfds, NULL, NULL, NULL); + nfds = select(n+1, &rfds, NULL, NULL, once ? &tv : NULL); if (nfds == -1) { rc = gpg_error_from_errno(errno); @@ -265,6 +285,9 @@ static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input) return 0; s = pwmd_process(pwm, &rc, result); + + if (once) + break; } while (s == ASYNC_PROCESS); return rc; @@ -295,9 +318,119 @@ static int is_remote_url(const char *str) } #endif +static gpg_error_t send_inquire(int fd, const char *command) +{ + struct inquire_s inq; + struct stat st; + gpg_error_t rc; + + if (fstat(fd, &st) == -1) + return gpg_error_from_syserror(); + + rc = pwmd_setopt(pwm, PWMD_OPTION_INQUIRE_TOTAL, st.st_size); + + if (rc) + return rc; + + memset(&inq, 0, sizeof(inq)); + inq.fd = fd; + inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH); + + if (!inq.line) + return GPG_ERR_ENOMEM; + + inq.len = 0; + rc = pwmd_inquire(pwm, command, inquire_cb, &inq); + pwmd_free(inq.line); + return rc; +} + +#ifdef HAVE_LIBREADLINE +static int interactive_hook(void) +{ + process_cmd(pwm, NULL, 0, 1); + return 0; +} + +static gpg_error_t do_interactive() +{ + gpg_error_t rc = 0; + + fprintf(stderr, "WARNING: interactive mode doesn't use secure memory!\n"); + rl_initialize(); + rl_event_hook = &interactive_hook; + + for (;;) { + char *line; + char *result = NULL; + + line = readline("pwm> "); + + if (!line) + break; + else if (!*line) { + free(line); + continue; + } + +#ifdef HAVE_READLINE_HISTORY + add_history(line); +#endif + + /* INQUIRE */ + if (!strncasecmp(line, "inquire ", 8)) { + fprintf(stderr, "Press CTRL-D to end.\n"); + rc = send_inquire(STDIN_FILENO, line+8); + } + /* INQUIRE_FILE */ + else if (!strncasecmp(line, "inquire_file ", 13)) { + char *p = strchr(line+13, ' '); + char *f = p ? line+13 : NULL; + int fd; + + if (!f || !*f || !*p) { + fprintf(stderr, "Invalid number of command arguments.\n"); + free(line); + continue; + } + + f[strlen(f) - strlen(p++)] = 0; + fd = open(f, O_RDONLY); + + if (fd == -1) { + fprintf(stderr, "%s\n", strerror(errno)); + free(line); + continue; + } + else { + rc = send_inquire(fd, p); + close(fd); + } + } + else + rc = pwmd_command(pwm, &result, line); + + free(line); + + if (rc) + fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc)); + else if (result && *result) + printf("%s%s", result, result[strlen(result)-1] != '\n' ? "\n" : ""); + + if (result) + pwmd_free(result); + } + + return 0; +} +#endif + int main(int argc, char *argv[]) { int opt; +#ifdef HAVE_LIBREADLINE + int interactive = 0; +#endif char *password = NULL; char *keyfile = NULL; int base64 = 0; @@ -358,6 +491,9 @@ int main(int argc, char *argv[]) OPT_NOLOCK, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD, OPT_INQUIRE, OPT_INQUIRE_FD, OPT_NO_STATUS, OPT_NAME, OPT_VERSION, OPT_HELP, OPT_CIPHER, +#ifdef HAVE_LIBREADLINE + OPT_INTERACTIVE, +#endif }; const struct option long_opts[] = { #ifdef DEBUG @@ -399,6 +535,9 @@ int main(int argc, char *argv[]) { "version", 0, 0, 0 }, { "help", 0, 0, 0 }, { "cipher", 1, 0, 0 }, +#ifdef HAVE_LIBREADLINE + { "interactive", 0, 0 }, +#endif { 0, 0, 0, 0} }; #ifdef WITH_TCP @@ -413,6 +552,9 @@ int main(int argc, char *argv[]) bindtextdomain("libpwmd", LOCALEDIR); #endif + if (!strcmp(basename(argv[0]), "pwmsh")) + interactive = 1; + while ((opt = getopt_long(argc, argv, optstring, long_opts, &opt_index)) != -1) { switch (opt) { /* Handle long options without a short option part. */ @@ -519,6 +661,11 @@ int main(int argc, char *argv[]) case OPT_CIPHER: cipher = optarg; break; +#ifdef HAVE_LIBREADLINE + case OPT_INTERACTIVE: + interactive = 1; + break; +#endif default: usage(argv[0], EXIT_FAILURE); } @@ -635,7 +782,7 @@ int main(int argc, char *argv[]) if (error) errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error)); - error = process_cmd(pwm, &hostkey, 0); + error = process_cmd(pwm, &hostkey, 0, 0); if (error) goto done; @@ -656,7 +803,7 @@ int main(int argc, char *argv[]) if (error) goto done; - error = process_cmd(pwm, NULL, 0); + error = process_cmd(pwm, NULL, 0, 0); if (error) goto done; @@ -903,7 +1050,7 @@ do_open: goto done; if (method >= 2) - error = process_cmd(pwm, &result, 0); + error = process_cmd(pwm, &result, 0, 0); #else #ifdef WITH_TCP if (host) @@ -923,40 +1070,27 @@ do_open: #ifdef WITH_TCP read_command: #endif - if (inquire) { - struct inquire_s inq; - struct stat st; - - memset(&inq, 0, sizeof(inq)); - inq.fd = fileno(inquirefp); - - if (inq.fd == -1) { - error = gpg_error_from_syserror(); - goto done; - } - - if (fstat(inq.fd, &st) == -1) { - error = gpg_error_from_syserror(); - goto done; - } - - error = pwmd_setopt(pwm, PWMD_OPTION_INQUIRE_TOTAL, - st.st_size ? (size_t)st.st_size+strlen(p) : 0); +#ifdef HAVE_LIBREADLINE + if (interactive) { + error = do_interactive(); if (error) - goto done; + goto do_exit; + + goto done; + } +#endif - inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH); + if (inquire) { + int fd = fileno(inquirefp); - if (!inq.line) { - error = GPG_ERR_ENOMEM; + if (fd == -1) { + error = gpg_error_from_syserror(); goto done; } - inq.len = 0; - error = pwmd_inquire(pwm, inquire, inquire_cb, &inq); - pwmd_free(inq.line); - close(inq.fd); + error = send_inquire(fd, inquire); + close(fd); goto done; } @@ -964,7 +1098,7 @@ read_command: ssize_t n; for (;;) { - error = process_cmd(pwm, NULL, 1); + error = process_cmd(pwm, NULL, 1, 0); if (error) goto done; @@ -993,7 +1127,6 @@ read_command: goto done; error = pwmd_command(pwm, &result, command); - memset(command, 0, sizeof(command)); if (error) goto done; @@ -1004,8 +1137,6 @@ read_command: } done: - memset(command, 0, sizeof(command)); - if (password) pwmd_free(password); @@ -1127,7 +1258,7 @@ do_save: } if (!error && method >= 2) - error = process_cmd(pwm, NULL, 0); + error = process_cmd(pwm, NULL, 0, 0); #else #ifdef WITH_TCP @@ -1141,6 +1272,8 @@ do_save: } do_exit: + memset(command, 0, sizeof(command)); + if (error) { show_error(error); ret = EXIT_FAILURE; -- 2.11.4.GIT