X-Git-Url: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blobdiff_plain;f=g10%2Fpassphrase.c;h=d0a19d25d9ad3df5573877323a815940da486b7c;hp=593c85622d8e111a563084c3c397c3bc47aed820;hb=094a7ab401c03d6fc7cb1d3023ea8ee1e8dc9e34;hpb=c3a51cd17d3a499b14b6329fd9390fa9d73521b7 diff --git a/g10/passphrase.c b/g10/passphrase.c index 593c85622..d0a19d25d 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -1,6 +1,6 @@ /* passphrase.c - Get a passphrase - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, - * 2004 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -16,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 @@ -30,8 +31,8 @@ #include #include #endif -#if defined (_WIN32) || defined (__CYGWIN32__) -# include +#if defined (_WIN32) +#include #endif #include #ifdef HAVE_LOCALE_H @@ -50,32 +51,9 @@ #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 -}; +#ifdef ENABLE_AGENT_SUPPORT +#include "assuan.h" +#endif /*ENABLE_AGENT_SUPPORT*/ #define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ @@ -95,17 +73,10 @@ enum gpga_protocol_codes { *(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 (_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 +94,10 @@ have_static_passphrase() void set_next_passphrase( const char *s ) { - m_free(next_pw); + xfree(next_pw); next_pw = NULL; if( s ) { - next_pw = m_alloc_secure( strlen(s)+1 ); + next_pw = xmalloc_secure( strlen(s)+1 ); strcpy(next_pw, s ); } } @@ -144,6 +115,19 @@ get_last_passphrase() return p; } +/* 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 +155,12 @@ read_passphrase_from_fd( int fd ) { char *pw2 = pw; len += 100; - pw = m_alloc_secure( len ); + pw = xmalloc_secure( len ); if( pw2 ) - memcpy(pw, pw2, i ); + { + memcpy(pw, pw2, i ); + xfree (pw2); + } else i=0; } @@ -184,181 +171,33 @@ read_passphrase_from_fd( int fd ) if (!opt.batch) tty_printf("\b\b\b \n" ); - m_free( 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; -} - - -static int -readn ( int fd, void *buf, size_t buflen, size_t *ret_nread ) -{ -#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 */ +#ifdef ENABLE_AGENT_SUPPORT +/* Send one option to the gpg-agent. */ static int -agent_send_option (int fd, const char *name, const char *value) +agent_send_option (assuan_context_t ctx, const char *name, const char *value) { - char buf[200]; - int nread; char *line; - int i; + int rc; - 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; + if (!value || !*value) + return 0; /* Avoid sending empty option values. */ + + 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 +209,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 +234,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; } @@ -399,19 +242,19 @@ agent_send_all_options (int fd) #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) old_lc = setlocale (LC_CTYPE, NULL); if (old_lc) - old_lc = m_strdup (old_lc); + old_lc = xstrdup (old_lc); dft_lc = setlocale (LC_CTYPE, ""); #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) if (old_lc) { setlocale (LC_CTYPE, old_lc); - m_free (old_lc); + xfree (old_lc); } #endif if (rc) @@ -420,187 +263,203 @@ agent_send_all_options (int fd) #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) old_lc = setlocale (LC_MESSAGES, NULL); if (old_lc) - old_lc = m_strdup (old_lc); + old_lc = xstrdup (old_lc); dft_lc = setlocale (LC_MESSAGES, ""); #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) if (old_lc) { setlocale (LC_MESSAGES, old_lc); - m_free (old_lc); + xfree (old_lc); } #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 = 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")); + + 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 = m_strdup ( infostr ); - } - - if ( !(p = strchr ( infostr, ':')) || p == infostr - || (p-infostr)+1 >= sizeof client_addr.sun_path ) { - log_error( _("malformed GPG_AGENT_INFO environment variable\n")); - m_free (infostr ); - opt.use_agent = 0; - return -1; + xfree (infostr); + return NULL; } - *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) ); - m_free (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) ); - m_free (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; } - m_free (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 /* !__riscos__ */ +#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 /*ENABLE_AGENT_SUPPORT*/ @@ -612,26 +471,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 = m_alloc_clear( 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,12 +500,13 @@ 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 protol requires us to trasnmit utf-8 strings */ + /* The Assuan agent protocol requires us to transmit utf-8 strings */ orig_codeset = bind_textdomain_codeset (PACKAGE, NULL); #ifdef HAVE_LANGINFO_CODESET if (!orig_codeset) @@ -657,16 +514,18 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, #endif if (orig_codeset) { /* We only switch when we are able to restore the codeset later. */ - orig_codeset = m_strdup (orig_codeset); + orig_codeset = xstrdup (orig_codeset); 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; @@ -679,7 +538,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, #define KEYIDSTRING _(" (main key ID %s)") - maink = m_alloc ( strlen (KEYIDSTRING) + keystrlen() + 20 ); + maink = xmalloc ( strlen (KEYIDSTRING) + keystrlen() + 20 ); if( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) sprintf( maink, KEYIDSTRING, keystr(&keyid[2]) ); @@ -696,15 +555,15 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, "\"%.*s\"\n" \ "%u-bit %s key, ID %s, created %s%s\n" ) - atext = m_alloc ( 100 + strlen (PROMPTSTRING) + atext = xmalloc ( 100 + strlen (PROMPTSTRING) + uidlen + 15 + strlen(algo_name) + keystrlen() + strlen (timestr) + strlen (maink) ); sprintf (atext, PROMPTSTRING, - uidlen, uid, + (int)uidlen, uid, nbits_from_pk (pk), algo_name, keystr(&keyid[0]), timestr, maink ); - m_free (uid); - m_free (maink); + xfree (uid); + xfree (maink); #undef PROMPTSTRING @@ -716,169 +575,84 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, } else if (mode == 2 ) - atext = m_strdup ( _("Repeat passphrase\n") ); + atext = xstrdup ( _("Repeat passphrase\n") ); else - atext = m_strdup ( _("Enter passphrase\n") ); + 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; - 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; - } - 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; - } - pw[pwlen] = 0; /* make a C String */ - agent_close (fd); - free_public_key( pk ); -#ifdef ENABLE_NLS - if (orig_codeset) - bind_textdomain_codeset (PACKAGE, orig_codeset); -#endif - m_free (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 nenough space for escaping */ - line = m_alloc (15 + 46 - + 3*strlen (tryagain_text) + 3*strlen (atext) + 2); + /* We allocate 23 times the needed space for thye texts so that + there is enough space for escaping. */ + line = xmalloc (15 + 46 + + 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++ = 'X'; /* No caching. */ *p++ = ' '; - for (i=0, s=tryagain_text; *s; s++) + + p = percent_plus_escape (p, tryagain_text); + *p++ = ' '; + + /* 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); } + else + *p++ = 'X'; /* Use the standard prompt. */ *p++ = ' '; - *p++ = 'X'; /* Use the standard prompt */ - *p++ = ' '; - /* copy description */ - for (i=0, s= atext; *s; s++) + + /* 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); + xfree (atext); atext = NULL; + if (!rc) { - if (*s < ' ' || *s == '+') - { - sprintf (p, "%%%02X", *s); - p += 3; - } - else if (*s == ' ') - *p++ = '+'; - else - *p++ = *s; - } - *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 ); + 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, orig_codeset); #endif - m_free (orig_codeset); + 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; @@ -888,41 +662,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, orig_codeset); + { + bind_textdomain_codeset (PACKAGE, orig_codeset); + xfree (orig_codeset); + } #endif - m_free (atext); - if ( fd != -1 ) - agent_close (fd); - m_free (pw ); - free_public_key( pk ); - + xfree (atext); + agent_close (ctx); + xfree (pw ); + 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 @@ -931,71 +704,50 @@ passphrase_clear_cache ( u32 *keyid, int algo ) if (!opt.use_agent) return; - pk = m_alloc_clear ( 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 = 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 + 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 (rc) { log_error (_("problem with the agent - disabling agent use\n")); opt.use_agent = 0; @@ -1003,22 +755,22 @@ 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*/ } - - /**************** * Ask for a passphrase and return that string. */ char * ask_passphrase (const char *description, + const char *tryagain_text, const char *promptid, - const char *prompt, int *canceled) + const char *prompt, + const char *cacheid, int *canceled) { char *pw = NULL; @@ -1026,12 +778,23 @@ ask_passphrase (const char *description, *canceled = 0; if (!opt.batch && description) - tty_printf ("\n%s\n",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, description, canceled ); + pw = agent_get_passphrase (NULL, 0, cacheid, + tryagain_text, description, prompt, + canceled ); if (!pw) { if (!opt.use_agent) @@ -1041,15 +804,17 @@ ask_passphrase (const char *description, } else if (fd_passwd) { - pw = m_alloc_secure (strlen(fd_passwd)+1); + pw = xmalloc_secure (strlen(fd_passwd)+1); strcpy (pw, fd_passwd); } else if (opt.batch) { - log_error(_("can't query password in batchmode\n")); + 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(); @@ -1109,7 +874,7 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, us = get_long_user_id_string( keyid ); write_status_text( STATUS_USERID_HINT, us ); - m_free(us); + xfree(us); sprintf( buf, "%08lX%08lX %08lX%08lX %d 0", (ulong)keyid[0], (ulong)keyid[1], @@ -1129,14 +894,14 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, 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 = m_alloc_clear( sizeof *pk ); + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); char *p; 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); - m_free(p); + xfree(p); if( !get_pubkey( pk, keyid ) ) { const char *s = pubkey_algo_to_string( pk->pubkey_algo ); @@ -1159,54 +924,57 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, } tty_printf("\n"); - free_public_key( pk ); + if (pk) + free_public_key( pk ); } agent_died: if( next_pw ) { - /* Simply return the passpharse we already have in NEXT_PW. */ + /* Simply return the passphrase we already have in NEXT_PW. */ pw = next_pw; next_pw = NULL; } else if ( opt.use_agent ) { - /* Divert to teh gpg-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) goto agent_died; - pw = m_strdup (""); + 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) { - m_free (pw); + xfree (pw); pw = NULL; goto agent_died; } - pw2 = m_strdup (""); + pw2 = xstrdup (""); } if( strcmp(pw, pw2) ) { - m_free(pw2); - m_free(pw); + xfree(pw2); + xfree(pw); return NULL; } - m_free(pw2); + xfree(pw2); } } else if( fd_passwd ) { /* Return the passphrase we have store in FD_PASSWD. */ - pw = m_alloc_secure( strlen(fd_passwd)+1 ); + 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 = m_strdup( "" ); /* 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: ") ); @@ -1216,11 +984,11 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, _("Repeat passphrase: ") ); tty_kill_prompt(); if( strcmp(pw, pw2) ) { - m_free(pw2); - m_free(pw); + xfree(pw2); + xfree(pw); return NULL; } - m_free(pw2); + xfree(pw2); } } @@ -1230,13 +998,13 @@ passphrase_to_dek( u32 *keyid, int pubkey_algo, /* 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 = m_alloc_secure_clear ( sizeof *dek ); + 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 ); - m_free(last_pw); + xfree(last_pw); last_pw = pw; return dek; }