LCOV - code coverage report
Current view: top level - common - simple-pwquery.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 79 180 43.9 %
Date: 2016-11-29 15:00:56 Functions: 5 9 55.6 %

          Line data    Source code
       1             : /* simple-pwquery.c - A simple password query client for gpg-agent
       2             :  *      Copyright (C) 2002, 2004, 2007 Free Software Foundation, Inc.
       3             :  *
       4             :  * This file is part of GnuPG.
       5             :  *
       6             :  * GnuPG is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * GnuPG is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : /* This module is intended as a simple client implementation to
      21             :    gpg-agent's GET_PASSPHRASE command.  It can only cope with an
      22             :    already running gpg-agent.  Some stuff is configurable in the
      23             :    header file. */
      24             : 
      25             : #ifdef HAVE_CONFIG_H
      26             : #include <config.h>
      27             : #endif
      28             : #include <stdlib.h>
      29             : #include <stddef.h>
      30             : #include <string.h>
      31             : #include <errno.h>
      32             : #include <unistd.h>
      33             : #include <assuan.h>
      34             : #ifdef HAVE_W32_SYSTEM
      35             : #include <winsock2.h>
      36             : #else
      37             : #include <sys/socket.h>
      38             : #include <sys/un.h>
      39             : #endif
      40             : #ifdef HAVE_LOCALE_H
      41             : #include <locale.h>
      42             : #endif
      43             : 
      44             : #define GNUPG_COMMON_NEED_AFLOCAL
      45             : #include "../common/mischelp.h"
      46             : #include "sysutils.h"
      47             : #include "membuf.h"
      48             : 
      49             : 
      50             : #define SIMPLE_PWQUERY_IMPLEMENTATION 1
      51             : #include "simple-pwquery.h"
      52             : 
      53             : #define SPWQ_OUT_OF_CORE        gpg_error_from_errno (ENOMEM)
      54             : #define SPWQ_IO_ERROR           gpg_error_from_errno (EIO)
      55             : #define SPWQ_PROTOCOL_ERROR     gpg_error (GPG_ERR_PROTOCOL_VIOLATION)
      56             : #define SPWQ_ERR_RESPONSE       gpg_error (GPG_ERR_INV_RESPONSE)
      57             : #define SPWQ_NO_AGENT           gpg_error (GPG_ERR_NO_AGENT)
      58             : #define SPWQ_SYS_ERROR          gpg_error_from_syserror ()
      59             : #define SPWQ_GENERAL_ERROR      gpg_error (GPG_ERR_GENERAL)
      60             : #define SPWQ_NO_PIN_ENTRY       gpg_error (GPG_ERR_NO_PIN_ENTRY)
      61             : 
      62             : #ifndef _
      63             : #define _(a) (a)
      64             : #endif
      65             : 
      66             : #if !defined (hexdigitp) && !defined (xtoi_2)
      67             : #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
      68             : #define hexdigitp(a) (digitp (a)                     \
      69             :                       || (*(a) >= 'A' && *(a) <= 'F')  \
      70             :                       || (*(a) >= 'a' && *(a) <= 'f'))
      71             : #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
      72             :                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
      73             : #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
      74             : #endif
      75             : 
      76             : 
      77             : /* Name of the socket to be used.  This is a kludge to keep on using
      78             :    the existsing code despite that we only support a standard socket.  */
      79             : static char *default_gpg_agent_info;
      80             : 
      81             : 
      82             : 
      83             : 
      84             : 
      85             : #ifndef HAVE_STPCPY
      86             : static char *
      87             : my_stpcpy(char *a,const char *b)
      88             : {
      89             :     while( *b )
      90             :         *a++ = *b++;
      91             :     *a = 0;
      92             : 
      93             :     return (char*)a;
      94             : }
      95             : #define stpcpy(a,b)  my_stpcpy((a), (b))
      96             : #endif
      97             : 
      98             : 
      99             : /* Send an option to the agent */
     100             : static int
     101         135 : agent_send_option (assuan_context_t ctx, const char *name, const char *value)
     102             : {
     103             :   int err;
     104             :   char *line;
     105             : 
     106         135 :   line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
     107         135 :   if (!line)
     108           0 :     return SPWQ_OUT_OF_CORE;
     109         135 :   strcpy (stpcpy (stpcpy (stpcpy (
     110         135 :                      stpcpy (line, "OPTION "), name), "="), value), "\n");
     111             : 
     112         135 :   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
     113             : 
     114         135 :   spwq_free (line);
     115         135 :   return err;
     116             : }
     117             : 
     118             : 
     119             : /* Send all available options to the agent. */
     120             : static int
     121         135 : agent_send_all_options (assuan_context_t ctx)
     122             : {
     123         135 :   char *dft_display = NULL;
     124         135 :   char *dft_ttyname = NULL;
     125         135 :   char *dft_ttytype = NULL;
     126         135 :   char *dft_xauthority = NULL;
     127         135 :   char *dft_pinentry_user_data = NULL;
     128         135 :   int rc = 0;
     129             : 
     130         135 :   dft_display = getenv ("DISPLAY");
     131         135 :   if (dft_display)
     132             :     {
     133         135 :       if ((rc = agent_send_option (ctx, "display", dft_display)))
     134           0 :         return rc;
     135             :     }
     136             : 
     137         135 :   dft_ttyname = getenv ("GPG_TTY");
     138             : #if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME)
     139         135 :   if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
     140           0 :     dft_ttyname = ttyname (0);
     141             : #endif
     142         135 :   if (dft_ttyname && *dft_ttyname)
     143             :     {
     144           0 :       if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname)))
     145           0 :         return rc;
     146             :     }
     147             : 
     148         135 :   dft_ttytype = getenv ("TERM");
     149         135 :   if (dft_ttyname && dft_ttytype)
     150             :     {
     151           0 :       if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype)))
     152           0 :         return rc;
     153             :     }
     154             : 
     155             : #if defined(HAVE_SETLOCALE)
     156             :   {
     157         135 :     char *old_lc = NULL;
     158         135 :     char *dft_lc = NULL;
     159             : 
     160             : #if defined(LC_CTYPE)
     161         135 :     old_lc = setlocale (LC_CTYPE, NULL);
     162         135 :     if (old_lc)
     163             :       {
     164         135 :         char *p = spwq_malloc (strlen (old_lc)+1);
     165         135 :         if (!p)
     166           0 :           return SPWQ_OUT_OF_CORE;
     167         135 :         strcpy (p, old_lc);
     168         135 :         old_lc = p;
     169             :       }
     170         135 :     dft_lc = setlocale (LC_CTYPE, "");
     171         135 :     if (dft_ttyname && dft_lc)
     172           0 :       rc = agent_send_option (ctx, "lc-ctype", dft_lc);
     173         135 :     if (old_lc)
     174             :       {
     175         135 :         setlocale (LC_CTYPE, old_lc);
     176         135 :         spwq_free (old_lc);
     177             :       }
     178         135 :     if (rc)
     179           0 :       return rc;
     180             : #endif
     181             : 
     182             : #if defined(LC_MESSAGES)
     183         135 :     old_lc = setlocale (LC_MESSAGES, NULL);
     184         135 :     if (old_lc)
     185             :       {
     186         135 :         char *p = spwq_malloc (strlen (old_lc)+1);
     187         135 :         if (!p)
     188           0 :           return SPWQ_OUT_OF_CORE;
     189         135 :         strcpy (p, old_lc);
     190         135 :         old_lc = p;
     191             :       }
     192         135 :     dft_lc = setlocale (LC_MESSAGES, "");
     193         135 :     if (dft_ttyname && dft_lc)
     194           0 :       rc = agent_send_option (ctx, "lc-messages", dft_lc);
     195         135 :     if (old_lc)
     196             :       {
     197         135 :         setlocale (LC_MESSAGES, old_lc);
     198         135 :         spwq_free (old_lc);
     199             :       }
     200         135 :     if (rc)
     201           0 :       return rc;
     202             : #endif
     203             :   }
     204             : #endif /*HAVE_SETLOCALE*/
     205             : 
     206             :   /* Send the XAUTHORITY variable.  */
     207         135 :   dft_xauthority = getenv ("XAUTHORITY");
     208         135 :   if (dft_xauthority)
     209             :     {
     210             :       /* We ignore errors here because older gpg-agents don't support
     211             :          this option.  */
     212           0 :       agent_send_option (ctx, "xauthority", dft_xauthority);
     213             :     }
     214             : 
     215             :   /* Send the PINENTRY_USER_DATA variable.  */
     216         135 :   dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
     217         135 :   if (dft_pinentry_user_data)
     218             :     {
     219             :       /* We ignore errors here because older gpg-agents don't support
     220             :          this option.  */
     221           0 :       agent_send_option (ctx, "pinentry-user-data", dft_pinentry_user_data);
     222             :     }
     223             : 
     224             :   /* Tell the agent that we support Pinentry notifications.  No
     225             :      error checking so that it will work with older agents.  */
     226         135 :   assuan_transact (ctx, "OPTION allow-pinentry-notify",
     227             :                    NULL, NULL, NULL, NULL, NULL, NULL);
     228             : 
     229         135 :   return 0;
     230             : }
     231             : 
     232             : 
     233             : 
     234             : /* Try to open a connection to the agent, send all options and return
     235             :    the file descriptor for the connection.  Return -1 in case of
     236             :    error. */
     237             : static int
     238         135 : agent_open (assuan_context_t *ctx)
     239             : {
     240             :   int rc;
     241             :   char *infostr;
     242             : 
     243         135 :   infostr = default_gpg_agent_info;
     244         135 :   if ( !infostr || !*infostr )
     245             :     {
     246             : #ifdef SPWQ_USE_LOGGING
     247           0 :       log_error (_("no gpg-agent running in this session\n"));
     248             : #endif
     249           0 :       return SPWQ_NO_AGENT;
     250             :     }
     251             : 
     252         135 :   rc = assuan_new (ctx);
     253         135 :   if (rc)
     254           0 :     return rc;
     255             : 
     256         135 :   rc = assuan_socket_connect (*ctx, infostr, 0, 0);
     257         135 :   if (rc)
     258             :     {
     259             : #ifdef SPWQ_USE_LOGGING
     260           0 :       log_error (_("can't connect to '%s': %s\n"),
     261             :                  infostr, gpg_strerror (rc));
     262             : #endif
     263           0 :       goto errout;
     264             :     }
     265             : 
     266         135 :   rc = agent_send_all_options (*ctx);
     267         135 :   if (rc)
     268             :     {
     269             : #ifdef SPWQ_USE_LOGGING
     270           0 :       log_error (_("problem setting the gpg-agent options\n"));
     271             : #endif
     272           0 :       goto errout;
     273             :     }
     274             : 
     275         135 :   return 0;
     276             : 
     277             :  errout:
     278           0 :   assuan_release (*ctx);
     279           0 :   *ctx = NULL;
     280           0 :   return rc;
     281             : }
     282             : 
     283             : 
     284             : /* Copy text to BUFFER and escape as required.  Return a pointer to
     285             :    the end of the new buffer.  Note that BUFFER must be large enough
     286             :    to keep the entire text; allocataing it 3 times the size of TEXT
     287             :    is sufficient. */
     288             : static char *
     289           0 : copy_and_escape (char *buffer, const char *text)
     290             : {
     291             :   int i;
     292           0 :   const unsigned char *s = (unsigned char *)text;
     293           0 :   char *p = buffer;
     294             : 
     295             : 
     296           0 :   for (i=0; s[i]; i++)
     297             :     {
     298           0 :       if (s[i] < ' ' || s[i] == '+')
     299             :         {
     300           0 :           sprintf (p, "%%%02X", s[i]);
     301           0 :           p += 3;
     302             :         }
     303           0 :       else if (s[i] == ' ')
     304           0 :         *p++ = '+';
     305             :       else
     306           0 :         *p++ = s[i];
     307             :     }
     308           0 :   return p;
     309             : }
     310             : 
     311             : 
     312             : /* Set the name of the default socket to NAME.  */
     313             : int
     314         135 : simple_pw_set_socket (const char *name)
     315             : {
     316         135 :   spwq_free (default_gpg_agent_info);
     317         135 :   default_gpg_agent_info = NULL;
     318         135 :   if (name)
     319             :     {
     320         135 :       default_gpg_agent_info = spwq_malloc (strlen (name) + 1);
     321         135 :       if (!default_gpg_agent_info)
     322           0 :         return SPWQ_OUT_OF_CORE;
     323         135 :       strcpy (default_gpg_agent_info, name);
     324             :     }
     325             : 
     326         135 :   return 0;
     327             : }
     328             : 
     329             : 
     330             : /* This is the default inquiry callback.  It merely handles the
     331             :    Pinentry notification.  */
     332             : static gpg_error_t
     333           0 : default_inq_cb (void *opaque, const char *line)
     334             : {
     335             :   (void)opaque;
     336             : 
     337           0 :   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     338             :     {
     339           0 :       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
     340             :       /* We do not return errors to avoid breaking other code.  */
     341             :     }
     342             :   else
     343             :     {
     344             : #ifdef SPWQ_USE_LOGGING
     345           0 :       log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
     346             : #endif
     347             :     }
     348             : 
     349           0 :   return 0;
     350             : }
     351             : 
     352             : 
     353             : /* Ask the gpg-agent for a passphrase and present the user with a
     354             :    DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
     355             :    If a CACHEID is not NULL it is used to locate the passphrase in in
     356             :    the cache and store it under this ID.  If OPT_CHECK is true
     357             :    gpg-agent is asked to apply some checks on the passphrase security.
     358             :    If ERRORCODE is not NULL it should point a variable receiving an
     359             :    errorcode; this error code might be 0 if the user canceled the
     360             :    operation.  The function returns NULL to indicate an error.  */
     361             : char *
     362           0 : simple_pwquery (const char *cacheid,
     363             :                 const char *tryagain,
     364             :                 const char *prompt,
     365             :                 const char *description,
     366             :                 int opt_check,
     367             :                 int *errorcode)
     368             : {
     369             :   int rc;
     370             :   assuan_context_t ctx;
     371             :   membuf_t data;
     372           0 :   char *result = NULL;
     373           0 :   char *pw = NULL;
     374             :   char *p;
     375             :   size_t n;
     376             : 
     377             : 
     378           0 :   rc = agent_open (&ctx);
     379           0 :   if (rc)
     380           0 :     goto leave;
     381             : 
     382           0 :   if (!cacheid)
     383           0 :     cacheid = "X";
     384           0 :   if (!tryagain)
     385           0 :     tryagain = "X";
     386           0 :   if (!prompt)
     387           0 :     prompt = "X";
     388           0 :   if (!description)
     389           0 :     description = "X";
     390             : 
     391             :   {
     392             :     char *line;
     393             :     /* We allocate 3 times the needed space so that there is enough
     394             :        space for escaping. */
     395           0 :     line = spwq_malloc (15 + 10
     396             :                         + 3*strlen (cacheid) + 1
     397             :                         + 3*strlen (tryagain) + 1
     398             :                         + 3*strlen (prompt) + 1
     399             :                         + 3*strlen (description) + 1
     400             :                         + 2);
     401           0 :     if (!line)
     402             :       {
     403           0 :         rc = SPWQ_OUT_OF_CORE;
     404           0 :         goto leave;
     405             :       }
     406           0 :     strcpy (line, "GET_PASSPHRASE ");
     407           0 :     p = line+15;
     408           0 :     if (opt_check)
     409           0 :       p = stpcpy (p, "--check ");
     410           0 :     p = copy_and_escape (p, cacheid);
     411           0 :     *p++ = ' ';
     412           0 :     p = copy_and_escape (p, tryagain);
     413           0 :     *p++ = ' ';
     414           0 :     p = copy_and_escape (p, prompt);
     415           0 :     *p++ = ' ';
     416           0 :     p = copy_and_escape (p, description);
     417           0 :     *p++ = '\n';
     418             : 
     419           0 :     init_membuf_secure (&data, 64);
     420             : 
     421           0 :     rc = assuan_transact (ctx, line, put_membuf_cb, &data,
     422             :                           default_inq_cb, NULL, NULL, NULL);
     423           0 :     spwq_free (line);
     424             : 
     425             :     /* Older Pinentries return the old assuan error code for canceled
     426             :        which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
     427             :        not to the code for a user cancel.  Fix this here. */
     428           0 :     if (rc && gpg_err_source (rc)
     429           0 :         && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
     430           0 :       rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
     431             : 
     432           0 :     if (rc)
     433             :       {
     434           0 :         p = get_membuf (&data, &n);
     435           0 :         if (p)
     436           0 :           wipememory (p, n);
     437           0 :         spwq_free (p);
     438             :       }
     439             :     else
     440             :       {
     441           0 :         put_membuf (&data, "", 1);
     442           0 :         result = get_membuf (&data, NULL);
     443           0 :         if (pw == NULL)
     444           0 :           rc = gpg_error_from_syserror ();
     445             :       }
     446             :   }
     447             : 
     448             :  leave:
     449           0 :   if (errorcode)
     450           0 :     *errorcode = rc;
     451           0 :   assuan_release (ctx);
     452           0 :   return result;
     453             : }
     454             : 
     455             : 
     456             : /* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID.  */
     457             : int
     458           0 : simple_pwclear (const char *cacheid)
     459             : {
     460             :   char line[500];
     461             :   char *p;
     462             : 
     463             :   /* We need not more than 50 characters for the command and the
     464             :      terminating nul.  */
     465           0 :   if (strlen (cacheid) * 3 > sizeof (line) - 50)
     466           0 :     return SPWQ_PROTOCOL_ERROR;
     467             : 
     468           0 :   strcpy (line, "CLEAR_PASSPHRASE ");
     469           0 :   p = line + 17;
     470           0 :   p = copy_and_escape (p, cacheid);
     471           0 :   *p++ = '\n';
     472           0 :   *p++ = '\0';
     473             : 
     474           0 :   return simple_query (line);
     475             : }
     476             : 
     477             : 
     478             : /* Perform the simple query QUERY (which must be new-line and 0
     479             :    terminated) and return the error code.  */
     480             : int
     481         135 : simple_query (const char *query)
     482             : {
     483             :   assuan_context_t ctx;
     484             :   int rc;
     485             : 
     486         135 :   rc = agent_open (&ctx);
     487         135 :   if (rc)
     488           0 :     return rc;
     489             : 
     490         135 :   rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL);
     491             : 
     492         135 :   assuan_release (ctx);
     493         135 :   return rc;
     494             : }

Generated by: LCOV version 1.11