diff options
author | David Shaw <dshaw@jabberwocky.com> | 2002-06-29 15:46:34 +0200 |
---|---|---|
committer | David Shaw <dshaw@jabberwocky.com> | 2002-06-29 15:46:34 +0200 |
commit | 3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37 (patch) | |
tree | dac7a3780fb4edf9ca89c93800230e88ab255434 /g10/passphrase.c | |
parent | Update head to match stable 1.0 (diff) | |
download | gnupg2-3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37.tar.xz gnupg2-3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37.zip |
Update head to match stable 1.0
Diffstat (limited to 'g10/passphrase.c')
-rw-r--r-- | g10/passphrase.c | 1002 |
1 files changed, 757 insertions, 245 deletions
diff --git a/g10/passphrase.c b/g10/passphrase.c index 6b06df72e..c8ebad620 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,5 +1,5 @@ /* passphrase.c - Get a passphrase - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -19,26 +19,60 @@ */ #include <config.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> -#include <stddef.h> #include <string.h> #include <unistd.h> #include <assert.h> +#if !defined(HAVE_DOSISH_SYSTEM) && !defined(__riscos__) #include <sys/socket.h> #include <sys/un.h> -#include <unistd.h> +#endif +#if defined (__MINGW32__) || defined (__CYGWIN32__) +# include <windows.h> +#endif #include <errno.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif -#include <gcrypt.h> #include "util.h" +#include "memory.h" #include "options.h" #include "ttyio.h" +#include "cipher.h" #include "keydb.h" #include "main.h" #include "i18n.h" #include "status.h" -#include "gpga-prot.h" + + +enum gpga_protocol_codes { + /* Request codes */ + GPGA_PROT_GET_VERSION = 1, + GPGA_PROT_GET_PASSPHRASE = 2, + GPGA_PROT_CLEAR_PASSPHRASE= 3, + GPGA_PROT_SHUTDOWN = 4, + GPGA_PROT_FLUSH = 5, + + /* Reply codes */ + GPGA_PROT_REPLY_BASE = 0x10000, + GPGA_PROT_OKAY = 0x10001, + GPGA_PROT_GOT_PASSPHRASE = 0x10002, + + /* Error codes */ + GPGA_PROT_ERROR_BASE = 0x20000, + GPGA_PROT_PROTOCOL_ERROR = 0x20001, + GPGA_PROT_INVALID_REQUEST= 0x20002, + GPGA_PROT_CANCELED = 0x20003, + GPGA_PROT_NO_PASSPHRASE = 0x20004, + GPGA_PROT_BAD_PASSPHRASE = 0x20005, + GPGA_PROT_INVALID_DATA = 0x20006, + GPGA_PROT_NOT_IMPLEMENTED= 0x20007, + GPGA_PROT_UI_PROBLEM = 0x20008 +}; + #define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) @@ -49,11 +83,25 @@ ((byte*)p)[3] = (byte)((a) ); \ } while(0) +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + + static char *fd_passwd = NULL; static char *next_pw = NULL; static char *last_pw = NULL; +#if defined (__MINGW32__) || defined (__CYGWIN32__) +static int read_fd = 0; +static int write_fd = 0; +#endif + static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ); int @@ -71,10 +119,10 @@ have_static_passphrase() void set_next_passphrase( const char *s ) { - gcry_free(next_pw); + m_free(next_pw); next_pw = NULL; if( s ) { - next_pw = gcry_xmalloc_secure( strlen(s)+1 ); + next_pw = m_alloc_secure( strlen(s)+1 ); strcpy(next_pw, s ); } } @@ -101,7 +149,6 @@ read_passphrase_from_fd( int fd ) if ( opt.use_agent ) return; /* not used here */ - if( !opt.batch ) tty_printf("Reading passphrase from file descriptor %d ...", fd ); @@ -109,7 +156,7 @@ read_passphrase_from_fd( int fd ) if( i >= len-1 ) { char *pw2 = pw; len += 100; - pw = gcry_xmalloc_secure( len ); + pw = m_alloc_secure( len ); if( pw2 ) memcpy(pw, pw2, i ); else @@ -122,16 +169,33 @@ read_passphrase_from_fd( int fd ) if( !opt.batch ) tty_printf("\b\b\b \n" ); - gcry_free( fd_passwd ); + m_free( fd_passwd ); fd_passwd = pw; } - static int writen ( int fd, const void *buf, size_t nbytes ) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + DWORD nwritten, nleft = nbytes; + + while (nleft > 0) { + if ( !WriteFile( (HANDLE)write_fd, buf, nleft, &nwritten, NULL) ) { + log_error("write failed: ec=%d\n", (int)GetLastError()); + return -1; + } + /*log_info("** WriteFile fd=%d nytes=%d nwritten=%d\n", + write_fd, nbytes, (int)nwritten);*/ + Sleep(100); + + nleft -= nwritten; + buf = (const BYTE *)buf + nwritten; + } +#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + /* not implemented */ +#else size_t nleft = nbytes; - ssize_t nwritten; + int nwritten; while( nleft > 0 ) { nwritten = write( fd, buf, nleft ); @@ -139,13 +203,15 @@ writen ( int fd, const void *buf, size_t nbytes ) if ( errno == EINTR ) nwritten = 0; else { - log_error ( "writen() failed: %s\n", strerror (errno) ); + log_error ( "write() failed: %s\n", strerror (errno) ); return -1; } } nleft -= nwritten; buf = (const char*)buf + nwritten; } +#endif + return 0; } @@ -153,6 +219,29 @@ writen ( int fd, const void *buf, size_t nbytes ) static int readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + DWORD nread, nleft = buflen; + + while (nleft > 0) { + if ( !ReadFile( (HANDLE)read_fd, buf, nleft, &nread, NULL) ) { + log_error("read() error: ec=%d\n", (int)GetLastError()); + return -1; + } + if (!nread || GetLastError() == ERROR_BROKEN_PIPE) + break; + /*log_info("** ReadFile fd=%d buflen=%d nread=%d\n", + read_fd, buflen, (int)nread);*/ + Sleep(100); + + nleft -= nread; + buf = (BYTE *)buf + nread; + } + if (ret_nread) + *ret_nread = buflen - nleft; + +#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + /* not implemented */ +#else size_t nleft = buflen; int nread; char *p; @@ -175,9 +264,161 @@ readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) } if( ret_nread ) *ret_nread = buflen - nleft; +#endif + return 0; } +/* read an entire line */ +static int +readline (int fd, char *buf, size_t buflen) +{ + size_t nleft = buflen; + char *p; + int nread = 0; + + while (nleft > 0) + { + int n = read (fd, buf, nleft); + if (n < 0) + { + if (errno == EINTR) + continue; + return -1; /* read error */ + } + else if (!n) + { + return -1; /* incomplete line */ + } + p = buf; + nleft -= n; + buf += n; + nread += n; + + for (; n && *p != '\n'; n--, p++) + ; + if (n) + { + break; /* at least one full line available - that's enough. + This function is just a temporary hack until we use + the assuna lib in gpg. So it is okay to forget + about pending bytes */ + } + } + + return nread; +} + + + +#if !defined (__riscos__) + +#if !defined (__MINGW32__) && !defined (__CYGWIN32__) +/* For the new Assuan protocol we may have to send options */ +static int +agent_send_option (int fd, const char *name, const char *value) +{ + char buf[200]; + int nread; + char *line; + int i; + + line = m_alloc (7 + strlen (name) + 1 + strlen (value) + 2); + strcpy (stpcpy (stpcpy (stpcpy ( + stpcpy (line, "OPTION "), name), "="), value), "\n"); + i = writen (fd, line, strlen (line)); + m_free (line); + if (i) + return -1; + + /* get response */ + nread = readline (fd, buf, DIM(buf)-1); + if (nread < 3) + return -1; + + if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) + return 0; /* okay */ + + return -1; +} + +static int +agent_send_all_options (int fd) +{ + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + char *old_lc = NULL; + char *dft_lc = NULL; + int rc = 0; + + dft_display = getenv ("DISPLAY"); + if (opt.display || dft_display) + { + if (agent_send_option (fd, "display", + opt.display ? opt.display : dft_display)) + return -1; + } + + if (!opt.ttyname && ttyname (1)) + dft_ttyname = ttyname (1); + if (opt.ttyname || dft_ttyname) + { + if (agent_send_option (fd, "ttyname", + opt.ttyname ? opt.ttyname : dft_ttyname)) + return -1; + } + + dft_ttytype = getenv ("TERM"); + if (opt.ttytype || (dft_ttyname && dft_ttytype)) + { + if (agent_send_option (fd, "ttytype", + opt.ttyname ? opt.ttytype : dft_ttytype)) + return -1; + } + +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + old_lc = m_strdup (old_lc); + dft_lc = setlocale (LC_CTYPE, ""); +#endif + if (opt.lc_ctype || (dft_ttyname && dft_lc)) + { + rc = agent_send_option (fd, "lc-ctype", + opt.lc_ctype ? opt.lc_ctype : dft_lc); + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + m_free (old_lc); + } +#endif + if (rc) + return rc; + +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + old_lc = m_strdup (old_lc); + dft_lc = setlocale (LC_MESSAGES, ""); +#endif + if (opt.lc_messages || (dft_ttyname && dft_lc)) + { + rc = agent_send_option (fd, "lc-messages", + opt.lc_messages ? opt.lc_messages : dft_lc); + } +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + m_free (old_lc); + } +#endif + return rc; +} +#endif /*!__MINGW32__ && !__CYGWIN32__*/ /* @@ -186,33 +427,101 @@ readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) */ static int -agent_open () +agent_open (int *ret_prot) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + int fd; + char *infostr, *p; + HANDLE h; + char pidstr[128]; + + *ret_prot = 0; + if ( !(infostr = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentPID")) + || *infostr == '0') { + log_error( _("gpg-agent is not available in this session\n")); + return -1; + } + free(infostr); + + sprintf(pidstr, "%u", (unsigned int)GetCurrentProcessId()); + if (write_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentCID", pidstr)) { + log_error( _("can't set client pid for the agent\n") ); + return -1; + } + h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); + SetEvent(h); + Sleep(50); /* some time for the server */ + if ( !(p = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentReadFD")) ) { + log_error( _("can't get server read FD for the agent\n") ); + return -1; + } + read_fd = atol(p); + free(p); + if ( !(p = read_w32_registry_string(NULL, "Software\\GNU\\GnuPG", + "agentWriteFD")) ) { + log_error ( _("can't get server write FD for the agent\n") ); + return -1; + } + write_fd = atol(p); + free(p); + fd = 0; + + if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { + fd = -1; + } +#else /* Posix */ + int fd; char *infostr, *p; struct sockaddr_un client_addr; size_t len; + int prot; + + if (opt.gpg_agent_info) + infostr = m_strdup (opt.gpg_agent_info); + else + { + infostr = getenv ( "GPG_AGENT_INFO" ); + if ( !infostr ) { + log_error (_("gpg-agent is not available in this session\n")); + opt.use_agent = 0; + return -1; + } + infostr = m_strdup ( infostr ); + } - infostr = getenv ( "GPG_AGENT_INFO" ); - if ( !infostr ) { - log_error (_("gpg-agent is not available in this session\n")); - return -1; - } - infostr = gcry_xstrdup ( infostr ); if ( !(p = strchr ( infostr, ':')) || p == infostr || (p-infostr)+1 >= sizeof client_addr.sun_path ) { - log_error (_("malformed GPG_AGENT_INFO environment variable\n")); - gcry_free (infostr ); + log_error( _("malformed GPG_AGENT_INFO environment variable\n")); + m_free (infostr ); + opt.use_agent = 0; return -1; } - *p = 0; - + *p++ = 0; + /* See whether this is the new gpg-agent using the Assuna protocl. + This agent identifies itself by have an info string with a + version number in the 3rd field. */ + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if ( prot < 0 || prot > 1) { + log_error (_("gpg-agent protocol version %d is not supported\n"),prot); + m_free (infostr ); + opt.use_agent = 0; + return -1; + } + *ret_prot = prot; + if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) { log_error ("can't create socket: %s\n", strerror(errno) ); - gcry_free (infostr ); + m_free (infostr ); + opt.use_agent = 0; return -1; } - + memset( &client_addr, 0, sizeof client_addr ); client_addr.sun_family = AF_UNIX; strcpy( client_addr.sun_path, infostr ); @@ -222,24 +531,57 @@ agent_open () if( connect( fd, (struct sockaddr*)&client_addr, len ) == -1 ) { log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno) ); - gcry_free (infostr ); + m_free (infostr ); close (fd ); + opt.use_agent = 0; return -1; } - gcry_free (infostr); + m_free (infostr); - if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { + if (!prot) { + if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { + close (fd); + fd = -1; + } + } + else { /* assuan based gpg-agent */ + char line[200]; + int nread; + + nread = readline (fd, line, DIM(line)); + if (nread < 3 || !(line[0] == 'O' && line[1] == 'K' + && (line[2] == '\n' || line[2] == ' ')) ) { + log_error ( _("communication problem with gpg-agent\n")); + close (fd ); + opt.use_agent = 0; + return -1; + } + + if (agent_send_all_options (fd)) { + log_error (_("problem with the agent - disabling agent use\n")); close (fd); - fd = -1; + opt.use_agent = 0; + return -1; + } + } +#endif + return fd; } + static void agent_close ( int fd ) { +#if defined (__MINGW32__) || defined (__CYGWIN32__) + HANDLE h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); + ResetEvent(h); +#else close (fd); +#endif } +#endif /* !__riscos__ */ @@ -250,210 +592,345 @@ agent_close ( int fd ) * 2: Ditto, but change the text to "repeat entry" */ static char * -agent_get_passphrase ( u32 *keyid, int mode ) +agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text ) { - size_t n; - char *atext; - char buf[50]; - int fd = -1; - int nread; - u32 reply; - char *pw = NULL; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - byte fpr[MAX_FINGERPRINT_LEN]; +#if defined(__riscos__) + return NULL; +#else + size_t n; + char *atext; + char buf[50]; + int fd = -1; + int nread; + u32 reply; + char *pw = NULL; + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); + byte fpr[MAX_FINGERPRINT_LEN]; + int prot; #if MAX_FINGERPRINT_LEN < 20 - #error agent needs a 20 byte fingerprint +#error agent needs a 20 byte fingerprint #endif - memset (fpr, 0, MAX_FINGERPRINT_LEN ); - if( keyid && get_pubkey( pk, keyid ) ) - pk = NULL; /* oops: no key for some reason */ - - if ( !mode && pk ) { - char *uid; - size_t uidlen; - const char *algo_name = gcry_pk_algo_name( pk->pubkey_algo ); - const char *timestr; - char *maink; - const char *fmtstr; - - if ( !algo_name ) - algo_name = "?"; - - fmtstr = _(" (main key ID %08lX)"); - maink = gcry_xmalloc ( strlen (fmtstr) + 20 ); - if( keyid[2] && keyid[3] && keyid[0] != keyid[2] - && keyid[1] != keyid[3] ) - sprintf( maink, fmtstr, (ulong)keyid[3] ); - else - *maink = 0; - - uid = get_user_id( keyid, &uidlen ); - timestr = strtimestamp (pk->timestamp); - fmtstr = _("You need a passphrase to unlock the" - " secret key for user:\n" - "\"%.*s\"\n" - "%u-bit %s key, ID %08lX, created %s%s\n" ); - atext = gcry_xmalloc ( 100 + strlen (fmtstr) - + uidlen + 15 + strlen(algo_name) + 8 - + strlen (timestr) + strlen (maink) ); - sprintf (atext, fmtstr, - uidlen, uid, - nbits_from_pk (pk), algo_name, (ulong)keyid[1], timestr, - maink ); - gcry_free (uid); - gcry_free (maink); - - { - size_t dummy; - fingerprint_from_pk( pk, fpr, &dummy ); - } - + memset (fpr, 0, MAX_FINGERPRINT_LEN ); + if( keyid && get_pubkey( pk, keyid ) ) + pk = NULL; /* oops: no key for some reason */ + + if ( !mode && pk ) + { + char *uid; + size_t uidlen; + const char *algo_name = pubkey_algo_to_string ( pk->pubkey_algo ); + const char *timestr; + char *maink; + const char *fmtstr; + + if ( !algo_name ) + algo_name = "?"; + + fmtstr = _(" (main key ID %08lX)"); + maink = m_alloc ( strlen (fmtstr) + 20 ); + if( keyid[2] && keyid[3] && keyid[0] != keyid[2] + && keyid[1] != keyid[3] ) + sprintf( maink, fmtstr, (ulong)keyid[3] ); + else + *maink = 0; + + uid = get_user_id( keyid, &uidlen ); + timestr = strtimestamp (pk->timestamp); + fmtstr = _("You need a passphrase to unlock the" + " secret key for user:\n" + "\"%.*s\"\n" + "%u-bit %s key, ID %08lX, created %s%s\n" ); + atext = m_alloc ( 100 + strlen (fmtstr) + + uidlen + 15 + strlen(algo_name) + 8 + + strlen (timestr) + strlen (maink) ); + sprintf (atext, fmtstr, + uidlen, uid, + nbits_from_pk (pk), algo_name, (ulong)keyid[1], timestr, + maink ); + m_free (uid); + m_free (maink); + + { + size_t dummy; + fingerprint_from_pk( pk, fpr, &dummy ); + } + } - else if (mode == 1 ) - atext = gcry_xstrdup ( _("Enter passphrase\n") ); - else - atext = gcry_xstrdup ( _("Repeat passphrase\n") ); - - - - if ( (fd = agent_open ()) == -1 ) + else if (mode == 1 ) + atext = m_strdup ( _("Enter passphrase\n") ); + else + atext = m_strdup ( _("Repeat passphrase\n") ); + + if ( (fd = agent_open (&prot)) == -1 ) + goto failure; + + if (!prot) + { /* old style protocol */ + n = 4 + 20 + strlen (atext); + u32tobuf (buf, n ); + u32tobuf (buf+4, GPGA_PROT_GET_PASSPHRASE ); + memcpy (buf+8, fpr, 20 ); + if ( writen ( fd, buf, 28 ) || writen ( fd, atext, strlen (atext) ) ) goto failure; - - - n = 4 + 20 + strlen (atext); - u32tobuf (buf, n ); - u32tobuf (buf+4, GPGA_PROT_GET_PASSPHRASE ); - memcpy (buf+8, fpr, 20 ); - if ( writen ( fd, buf, 28 ) || writen ( fd, atext, strlen (atext) ) ) - goto failure; - gcry_free (atext); atext = NULL; - - /* get response */ - if ( readn ( fd, buf, 12, &nread ) ) + m_free (atext); atext = NULL; + + /* get response */ + if ( readn ( fd, buf, 12, &nread ) ) goto failure; - - if ( nread < 8 ) { - log_error ( _("response from agent too short\n") ); - goto failure; - } - n = buftou32 ( buf ); - reply = buftou32 ( buf + 4 ); - if ( reply == GPGA_PROT_GOT_PASSPHRASE ) { - size_t pwlen; - size_t nn; - - if ( nread < 12 || n < 8 ) { - log_error ( _("response from agent too short\n") ); - goto failure; + + if ( nread < 8 ) + { + log_error ( "response from agent too short\n" ); + goto failure; } - pwlen = buftou32 ( buf + 8 ); - nread -= 12; - n -= 8; - if ( pwlen > n || n > 1000 ) { - log_error (_("passphrase too long\n")); - /* or protocol error */ - goto failure; - } - /* we read the whole block in one chunk to give no hints - * on how long the passhrase actually is - this wastes some bytes - * but because we already have this padding we should not loosen - * the by issuing 2 read calls */ - pw = gcry_xmalloc_secure ( n+1 ); - if ( readn ( fd, pw, n, &nn ) ) + n = buftou32 ( buf ); + reply = buftou32 ( buf + 4 ); + if ( reply == GPGA_PROT_GOT_PASSPHRASE ) + { + size_t pwlen; + size_t nn; + + if ( nread < 12 || n < 8 ) + { + log_error ( "response from agent too short\n" ); + goto failure; + } + pwlen = buftou32 ( buf + 8 ); + nread -= 12; + n -= 8; + if ( pwlen > n || n > 1000 ) + { + log_error (_("passphrase too long\n")); + /* or protocol error */ + goto failure; + } + /* we read the whole block in one chunk to give no hints + * on how long the passhrase actually is - this wastes some bytes + * but because we already have this padding we should not loosen + * this by issuing 2 read calls */ + pw = m_alloc_secure ( n+1 ); + if ( readn ( fd, pw, n, &nn ) ) goto failure; - if ( n != nn ) { - log_error (_("invalid response from agent\n")); - goto failure; + if ( n != nn ) + { + log_error (_("invalid response from agent\n")); + goto failure; + } + pw[pwlen] = 0; /* make a C String */ + agent_close (fd); + free_public_key( pk ); + return pw; } - pw[pwlen] = 0; /* make a C String */ - agent_close (fd); - free_public_key( pk ); - return pw; - } - else if ( reply == GPGA_PROT_CANCELED ) { + else if ( reply == GPGA_PROT_CANCELED ) log_info ( _("cancelled by user\n") ); - } - else { + else log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); + (ulong)reply ); } + else + { /* The new Assuan protocol */ + char *line, *p; + int i; + + if (!tryagain_text) + tryagain_text = "X"; + + /* We allocate 2 time the needed space for atext so that there + is nenough space for escaping */ + line = m_alloc (15 + 46 + + 3*strlen (tryagain_text) + 3*strlen (atext) + 2); + strcpy (line, "GET_PASSPHRASE "); + p = line+15; + if (!mode) + { + for (i=0; i < 20; i++, p +=2 ) + sprintf (p, "%02X", fpr[i]); + } + else + *p++ = 'X'; /* no caching */ + *p++ = ' '; + for (i=0; tryagain_text[i]; i++) + { + if (tryagain_text[i] < ' ' || tryagain_text[i] == '+') + { + sprintf (p, "%%%02X", tryagain_text[i]); + p += 3; + } + else if (tryagain_text[i] == ' ') + *p++ = '+'; + else + *p++ = tryagain_text[i]; + } + *p++ = ' '; + *p++ = 'X'; /* Use the standard prompt */ + *p++ = ' '; + /* copy description */ + for (i=0; atext[i]; i++) + { + if (atext[i] < ' ' || atext[i] == '+') + { + sprintf (p, "%%%02X", atext[i]); + p += 3; + } + else if (atext[i] == ' ') + *p++ = '+'; + else + *p++ = atext[i]; + } + *p++ = '\n'; + i = writen (fd, line, p - line); + m_free (line); + if (i) + goto failure; + m_free (atext); atext = NULL; + + /* get response */ + pw = m_alloc_secure (500); + nread = readline (fd, pw, 499); + if (nread < 3) + goto failure; + + if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') + { /* we got a passphrase - convert it back from hex */ + size_t pwlen = 0; + + for (i=3; i < nread && hexdigitp (pw+i); i+=2) + pw[pwlen++] = xtoi_2 (pw+i); + pw[pwlen] = 0; /* make a C String */ + agent_close (fd); + free_public_key( pk ); + return pw; + } + else if (nread > 7 && !memcmp (pw, "ERR 111", 7) + && (pw[7] == ' ' || pw[7] == '\n') ) + log_info (_("cancelled by user\n") ); + else + { + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; + } + } + - - failure: - gcry_free (atext); - if ( fd != -1 ) - agent_close (fd); - gcry_free (pw ); - free_public_key( pk ); - - return NULL; + failure: + m_free (atext); + if ( fd != -1 ) + agent_close (fd); + m_free (pw ); + free_public_key( pk ); + + return NULL; +#endif /* Posix or W32 */ } - /* - * Reste the cached passphrase + * Clear the cached passphrase */ void passphrase_clear_cache ( u32 *keyid, int algo ) { - size_t n; - char buf[50]; - int fd = -1; - int nread; - u32 reply; - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); - byte fpr[MAX_FINGERPRINT_LEN]; - +#if defined(__riscos__) + return ; +#else + size_t n; + char buf[200]; + int fd = -1; + size_t nread; + u32 reply; + PKT_public_key *pk; + byte fpr[MAX_FINGERPRINT_LEN]; + int prot; + #if MAX_FINGERPRINT_LEN < 20 #error agent needs a 20 byte fingerprint #endif - memset (fpr, 0, MAX_FINGERPRINT_LEN ); - if( !keyid || get_pubkey( pk, keyid ) ) { - log_debug ("oops, no key in passphrase_clear_cache\n"); - goto failure; /* oops: no key for some reason */ - } - + if (!opt.use_agent) + return; + + pk = m_alloc_clear ( sizeof *pk ); + memset (fpr, 0, MAX_FINGERPRINT_LEN ); + if( !keyid || get_pubkey( pk, keyid ) ) { - size_t dummy; - fingerprint_from_pk( pk, fpr, &dummy ); + log_debug ("oops, no key in passphrase_clear_cache\n"); + goto failure; /* oops: no key for some reason */ } + + { + size_t dummy; + fingerprint_from_pk( pk, fpr, &dummy ); + } - if ( (fd = agent_open ()) == -1 ) + if ( (fd = agent_open (&prot)) == -1 ) + goto failure; + + if (!prot) + { + n = 4 + 20; + u32tobuf (buf, n ); + u32tobuf (buf+4, GPGA_PROT_CLEAR_PASSPHRASE ); + memcpy (buf+8, fpr, 20 ); + if ( writen ( fd, buf, 28 ) ) goto failure; - - n = 4 + 20; - u32tobuf (buf, n ); - u32tobuf (buf+4, GPGA_PROT_CLEAR_PASSPHRASE ); - memcpy (buf+8, fpr, 20 ); - if ( writen ( fd, buf, 28 ) ) + + /* get response */ + if ( readn ( fd, buf, 8, &nread ) ) goto failure; - - /* get response */ - if ( readn ( fd, buf, 8, &nread ) ) - goto failure; - - if ( nread < 8 ) { - log_error ( _("response from agent too short\n") ); + + if ( nread < 8 ) { + log_error ( "response from agent too short\n" ); goto failure; + } + + reply = buftou32 ( buf + 4 ); + if ( reply != GPGA_PROT_OKAY && reply != GPGA_PROT_NO_PASSPHRASE ) + { + log_error ( _("problem with the agent: agent returns 0x%lx\n"), + (ulong)reply ); + } } - - reply = buftou32 ( buf + 4 ); - if ( reply != GPGA_PROT_OKAY && reply != GPGA_PROT_NO_PASSPHRASE ) { - log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); + else + { /* The assuan protocol */ + char *line, *p; + int i; + + line = m_alloc (17 + 40 + 2); + strcpy (line, "CLEAR_PASSPHRASE "); + p = line+17; + for (i=0; i < 20; i++, p +=2 ) + sprintf (p, "%02X", fpr[i]); + *p++ = '\n'; + i = writen (fd, line, p - line); + m_free (line); + if (i) + goto failure; + + /* get response */ + nread = readline (fd, buf, DIM(buf)-1); + if (nread < 3) + goto failure; + + if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) + ; + else + { + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; + } } - - failure: - if ( fd != -1 ) - agent_close (fd); - free_public_key( pk ); + failure: + if (fd != -1) + agent_close (fd); + free_public_key( pk ); +#endif /* Posix or W32 */ } + + /**************** * Get a passphrase for the secret key with KEYID, display TEXT * if the user needs to enter the passphrase. @@ -467,31 +944,49 @@ passphrase_clear_cache ( u32 *keyid, int algo ) */ DEK * passphrase_to_dek( u32 *keyid, int pubkey_algo, - int cipher_algo, STRING2KEY *s2k, int mode ) + int cipher_algo, STRING2KEY *s2k, int mode, + const char *tryagain_text) { char *pw = NULL; DEK *dek; STRING2KEY help_s2k; if( !s2k ) { + /* This is used for the old rfc1991 mode + * Note: This must match the code in encode.c with opt.rfc1991 set */ + int algo = opt.def_digest_algo ? opt.def_digest_algo + : opt.s2k_digest_algo; + s2k = &help_s2k; s2k->mode = 0; - /* this should be MD5 if cipher is IDEA, but because we do - * not have IDEA, we use the default one, the user - * can select it from the commandline - */ - s2k->hash_algo = opt.def_digest_algo?opt.def_digest_algo - :DEFAULT_DIGEST_ALGO; + s2k->hash_algo = algo; } if( !next_pw && is_status_enabled() ) { char buf[50]; + if( keyid ) { - sprintf( buf, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1] ); - if( keyid[2] && keyid[3] && keyid[0] != keyid[2] - && keyid[1] != keyid[3] ) - sprintf( buf+strlen(buf), " %08lX%08lX %d 0", - (ulong)keyid[2], (ulong)keyid[3], pubkey_algo ); + u32 used_kid[2]; + char *us; + + if( keyid[2] && keyid[3] ) { + used_kid[0] = keyid[2]; + used_kid[1] = keyid[3]; + } + else { + used_kid[0] = keyid[0]; + used_kid[1] = keyid[1]; + } + + us = get_long_user_id_string( keyid ); + write_status_text( STATUS_USERID_HINT, us ); + m_free(us); + + sprintf( buf, "%08lX%08lX %08lX%08lX %d 0", + (ulong)keyid[0], (ulong)keyid[1], + (ulong)used_kid[0], (ulong)used_kid[1], + pubkey_algo ); + write_status_text( STATUS_NEED_PASSPHRASE, buf ); } else { @@ -501,7 +996,7 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, } if( keyid && !opt.batch && !next_pw ) { - PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk ); + PKT_public_key *pk = m_alloc_clear( sizeof *pk ); size_t n; char *p; @@ -509,11 +1004,11 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, "user: \"") ); p = get_user_id( keyid, &n ); tty_print_utf8_string( p, n ); - gcry_free(p); + m_free(p); tty_printf("\"\n"); if( !get_pubkey( pk, keyid ) ) { - const char *s = gcry_pk_algo_name( pk->pubkey_algo ); + const char *s = pubkey_algo_to_string( pk->pubkey_algo ); tty_printf( _("%u-bit %s key, ID %08lX, created %s"), nbits_from_pk( pk ), s?s:"?", (ulong)keyid[1], strtimestamp(pk->timestamp) ); @@ -527,33 +1022,46 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, free_public_key( pk ); } + agent_died: if( next_pw ) { pw = next_pw; next_pw = NULL; } else if ( opt.use_agent ) { - pw = agent_get_passphrase ( keyid, mode == 2? 1: 0 ); - if ( !pw ) - pw = gcry_xstrdup (""); + pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, tryagain_text ); + if (!pw) + { + if (!opt.use_agent) + goto agent_died; + pw = m_strdup (""); + } if( *pw && mode == 2 ) { - char *pw2 = agent_get_passphrase ( keyid, 2 ); - if ( !pw2 ) - pw2 = gcry_xstrdup (""); + char *pw2 = agent_get_passphrase ( keyid, 2, NULL ); + if (!pw2) + { + if (!opt.use_agent) + { + m_free (pw); + pw = NULL; + goto agent_died; + } + pw2 = m_strdup (""); + } if( strcmp(pw, pw2) ) { - gcry_free(pw2); - gcry_free(pw); + m_free(pw2); + m_free(pw); return NULL; } - gcry_free(pw2); + m_free(pw2); } } else if( fd_passwd ) { - pw = gcry_xmalloc_secure( strlen(fd_passwd)+1 ); + pw = m_alloc_secure( strlen(fd_passwd)+1 ); strcpy( pw, fd_passwd ); } else if( opt.batch ) { log_error(_("can't query password in batchmode\n")); - pw = gcry_xstrdup( "" ); /* return an empty passphrase */ + pw = m_strdup( "" ); /* return an empty passphrase */ } else { pw = cpr_get_hidden("passphrase.enter", _("Enter passphrase: ") ); @@ -563,24 +1071,24 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, _("Repeat passphrase: ") ); tty_kill_prompt(); if( strcmp(pw, pw2) ) { - gcry_free(pw2); - gcry_free(pw); + m_free(pw2); + m_free(pw); return NULL; } - gcry_free(pw2); + m_free(pw2); } } if( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); - dek = gcry_xmalloc_secure( sizeof *dek ); + dek = m_alloc_secure_clear ( sizeof *dek ); dek->algo = cipher_algo; if( !*pw && mode == 2 ) dek->keylen = 0; else hash_passphrase( dek, pw, s2k, mode==2 ); - gcry_free(last_pw); + m_free(last_pw); last_pw = pw; return dek; } @@ -594,24 +1102,28 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) { - GCRY_MD_HD md; + MD_HANDLE md; int pass, i; int used = 0; int pwlen = strlen(pw); assert( s2k->hash_algo ); - dek->keylen = gcry_cipher_get_algo_keylen( dek->algo ); + dek->keylen = cipher_get_keylen( dek->algo ) / 8; if( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) ) BUG(); - if( !(md = gcry_md_open( s2k->hash_algo, GCRY_MD_FLAG_SECURE )) ) - BUG(); - + md = md_open( s2k->hash_algo, 1); for(pass=0; used < dek->keylen ; pass++ ) { if( pass ) { - gcry_md_reset(md); + if( (opt.emulate_bugs & EMUBUG_3DESS2K)) { + int tmp = md->finalized; + md_reset( md ); + md->finalized = tmp; + } + else + md_reset(md); for(i=0; i < pass; i++ ) /* preset the hash context */ - gcry_md_putc(md, 0 ); + md_putc(md, 0 ); } if( s2k->mode == 1 || s2k->mode == 3 ) { @@ -619,7 +1131,7 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) ulong count = len2; if( create && !pass ) { - gcry_randomize(s2k->salt, 8, GCRY_STRONG_RANDOM ); + randomize_buffer(s2k->salt, 8, 1); if( s2k->mode == 3 ) s2k->count = 96; /* 65536 iterations */ } @@ -631,27 +1143,27 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) } /* a little bit complicated because we need a ulong for count */ while( count > len2 ) { /* maybe iterated+salted */ - gcry_md_write( md, s2k->salt, 8 ); - gcry_md_write( md, pw, pwlen ); + md_write( md, s2k->salt, 8 ); + md_write( md, pw, pwlen ); count -= len2; } if( count < 8 ) - gcry_md_write( md, s2k->salt, count ); + md_write( md, s2k->salt, count ); else { - gcry_md_write( md, s2k->salt, 8 ); + md_write( md, s2k->salt, 8 ); count -= 8; - gcry_md_write( md, pw, count ); + md_write( md, pw, count ); } } else - gcry_md_write( md, pw, pwlen ); - gcry_md_final( md ); - i = gcry_md_get_algo_dlen( s2k->hash_algo ); + md_write( md, pw, pwlen ); + md_final( md ); + i = md_digest_length( s2k->hash_algo ); if( i > dek->keylen - used ) i = dek->keylen - used; - memcpy( dek->key+used, gcry_md_read(md, s2k->hash_algo), i ); + memcpy( dek->key+used, md_read(md, s2k->hash_algo), i ); used += i; } - gcry_md_close(md); + md_close(md); } |