diff options
Diffstat (limited to '')
-rw-r--r-- | g10/passphrase.c | 1185 |
1 files changed, 509 insertions, 676 deletions
diff --git a/g10/passphrase.c b/g10/passphrase.c index 30149908e..c63ee66d4 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,5 +1,6 @@ /* passphrase.c - Get a passphrase - * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include <config.h> @@ -29,8 +31,8 @@ #include <sys/socket.h> #include <sys/un.h> #endif -#if defined (_WIN32) || defined (__CYGWIN32__) -# include <windows.h> +#if defined (_WIN32) +#include <windows.h> #endif #include <errno.h> #ifdef HAVE_LOCALE_H @@ -42,7 +44,6 @@ #include "gpg.h" #include "util.h" -#include "memory.h" #include "options.h" #include "ttyio.h" #include "cipher.h" @@ -50,62 +51,14 @@ #include "main.h" #include "i18n.h" #include "status.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))) -#define u32tobuf( p, a ) do { \ - ((byte*)p)[0] = (byte)((a) >> 24); \ - ((byte*)p)[1] = (byte)((a) >> 16); \ - ((byte*)p)[2] = (byte)((a) >> 8); \ - ((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)) - - +#ifdef ENABLE_AGENT_SUPPORT +#include "assuan.h" +#endif /*ENABLE_AGENT_SUPPORT*/ static char *fd_passwd = NULL; static char *next_pw = NULL; static char *last_pw = NULL; -#if defined (_WIN32) -static int read_fd = 0; -static int write_fd = 0; -#endif - static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ); int @@ -123,10 +76,10 @@ have_static_passphrase() void set_next_passphrase( const char *s ) { - xfree (next_pw); + xfree(next_pw); next_pw = NULL; if( s ) { - next_pw = gcry_xmalloc_secure ( strlen(s)+1 ); + next_pw = xmalloc_secure( strlen(s)+1 ); strcpy(next_pw, s ); } } @@ -144,6 +97,30 @@ get_last_passphrase() return p; } +/* As if we had used the passphrase - make it the last_pw. */ +void +next_to_last_passphrase(void) +{ + if(next_pw) + { + last_pw=next_pw; + next_pw=NULL; + } +} + +/* Here's an interesting question: since this passphrase was passed in + on the command line, is there really any point in using secure + memory for it? I'm going with 'yes', since it doesn't hurt, and + might help in some small way (swapping). */ + +void +set_passphrase_from_string(const char *pass) +{ + xfree( fd_passwd ); + fd_passwd = xmalloc_secure(strlen(pass)+1); + strcpy(fd_passwd,pass); +} + void read_passphrase_from_fd( int fd ) @@ -171,9 +148,12 @@ read_passphrase_from_fd( int fd ) { char *pw2 = pw; len += 100; - pw = gcry_xmalloc_secure ( len ); + pw = xmalloc_secure( len ); if( pw2 ) - memcpy(pw, pw2, i ); + { + memcpy(pw, pw2, i ); + xfree (pw2); + } else i=0; } @@ -184,181 +164,33 @@ read_passphrase_from_fd( int fd ) if (!opt.batch) tty_printf("\b\b\b \n" ); - xfree ( fd_passwd ); + xfree( fd_passwd ); fd_passwd = pw; } -static int -writen ( int fd, const void *buf, size_t nbytes ) -{ -#if defined (_WIN32) - 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; - int nwritten; - - while( nleft > 0 ) { - nwritten = write( fd, buf, nleft ); - if( nwritten < 0 ) { - if ( errno == EINTR ) - nwritten = 0; - else { - log_error ( "write() failed: %s\n", strerror (errno) ); - return -1; - } - } - nleft -= nwritten; - buf = (const char*)buf + nwritten; - } -#endif - - return 0; -} +#ifdef ENABLE_AGENT_SUPPORT +/* Send one option to the gpg-agent. */ static int -readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) +agent_send_option (assuan_context_t ctx, const char *name, const char *value) { -#if defined (_WIN32) - 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; - - p = buf; - while( nleft > 0 ) { - nread = read ( fd, buf, nleft ); - if( nread < 0 ) { - if (nread == EINTR) - nread = 0; - else { - log_error ( "read() error: %s\n", strerror (errno) ); - return -1; - } - } - else if( !nread ) - break; /* EOF */ - nleft -= nread; - buf = (char*)buf + 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 (_WIN32) -/* 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; + int rc; - line = xmalloc (7 + strlen (name) + 1 + strlen (value) + 2); - strcpy (stpcpy (stpcpy (stpcpy ( - stpcpy (line, "OPTION "), name), "="), value), "\n"); - i = writen (fd, line, strlen (line)); - xfree (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 */ + if (!value || !*value) + return 0; /* Avoid sending empty option values. */ - return -1; + line = xmalloc (7 + strlen (name) + 1 + strlen (value) + 1); + strcpy (stpcpy (stpcpy (stpcpy (line, "OPTION "), name), "="), value); + rc = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + xfree (line); + return rc? -1 : 0; } +/* Send all required options to the gpg-agent. */ static int -agent_send_all_options (int fd) +agent_send_all_options (assuan_context_t ctx) { char *dft_display = NULL; const char *dft_ttyname = NULL; @@ -370,20 +202,24 @@ agent_send_all_options (int fd) dft_display = getenv ("DISPLAY"); if (opt.display || dft_display) { - if (agent_send_option (fd, "display", + if (agent_send_option (ctx, "display", opt.display ? opt.display : dft_display)) return -1; } if (!opt.ttyname) { + const char *tmp; + dft_ttyname = getenv ("GPG_TTY"); - if ((!dft_ttyname || !*dft_ttyname) && tty_get_ttyname ()) - dft_ttyname = tty_get_ttyname (); + if ((!dft_ttyname || !*dft_ttyname) && (tmp=ttyname (0))) + dft_ttyname = tmp; + if ((!dft_ttyname || !*dft_ttyname) && (tmp=tty_get_ttyname ())) + dft_ttyname = tmp; } if (opt.ttyname || dft_ttyname) { - if (agent_send_option (fd, "ttyname", + if (agent_send_option (ctx, "ttyname", opt.ttyname ? opt.ttyname : dft_ttyname)) return -1; } @@ -391,7 +227,7 @@ agent_send_all_options (int fd) dft_ttytype = getenv ("TERM"); if (opt.ttytype || (dft_ttyname && dft_ttytype)) { - if (agent_send_option (fd, "ttytype", + if (agent_send_option (ctx, "ttytype", opt.ttyname ? opt.ttytype : dft_ttytype)) return -1; } @@ -404,7 +240,7 @@ agent_send_all_options (int fd) #endif if (opt.lc_ctype || (dft_ttyname && dft_lc)) { - rc = agent_send_option (fd, "lc-ctype", + rc = agent_send_option (ctx, "lc-ctype", opt.lc_ctype ? opt.lc_ctype : dft_lc); } #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) @@ -425,7 +261,7 @@ agent_send_all_options (int fd) #endif if (opt.lc_messages || (dft_ttyname && dft_lc)) { - rc = agent_send_option (fd, "lc-messages", + rc = agent_send_option (ctx, "lc-messages", opt.lc_messages ? opt.lc_messages : dft_lc); } #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) @@ -437,170 +273,186 @@ agent_send_all_options (int fd) #endif return rc; } -#endif /*!_WIN32*/ +#endif /*ENABLE_AGENT_SUPPORT*/ /* - * Open a connection to the agent and send the magic string - * Returns: -1 on error or an filedescriptor for urther processing + * Open a connection to the agent and initializes the connection. + * Returns: -1 on error; on success an Assuan context for that + * connection is returned. With TRY set to true, no error messages + * are printed and the use of the agent won't get disabled on failure. + * If ORIG_CODESET is not NULL, the function will swithc the codeset + * back to that one before printing error messages. */ - -static int -agent_open (int *ret_prot) +#ifdef ENABLE_AGENT_SUPPORT +assuan_context_t +agent_open (int try, const char *orig_codeset) { -#if defined (_WIN32) - 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; + int rc; + assuan_context_t ctx; + char *infostr, *p; + int prot; + int pid; - if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { - fd = -1; + if (opt.gpg_agent_info) + infostr = xstrdup (opt.gpg_agent_info); + else + { + infostr = getenv ( "GPG_AGENT_INFO" ); + if (!infostr || !*infostr) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_info (_("gpg-agent is not available in this session\n")); + opt.use_agent = 0; + } + return NULL; + } + infostr = xstrdup ( infostr ); } -#else /* Posix */ - - int fd; - char *infostr, *p; - struct sockaddr_un client_addr; - size_t len; - int prot; - - if (opt.gpg_agent_info) - infostr = xstrdup (opt.gpg_agent_info); - else - { - infostr = getenv ( "GPG_AGENT_INFO" ); - if ( !infostr || !*infostr ) { - log_error (_("gpg-agent is not available in this session\n")); + + if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error ( _("malformed GPG_AGENT_INFO environment variable\n")); opt.use_agent = 0; - return -1; } - infostr = 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")); - xfree (infostr ); - opt.use_agent = 0; - return -1; - } - *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); - xfree (infostr ); - opt.use_agent = 0; - return -1; + xfree (infostr); + return NULL; } - *ret_prot = prot; - - if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) { - log_error ("can't create socket: %s\n", strerror(errno) ); - xfree (infostr ); - opt.use_agent = 0; - return -1; + *p++ = 0; + pid = atoi (p); + while (*p && *p != PATHSEP_C) + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + opt.use_agent = 0; + } + xfree (infostr); + return NULL; } - - memset( &client_addr, 0, sizeof client_addr ); - client_addr.sun_family = AF_UNIX; - strcpy( client_addr.sun_path, infostr ); - len = offsetof (struct sockaddr_un, sun_path) - + strlen(client_addr.sun_path) + 1; - - if( connect( fd, (struct sockaddr*)&client_addr, len ) == -1 ) { - log_error ( _("can't connect to `%s': %s\n"), - infostr, strerror (errno) ); - xfree (infostr ); - close (fd ); - opt.use_agent = 0; - return -1; + + rc = assuan_socket_connect (&ctx, infostr, pid); + if (rc) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error ( _("can't connect to `%s': %s\n"), + infostr, assuan_strerror (rc)); + opt.use_agent = 0; + } + xfree (infostr ); + return NULL; } - xfree (infostr); + xfree (infostr); - if (!prot) { - if ( writen ( fd, "GPGA\0\0\0\x01", 8 ) ) { - close (fd); - fd = -1; + if (agent_send_all_options (ctx)) + { + if (!try) + { +#ifdef ENABLE_NLS + if (orig_codeset) + bind_textdomain_codeset (PACKAGE, orig_codeset); +#endif /*ENABLE_NLS*/ + log_error (_("problem with the agent - disabling agent use\n")); + opt.use_agent = 0; } + assuan_disconnect (ctx); + return NULL; } - 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); - opt.use_agent = 0; - return -1; - } - - } -#endif + return ctx; +} +#endif/*ENABLE_AGENT_SUPPORT*/ - return fd; + +#ifdef ENABLE_AGENT_SUPPORT +void +agent_close (assuan_context_t ctx) +{ + assuan_disconnect (ctx); } +#endif /*ENABLE_AGENT_SUPPORT*/ -static void -agent_close ( int fd ) +/* Copy the text ATEXT into the buffer P and do plus '+' and percent + escaping. Note that the provided buffer needs to be 3 times the + size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */ +#ifdef ENABLE_AGENT_SUPPORT +static char * +percent_plus_escape (char *p, const char *atext) { -#if defined (_WIN32) - HANDLE h = OpenEvent(EVENT_ALL_ACCESS, FALSE, "gpg_agent"); - ResetEvent(h); -#else - close (fd); -#endif + const unsigned char *s; + + for (s=atext; *s; s++) + { + if (*s < ' ' || *s == '+') + { + sprintf (p, "%%%02X", *s); + p += 3; + } + else if (*s == ' ') + *p++ = '+'; + else + *p++ = *s; + } + *p = 0; + return p; +} +#endif /*ENABLE_AGENT_SUPPORT*/ + + +#ifdef ENABLE_AGENT_SUPPORT + +/* Object for the agent_okay_cb function. */ +struct agent_okay_cb_s { + char *pw; +}; + +/* A callback used to get the passphrase from the okay line. See + agent-get_passphrase for details. LINE is the rest of the OK + status line without leading white spaces. */ +static assuan_error_t +agent_okay_cb (void *opaque, const char *line) +{ + struct agent_okay_cb_s *parm = opaque; + int i; + + /* Note: If the malloc below fails we won't be able to wipe the + memory at LINE given the current implementation of the Assuan + code. There is no easy ay around this w/o adding a lot of more + memory function code to allow wiping arbitrary stuff on memory + failure. */ + parm->pw = xmalloc_secure (strlen (line)/2+2); + + for (i=0; hexdigitp (line) && hexdigitp (line+1); line += 2) + parm->pw[i++] = xtoi_2 (line); + parm->pw[i] = 0; + return 0; } -#endif /* !__riscos__ */ +#endif /*ENABLE_AGENT_SUPPORT*/ @@ -612,26 +464,23 @@ agent_close ( int fd ) * * Note that TRYAGAIN_TEXT must not be translated. If canceled is not * NULL, the function does set it to 1 if the user canceled the - * operation. + * operation. If CACHEID is not NULL, it will be used as the cacheID + * for the gpg-agent; if is NULL and a key fingerprint can be + * computed, this will be used as the cacheid. */ static char * -agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, - int *canceled) +agent_get_passphrase ( u32 *keyid, int mode, const char *cacheid, + const char *tryagain_text, + const char *custom_description, + const char *custom_prompt, int *canceled) { -#if defined(__riscos__) - return NULL; -#else - size_t n; +#ifdef ENABLE_AGENT_SUPPORT char *atext = NULL; - char buf[50]; - int fd = -1; - int nread; - u32 reply; + assuan_context_t ctx = NULL; char *pw = NULL; - PKT_public_key *pk = xcalloc (1, sizeof *pk ); + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); byte fpr[MAX_FINGERPRINT_LEN]; int have_fpr = 0; - int prot; char *orig_codeset = NULL; if (canceled) @@ -644,13 +493,14 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, memset (fpr, 0, MAX_FINGERPRINT_LEN ); if( keyid && get_pubkey( pk, keyid ) ) { - free_public_key( pk ); + if (pk) + free_public_key( pk ); pk = NULL; /* oops: no key for some reason */ } #ifdef ENABLE_NLS /* The Assuan agent protocol requires us to transmit utf-8 strings */ - orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); + orig_codeset = bind_textdomain_codeset (PACKAGE, NULL); #ifdef HAVE_LANGINFO_CODESET if (!orig_codeset) orig_codeset = nl_langinfo (CODESET); @@ -658,44 +508,58 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, if (orig_codeset) { /* We only switch when we are able to restore the codeset later. */ orig_codeset = xstrdup (orig_codeset); - if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) + if (!bind_textdomain_codeset (PACKAGE, "utf-8")) orig_codeset = NULL; } #endif - if ( (fd = agent_open (&prot)) == -1 ) + if ( !(ctx = agent_open (0, orig_codeset)) ) goto failure; - if ( !mode && pk && keyid ) + if (custom_description) + atext = native_to_utf8 (custom_description); + else if ( !mode && pk && keyid ) { char *uid; size_t uidlen; - const char *algo_name = gcry_pk_algo_name ( pk->pubkey_algo ); + const char *algo_name = pubkey_algo_to_string ( pk->pubkey_algo ); const char *timestr; char *maink; if ( !algo_name ) algo_name = "?"; - + +#define KEYIDSTRING _(" (main key ID %s)") + + maink = xmalloc ( strlen (KEYIDSTRING) + keystrlen() + 20 ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) - maink = xasprintf ( _(" (main key ID %08lX)"), (ulong)keyid[3] ); + sprintf( maink, KEYIDSTRING, keystr(&keyid[2]) ); else - maink = NULL; + *maink = 0; uid = get_user_id ( keyid, &uidlen ); timestr = strtimestamp (pk->timestamp); - atext = xasprintf ( - _("You need a passphrase to unlock the" - " secret key for user:\n" - "\"%.*s\"\n" - "%u-bit %s key, ID %08lX, created %s%s\n" ), - uidlen, uid, - nbits_from_pk (pk), algo_name, (ulong)keyid[1], timestr, - maink?maink:"" ); + +#undef KEYIDSTRING + +#define PROMPTSTRING _("You need a passphrase to unlock the secret" \ + " key for user:\n" \ + "\"%.*s\"\n" \ + "%u-bit %s key, ID %s, created %s%s\n" ) + + atext = xmalloc ( 100 + strlen (PROMPTSTRING) + + uidlen + 15 + strlen(algo_name) + keystrlen() + + strlen (timestr) + strlen (maink) ); + sprintf (atext, PROMPTSTRING, + (int)uidlen, uid, + nbits_from_pk (pk), algo_name, keystr(&keyid[0]), timestr, + maink ); xfree (uid); xfree (maink); - + +#undef PROMPTSTRING + { size_t dummy; fingerprint_from_pk( pk, fpr, &dummy ); @@ -708,165 +572,80 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, else atext = xstrdup ( _("Enter passphrase\n") ); - 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; - xfree (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; - } - 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 = xmalloc_secure ( n+1 ); - if ( readn ( fd, pw, n, &nn ) ) - 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 ); -#ifdef ENABLE_NLS - if (orig_codeset) - bind_textdomain_codeset (PACKAGE_GT, orig_codeset); -#endif - xfree (orig_codeset); - return pw; - } - else if ( reply == GPGA_PROT_CANCELED ) - { - log_info ( _("cancelled by user\n") ); - if (canceled) - *canceled = 1; - } - else - log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); - } - else - { /* The new Assuan protocol */ + { char *line, *p; - const unsigned char *s; - int i; + int i, rc; + struct agent_okay_cb_s okay_cb_parm; if (!tryagain_text) tryagain_text = "X"; else tryagain_text = _(tryagain_text); - /* We allocate 2 time the needed space for atext so that there - is enough space for escaping */ + /* We allocate 23 times the needed space for thye texts so that + there is enough space for escaping. */ line = xmalloc (15 + 46 - + 3*strlen (tryagain_text) + 3*strlen (atext) + 2); + + 3*strlen (atext) + + 3*strlen (custom_prompt? custom_prompt:"") + + (cacheid? (3*strlen (cacheid)): 0) + + 3*strlen (tryagain_text) + + 1); strcpy (line, "GET_PASSPHRASE "); p = line+15; - if (!mode && have_fpr) + if (!mode && cacheid) + { + p = percent_plus_escape (p, cacheid); + } + else if (!mode && have_fpr) { for (i=0; i < 20; i++, p +=2 ) sprintf (p, "%02X", fpr[i]); } else - *p++ = 'X'; /* no caching */ - *p++ = ' '; - for (i=0, s=tryagain_text; *s; s++) - { - if (*s < ' ' || *s == '+') - { - sprintf (p, "%%%02X", *s); - p += 3; - } - else if (*s == ' ') - *p++ = '+'; - else - *p++ = *s; - } + *p++ = 'X'; /* No caching. */ *p++ = ' '; - *p++ = 'X'; /* Use the standard prompt */ + + p = percent_plus_escape (p, tryagain_text); *p++ = ' '; - /* copy description */ - for (i=0, s= atext; *s; s++) + + /* The prompt. */ + if (custom_prompt) { - if (*s < ' ' || *s == '+') - { - sprintf (p, "%%%02X", *s); - p += 3; - } - else if (*s == ' ') - *p++ = '+'; - else - *p++ = *s; + char *tmp = native_to_utf8 (custom_prompt); + p = percent_plus_escape (p, tmp); + xfree (tmp); } - *p++ = '\n'; - i = writen (fd, line, p - line); + else + *p++ = 'X'; /* Use the standard prompt. */ + *p++ = ' '; + + /* Copy description. */ + percent_plus_escape (p, atext); + + /* Call gpg-agent. */ + memset (&okay_cb_parm, 0, sizeof okay_cb_parm); + rc = assuan_transact2 (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL, + agent_okay_cb, &okay_cb_parm); + xfree (line); - if (i) - goto failure; xfree (atext); atext = NULL; - - /* get response */ - pw = xmalloc_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 ); + if (!rc) + { + assert (okay_cb_parm.pw); + pw = okay_cb_parm.pw; + agent_close (ctx); + if (pk) + free_public_key( pk ); #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE_GT, orig_codeset); + bind_textdomain_codeset (PACKAGE, orig_codeset); #endif xfree (orig_codeset); return pw; } - else if (nread > 4 && !memcmp (pw, "ERR ", 4) - && (0xffff & strtoul (&pw[4], NULL, 0)) == 99) + else if (rc && (rc & 0xffff) == 99) { - /* 99 is GPG_ERR_CANCELED. FIXME: Check tail and overflow, - and use gpg-error. */ + /* 99 is GPG_ERR_CANCELED. */ log_info (_("cancelled by user\n") ); if (canceled) *canceled = 1; @@ -876,41 +655,40 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, log_error (_("problem with the agent - disabling agent use\n")); opt.use_agent = 0; } - } + } failure: #ifdef ENABLE_NLS if (orig_codeset) - bind_textdomain_codeset (PACKAGE_GT, orig_codeset); + { + bind_textdomain_codeset (PACKAGE, orig_codeset); + xfree (orig_codeset); + } #endif xfree (atext); - if ( fd != -1 ) - agent_close (fd); + agent_close (ctx); xfree (pw ); - free_public_key( pk ); - + if (pk) + free_public_key( pk ); + +#endif /*ENABLE_AGENT_SUPPORT*/ + return NULL; -#endif /* Posix or W32 */ } + /* - * Clear the cached passphrase + * Clear the cached passphrase. If CACHEID is not NULL, it will be + * used instead of a cache ID derived from KEYID. */ void -passphrase_clear_cache ( u32 *keyid, int algo ) +passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) { -#if defined(__riscos__) - return ; -#else - size_t n; - char buf[200]; - int fd = -1; - size_t nread; - u32 reply; +#ifdef ENABLE_AGENT_SUPPORT + assuan_context_t ctx = NULL; PKT_public_key *pk; byte fpr[MAX_FINGERPRINT_LEN]; - int prot; #if MAX_FINGERPRINT_LEN < 20 #error agent needs a 20 byte fingerprint @@ -919,71 +697,50 @@ passphrase_clear_cache ( u32 *keyid, int algo ) if (!opt.use_agent) return; - pk = xcalloc (1, sizeof *pk ); - memset (fpr, 0, MAX_FINGERPRINT_LEN ); - if( !keyid || get_pubkey( pk, keyid ) ) + if (!cacheid) { - log_debug ("oops, no key in passphrase_clear_cache\n"); - goto failure; /* oops: no key for some reason */ - } + pk = xcalloc (1, sizeof *pk); + memset (fpr, 0, MAX_FINGERPRINT_LEN ); + if( !keyid || get_pubkey( pk, keyid ) ) + { + goto failure; /* oops: no key for some reason */ + } - { - size_t dummy; - fingerprint_from_pk( pk, fpr, &dummy ); - } + { + size_t dummy; + fingerprint_from_pk( pk, fpr, &dummy ); + } + } + else + pk = NULL; - if ( (fd = agent_open (&prot)) == -1 ) + if ( !(ctx = agent_open (0, NULL)) ) 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; - - /* get response */ - if ( readn ( fd, buf, 8, &nread ) ) - goto failure; - - 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 ) + { + char *line, *p; + int i, rc; + + if (cacheid) { - log_error ( _("problem with the agent: agent returns 0x%lx\n"), - (ulong)reply ); + line = xmalloc (17 + 3*strlen (cacheid) + 2); + strcpy (line, "CLEAR_PASSPHRASE "); + p = line+17; + p = percent_plus_escape (p, cacheid); } - } - else - { /* The assuan protocol */ - char *line, *p; - int i; - - line = xmalloc (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); + else + { + line = xmalloc (17 + 40 + 2); + strcpy (line, "CLEAR_PASSPHRASE "); + p = line+17; + for (i=0; i < 20; i++, p +=2 ) + sprintf (p, "%02X", fpr[i]); + } + *p = 0; + + rc = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); xfree (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 + if (rc) { log_error (_("problem with the agent - disabling agent use\n")); opt.use_agent = 0; @@ -991,32 +748,85 @@ passphrase_clear_cache ( u32 *keyid, int algo ) } failure: - if (fd != -1) - agent_close (fd); - free_public_key( pk ); -#endif /* Posix or W32 */ + agent_close (ctx); + if (pk) + free_public_key( pk ); +#endif /*ENABLE_AGENT_SUPPORT*/ } - - /**************** - * Get a passphrase for the secret key with KEYID, display TEXT - * if the user needs to enter the passphrase. - * mode 0 = standard, 1 = same but don't show key info, - * 2 = create new passphrase - * Returns: a DEK with a session key; caller must free - * or NULL if the passphrase was not correctly repeated. - * (only for mode 2) - * a dek->keylen of 0 means: no passphrase entered. - * (only for mode 2) - * - * pubkey_algo is only informational. Note that TRYAGAIN_TEXT must - * not be translated as this is done within this function (required to - * switch to utf-8 when the agent is in use). If CANCELED is not - * NULL, it is set to 1 if the user choosed to cancel the operation, - * otherwise it will be set to 0. + * Ask for a passphrase and return that string. */ +char * +ask_passphrase (const char *description, + const char *tryagain_text, + const char *promptid, + const char *prompt, + const char *cacheid, int *canceled) +{ + char *pw = NULL; + + if (canceled) + *canceled = 0; + + if (!opt.batch && description) + { + if (strchr (description, '%')) + { + char *tmp = unescape_percent_string (description); + tty_printf ("\n%s\n", tmp); + xfree (tmp); + } + else + tty_printf ("\n%s\n",description); + } + + agent_died: + if ( opt.use_agent ) + { + pw = agent_get_passphrase (NULL, 0, cacheid, + tryagain_text, description, prompt, + canceled ); + if (!pw) + { + if (!opt.use_agent) + goto agent_died; + pw = NULL; + } + } + else if (fd_passwd) + { + pw = xmalloc_secure (strlen(fd_passwd)+1); + strcpy (pw, fd_passwd); + } + else if (opt.batch) + { + log_error(_("can't query passphrase in batch mode\n")); + pw = NULL; + } + else { + if (tryagain_text) + tty_printf(_("%s.\n"), tryagain_text); + pw = cpr_get_hidden(promptid? promptid : "passphrase.ask", + prompt?prompt : _("Enter passphrase: ") ); + tty_kill_prompt(); + } + + if (!pw || !*pw) + write_status( STATUS_MISSING_PASSPHRASE ); + + return pw; +} + + +/* Return a new DEK object Using the string-to-key sepcifier S2K. Use + * KEYID and PUBKEY_ALGO to prompt the user. + + MODE 0: Allow cached passphrase + 1: Ignore cached passphrase + 2: Ditto, but change the text to "repeat entry" +*/ DEK * passphrase_to_dek( u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, @@ -1034,12 +844,14 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, * Note: This must match the code in encode.c with opt.rfc1991 set */ s2k = &help_s2k; s2k->mode = 0; - s2k->hash_algo = opt.s2k_digest_algo; + s2k->hash_algo = S2K_DIGEST_ALGO; } + /* If we do not have a passphrase available in NEXT_PW and status + information are request, we print them now. */ if( !next_pw && is_status_enabled() ) { char buf[50]; - + if( keyid ) { u32 used_kid[2]; char *us; @@ -1055,7 +867,7 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, us = get_long_user_id_string( keyid ); write_status_text( STATUS_USERID_HINT, us ); - xfree (us); + xfree(us); sprintf( buf, "%08lX%08lX %08lX%08lX %d 0", (ulong)keyid[0], (ulong)keyid[1], @@ -1070,41 +882,55 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, } } + /* If we do have a keyID, we do not have a passphrase available in + NEXT_PW, we are not running in batch mode and we do not want to + ignore the passphrase cache (mode!=1), print a prompt with + information on that key. */ if( keyid && !opt.batch && !next_pw && mode!=1 ) { - PKT_public_key *pk = xcalloc (1, sizeof *pk ); - size_t n; + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); char *p; - tty_printf(_("\nYou need a passphrase to unlock the secret key for\n" - "user: \"") ); - p = get_user_id( keyid, &n ); - tty_print_utf8_string( p, n ); - xfree (p); - tty_printf("\"\n"); + p=get_user_id_native(keyid); + tty_printf("\n"); + tty_printf(_("You need a passphrase to unlock the secret key for\n" + "user: \"%s\"\n"),p); + xfree(p); if( !get_pubkey( pk, keyid ) ) { - const char *s = gcry_pk_algo_name ( pk->pubkey_algo ); - tty_printf( _("%u-bit %s key, ID %08lX, created %s"), - nbits_from_pk( pk ), s?s:"?", (ulong)keyid[1], + const char *s = pubkey_algo_to_string( pk->pubkey_algo ); + tty_printf( _("%u-bit %s key, ID %s, created %s"), + nbits_from_pk( pk ), s?s:"?", keystr(keyid), strtimestamp(pk->timestamp) ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) - tty_printf( _(" (main key ID %08lX)"), (ulong)keyid[3] ); + { + if(keystrlen()>10) + { + tty_printf("\n"); + tty_printf(_(" (subkey on main key ID %s)"), + keystr(&keyid[2]) ); + } + else + tty_printf( _(" (main key ID %s)"), keystr(&keyid[2]) ); + } tty_printf("\n"); } tty_printf("\n"); - free_public_key( pk ); + if (pk) + free_public_key( pk ); } agent_died: if( next_pw ) { + /* Simply return the passphrase we already have in NEXT_PW. */ pw = next_pw; next_pw = NULL; } else if ( opt.use_agent ) { - pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, - tryagain_text, canceled ); + /* Divert to the gpg-agent. */ + pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, NULL, + tryagain_text, NULL, NULL, canceled ); if (!pw) { if (!opt.use_agent) @@ -1112,7 +938,8 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, pw = xstrdup (""); } if( *pw && mode == 2 ) { - char *pw2 = agent_get_passphrase ( keyid, 2, NULL, canceled ); + char *pw2 = agent_get_passphrase ( keyid, 2, NULL, NULL, NULL, + NULL, canceled ); if (!pw2) { if (!opt.use_agent) @@ -1124,22 +951,25 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, pw2 = xstrdup (""); } if( strcmp(pw, pw2) ) { - xfree (pw2); - xfree (pw); + xfree(pw2); + xfree(pw); return NULL; } - xfree (pw2); + xfree(pw2); } } else if( fd_passwd ) { - pw = xmalloc_secure ( strlen(fd_passwd)+1 ); + /* Return the passphrase we have store in FD_PASSWD. */ + pw = xmalloc_secure( strlen(fd_passwd)+1 ); strcpy( pw, fd_passwd ); } - else if( opt.batch ) { - log_error(_("can't query password in batchmode\n")); - pw = xstrdup ( "" ); /* return an empty passphrase */ - } + else if( opt.batch ) + { + log_error(_("can't query passphrase in batch mode\n")); + pw = xstrdup( "" ); /* return an empty passphrase */ + } else { + /* Read the passphrase from the tty or the command-fd. */ pw = cpr_get_hidden("passphrase.enter", _("Enter passphrase: ") ); tty_kill_prompt(); if( mode == 2 && !cpr_enabled() ) { @@ -1147,24 +977,27 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, _("Repeat passphrase: ") ); tty_kill_prompt(); if( strcmp(pw, pw2) ) { - xfree (pw2); - xfree (pw); + xfree(pw2); + xfree(pw); return NULL; } - xfree (pw2); + xfree(pw2); } } if( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); - dek = xcalloc_secure (1, sizeof *dek ); + /* Hash the passphrase and store it in a newly allocated DEK + object. Keep a copy of the passphrase in LAST_PW for use by + get_last_passphrase(). */ + dek = xmalloc_secure_clear ( sizeof *dek ); dek->algo = cipher_algo; if( !*pw && mode == 2 ) dek->keylen = 0; else hash_passphrase( dek, pw, s2k, mode==2 ); - xfree (last_pw); + xfree(last_pw); last_pw = pw; return dek; } @@ -1184,16 +1017,16 @@ hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create ) 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(); - gcry_md_open (&md, s2k->hash_algo, 1); + md = md_open( s2k->hash_algo, 1); for(pass=0; used < dek->keylen ; pass++ ) { if( pass ) { - gcry_md_reset(md); + 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 ) { @@ -1201,7 +1034,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 */ } @@ -1213,27 +1046,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); } |