From a8e19d5d8057e82cbda2705d755f3d4e1d3da20a Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Sun, 1 May 2016 05:25:14 -0400 Subject: remove references to files outside of this repo (commit the files into this repo) --- src/samizdat-pinentry.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 src/samizdat-pinentry.c (limited to 'src/samizdat-pinentry.c') diff --git a/src/samizdat-pinentry.c b/src/samizdat-pinentry.c new file mode 100644 index 0000000..e1f7fee --- /dev/null +++ b/src/samizdat-pinentry.c @@ -0,0 +1,455 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// TODO: implement multiple ttys +// TODO: inotify on a password file (kills pinentry) +// TODO: secure memory + + +/* Buffers: + * + * assuan info page "The implementation is line based with a maximum line size + * of 1000 octets. The default IPC mechanism are Unix Domain Sockets. + * + * pinentry info page "Although it is called a PIN-Entry, it does allow to + * enter reasonably long strings (at least 2048 characters are supported by + * every pinentry). The client using the PIN-Entry has to check for + * correctness." + */ +#define MAXLINE 1000 // It's assumed this is > 2. + + +static int +open2(const char *argv[], int *result_in, int *result_out, pid_t *result_child) +{ + int pipe_out[2]; /* out of parent */ + int pipe_in[2]; /* into parent */ + pid_t cpid; + + if (pipe(pipe_out) < 0) + return -1; + if (pipe(pipe_in) < 0) + return -1; + + cpid = fork(); + if (cpid < 0) + return -1; + + if (cpid == 0) { /* child */ + int *input = pipe_out; /* for sanity */ + int *output = pipe_in; + close(output[0]); + close(input[1]); + + /* see http://unixwiz.net/techtips/remap-pipe-fds.c.txt */ + if (output[1] == 0) + if ((output[1] = dup(output[1]) < 0)) + error(1, errno, "in child process: dup"); + if (output[0] == 1) + if ((output[0] = dup(output[0]) < 0)) + error(1, errno, "in child process: dup"); + + if (dup2(output[1], 1) < 0) + error(1, errno, "in child process: dup2"); + if (dup2(input[0], 0) < 0) + error(1, errno, "in child process: dup2"); + + if (output[1] != 1) + close(output[1]); + if (input[0] != 0) + close(input[0]); + + execv(argv[0],(char *const*) &argv[1]); + error(1, errno, "in child process: exec"); + return -1; /* fucking warning */ + + } else { /* parent */ + close(pipe_out[0]); + close(pipe_in[1]); + + *result_in = pipe_in[0]; + *result_out = pipe_out[1]; + if (*result_child) + *result_child = cpid; + return 0; + } +} + +typedef struct { + FILE *in, *out; + pid_t pid; +} pipe2_t; + + +pipe2_t fopen2(const char *argv[]) +{ + pipe2_t res = { 0, 0, 0 }; + int in = 0; + int out = 0; + if( open2(argv, &in, &out, &res.pid) < 0 ) + error(1,0, "open2"); + res.out = fdopen(out, "w"); + res.in = fdopen(in, "r"); + return res; +} + +void pinentry_send(pipe2_t pinentry, const char *msg) +{ + fprintf(pinentry.out, "%s\n", msg); + fflush(pinentry.out); + // printf("C: %s\n", msg); +} +int pinentry_get(pipe2_t pinentry, char *data, size_t len) +{ + char *ret=fgets(data,len,pinentry.in); + char *p=ret; + int gotnewline=0; + for(;*p;p++); + if(p!=ret && p[-1]=='\n') { + p[-1]='\0'; + gotnewline=1; + } + if( !gotnewline ) { + int c = ' '; + do { + c=fgetc(pinentry.in); + } while( c!=-1 && c!='\n' ); + } + // printf("S: %s%s", ret, gotnewline?"\n":" \n"); + return !gotnewline; +} + +void pinentry_seterror(pipe2_t pinentry, const char *s) +{ + fprintf(pinentry.out,"SETERROR "); + pinentry_send( pinentry, s ); + char data[4] = { 0 }; + pinentry_get(pinentry,data,4); // OK +} + +void pinentry_set(pipe2_t pinentry, const char *k, const char *v) +{ + fprintf(pinentry.out,"SET%s %s\n", k, v); + fflush(pinentry.out); + char data[4] = { 0 }; + pinentry_get(pinentry,data,4); // OK +} + +static const char *arg_prompt = "Passphrase:"; +static const char *arg_desc = "Enter your passphrase to unlock the secret key. "; + +int pinentry_getpin(pipe2_t pinentry, char *secret, size_t maxline) +{ + char data[MAXLINE] = { 0 }; + + // pinentry_send( pinentry, "OPTION lc-ctype=en_US.UTF-8"); + // pinentry_get( pinentry, data, MAXLINE); // OK + + if (strlen(arg_prompt)) + pinentry_set( pinentry, "PROMPT", arg_prompt ); + if (strlen(arg_desc)) + pinentry_set( pinentry, "DESC", arg_desc ); + + pinentry_send( pinentry, "GETPIN"); + int e = pinentry_get(pinentry, data, MAXLINE); + if( strncmp(data,"D ",2)==0 ) { + if(!e && strlen(data+2)<=maxline-1 ) { + // D + snprintf(secret,maxline,"%s", data+2); + //printf("secret = %s\n", secret); + pinentry_get(pinentry,data,MAXLINE); // OK + return 0; + } + else { + // SECRET TOO BIG, discarding + pinentry_seterror(pinentry, "Too big."); + return 4; + } + } + else if( strncmp(data,"OK",2)==0 ) { + // EMPTY (not an error) + secret[0]='\0'; + //printf("empty secret\n"); + return 0; + } + else if( strncmp(data,"ERR ",4)==0 ) { + // CANCELED (ERR 83886179 canceled) + // ERR 83886246 locale problem + // xxx printf("canceled.\n"); + return 2; + } + else { + // WTF? + // xxx printf("Unexpected: %s\n", data); + char buf[1024]; + snprintf(buf,1024,"Unexpected: %s", data ); + pinentry_seterror(pinentry, buf); + return 3; + } +} + +pipe2_t do_shell(const char *shell_code) { + pipe2_t ret; + const char *argv[] = { "/bin/sh", "/bin/sh","-c",NULL,NULL }; + argv[3]=shell_code; + return fopen2(argv); +} + +int validate(pipe2_t pinentry, const char *secret, const char *arg_validate) +{ + int retv = 0; + if( !arg_validate ) return 1; + pipe2_t validate_script = do_shell( arg_validate ); + + fprintf( validate_script.out, "%s\n", secret ); + fclose( validate_script.out ); + char error_string[1024] = "Error%0Aunspecified error"; + + int c; + char *p = error_string; + while( p!=&error_string[1024-4] && -1!=(c=getc( validate_script.in )) ) + switch(c){ + case '%': + case '\n': + snprintf(p,4, "%%%0.02X", c); + p+=3; + break; + default: + *p++ = c; + } + *p='\0'; + + // pinentry ruins its display if this message ends with a newline + while(p-3>error_string && memcmp(&p[-3],"%0A",3)==0 ) p-=3; + *p='\0'; + + fclose( validate_script.in ); + waitpid(validate_script.pid, &retv, 0 ); + if( retv ) { + fprintf(stderr, "error_string: %s\n", error_string); + pinentry_seterror(pinentry, error_string ); + } + return !retv; +} + +FILE *open_socket(const char *socket_path) +{ + int sok = socket(PF_LOCAL, SOCK_STREAM, 0); + if( sok<0 ) { + return NULL; + } + struct sockaddr_un address; + memset(&address, 0, sizeof(struct sockaddr_un)); + address.sun_family = AF_UNIX; + snprintf(address.sun_path, 108, socket_path); + if(connect(sok, + (struct sockaddr *) &address, + sizeof(struct sockaddr_un)) != 0) + { + return NULL; + } + return fdopen(sok,"r+"); +} + +const char *arg_socket = NULL; +const char *arg_resocket = NULL; +const char *arg_tellsecret = NULL; +char secret[MAXLINE]; // TODO: secure memory + +void tell_encoded_secret(FILE *out) +{ + char *encoded_secret = malloc(strlen(secret)*2 + 1); + for (char *p = secret; *p; ++p) + snprintf(encoded_secret + 2*(p - secret), 3, "%0.02X", *p); + + fprintf(out, "%s\n", encoded_secret); + fflush(out); + free(encoded_secret); +} + +void signal_usr1(int val) +{ + if( !arg_tellsecret ) { + fprintf(stderr, "USR1, no action\n"); + return; + } else + fprintf(stderr, "Received SIGUSR1; will tell secret.\n"); + + pipe2_t tellsecret = do_shell( arg_tellsecret ); + tell_encoded_secret(tellsecret.out); + + FILE *sock = NULL; + if (arg_socket) sock=open_socket(arg_socket); + + for (;;) { + char *response = 0; + size_t len; + if (sock && (getline(&response, &len, sock) > 0)) { + fprintf(tellsecret.out, "%s", response); + fflush(tellsecret.out); + } + + if (getline(&response, &len, tellsecret.in) > 0) { + if (sock) { + fprintf(sock, "%s", response); + fflush(sock); + } else { + fprintf(stderr, "?> %s", response); + } + } else { + break; + } + } + + if (sock) fclose(sock); + fclose(tellsecret.out); + fclose(tellsecret.in); + int status = 0; + waitpid(tellsecret.pid, &status, 0 ); +} + +void signal_usr2(int wat) +{ + fprintf(stderr, "Received SIGUSR2; will change socket and emulate SIGUSR1.\n"); + if (arg_resocket) + arg_socket = arg_resocket; + signal_usr1(wat); +} + +void signal_term(int val) +{ + // TODO: Wipe secret + exit(0); +} + +int main(int argc, char **argv) +{ + pipe2_t pinentry = { 0 }; + + char tty_name_buf[256]; + if( ttyname_r(1, tty_name_buf, sizeof(tty_name_buf)) ) { + tty_name_buf[0]='\0'; + //error(2,0,"ttyname_r"); + } + const char *arg_ttyname = tty_name_buf; + const char *arg_pinentry = "/usr/bin/pinentry-curses"; + const char *arg_validate = NULL; + const char *arg_pidfile = NULL; //same as "/dev/stdout"; + int tell_at_start=0; + + const char **expect = NULL; + int i=0; + for(i=1;i