"Fossies" - the Fresh Open Source Software Archive

Member "gnupg-2.2.17/common/simple-pwquery.c" (9 Jul 2019, 12347 Bytes) of package /linux/misc/gnupg-2.2.17.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "simple-pwquery.c" see the Fossies "Dox" file reference documentation.

    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 agent_send_option (assuan_context_t ctx, const char *name, const char *value)
  102 {
  103   int err;
  104   char *line;
  105 
  106   line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
  107   if (!line)
  108     return SPWQ_OUT_OF_CORE;
  109   strcpy (stpcpy (stpcpy (stpcpy (
  110                      stpcpy (line, "OPTION "), name), "="), value), "\n");
  111 
  112   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
  113 
  114   spwq_free (line);
  115   return err;
  116 }
  117 
  118 
  119 /* Send all available options to the agent. */
  120 static int
  121 agent_send_all_options (assuan_context_t ctx)
  122 {
  123   char *dft_display = NULL;
  124   char *dft_ttyname = NULL;
  125   char *dft_ttytype = NULL;
  126   char *dft_xauthority = NULL;
  127   char *dft_pinentry_user_data = NULL;
  128   int rc = 0;
  129 
  130   dft_display = getenv ("DISPLAY");
  131   if (dft_display)
  132     {
  133       if ((rc = agent_send_option (ctx, "display", dft_display)))
  134         return rc;
  135     }
  136 
  137   dft_ttyname = getenv ("GPG_TTY");
  138 #if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME)
  139   if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
  140     dft_ttyname = ttyname (0);
  141 #endif
  142   if (dft_ttyname && *dft_ttyname)
  143     {
  144       if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname)))
  145         return rc;
  146     }
  147 
  148   dft_ttytype = getenv ("TERM");
  149   if (dft_ttyname && dft_ttytype)
  150     {
  151       if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype)))
  152         return rc;
  153     }
  154 
  155 #if defined(HAVE_SETLOCALE)
  156   {
  157     char *old_lc = NULL;
  158     char *dft_lc = NULL;
  159 
  160 #if defined(LC_CTYPE)
  161     old_lc = setlocale (LC_CTYPE, NULL);
  162     if (old_lc)
  163       {
  164         char *p = spwq_malloc (strlen (old_lc)+1);
  165         if (!p)
  166           return SPWQ_OUT_OF_CORE;
  167         strcpy (p, old_lc);
  168         old_lc = p;
  169       }
  170     dft_lc = setlocale (LC_CTYPE, "");
  171     if (dft_ttyname && dft_lc)
  172       rc = agent_send_option (ctx, "lc-ctype", dft_lc);
  173     if (old_lc)
  174       {
  175         setlocale (LC_CTYPE, old_lc);
  176         spwq_free (old_lc);
  177       }
  178     if (rc)
  179       return rc;
  180 #endif
  181 
  182 #if defined(LC_MESSAGES)
  183     old_lc = setlocale (LC_MESSAGES, NULL);
  184     if (old_lc)
  185       {
  186         char *p = spwq_malloc (strlen (old_lc)+1);
  187         if (!p)
  188           return SPWQ_OUT_OF_CORE;
  189         strcpy (p, old_lc);
  190         old_lc = p;
  191       }
  192     dft_lc = setlocale (LC_MESSAGES, "");
  193     if (dft_ttyname && dft_lc)
  194       rc = agent_send_option (ctx, "lc-messages", dft_lc);
  195     if (old_lc)
  196       {
  197         setlocale (LC_MESSAGES, old_lc);
  198         spwq_free (old_lc);
  199       }
  200     if (rc)
  201       return rc;
  202 #endif
  203   }
  204 #endif /*HAVE_SETLOCALE*/
  205 
  206   /* Send the XAUTHORITY variable.  */
  207   dft_xauthority = getenv ("XAUTHORITY");
  208   if (dft_xauthority)
  209     {
  210       /* We ignore errors here because older gpg-agents don't support
  211          this option.  */
  212       agent_send_option (ctx, "xauthority", dft_xauthority);
  213     }
  214 
  215   /* Send the PINENTRY_USER_DATA variable.  */
  216   dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
  217   if (dft_pinentry_user_data)
  218     {
  219       /* We ignore errors here because older gpg-agents don't support
  220          this option.  */
  221       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   assuan_transact (ctx, "OPTION allow-pinentry-notify",
  227                    NULL, NULL, NULL, NULL, NULL, NULL);
  228 
  229   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 agent_open (assuan_context_t *ctx)
  239 {
  240   int rc;
  241   char *infostr;
  242 
  243   infostr = default_gpg_agent_info;
  244   if ( !infostr || !*infostr )
  245     {
  246 #ifdef SPWQ_USE_LOGGING
  247       log_error (_("no gpg-agent running in this session\n"));
  248 #endif
  249       *ctx = NULL;
  250       return SPWQ_NO_AGENT;
  251     }
  252 
  253   rc = assuan_new (ctx);
  254   if (rc)
  255     return rc;
  256 
  257   rc = assuan_socket_connect (*ctx, infostr, 0, 0);
  258   if (rc)
  259     {
  260 #ifdef SPWQ_USE_LOGGING
  261       log_error (_("can't connect to '%s': %s\n"),
  262                  infostr, gpg_strerror (rc));
  263 #endif
  264       goto errout;
  265     }
  266 
  267   rc = agent_send_all_options (*ctx);
  268   if (rc)
  269     {
  270 #ifdef SPWQ_USE_LOGGING
  271       log_error (_("problem setting the gpg-agent options\n"));
  272 #endif
  273       goto errout;
  274     }
  275 
  276   return 0;
  277 
  278  errout:
  279   assuan_release (*ctx);
  280   *ctx = NULL;
  281   return rc;
  282 }
  283 
  284 
  285 /* Copy text to BUFFER and escape as required.  Return a pointer to
  286    the end of the new buffer.  Note that BUFFER must be large enough
  287    to keep the entire text; allocataing it 3 times the size of TEXT
  288    is sufficient. */
  289 static char *
  290 copy_and_escape (char *buffer, const char *text)
  291 {
  292   int i;
  293   const unsigned char *s = (unsigned char *)text;
  294   char *p = buffer;
  295 
  296 
  297   for (i=0; s[i]; i++)
  298     {
  299       if (s[i] < ' ' || s[i] == '+')
  300         {
  301           sprintf (p, "%%%02X", s[i]);
  302           p += 3;
  303         }
  304       else if (s[i] == ' ')
  305         *p++ = '+';
  306       else
  307         *p++ = s[i];
  308     }
  309   return p;
  310 }
  311 
  312 
  313 /* Set the name of the default socket to NAME.  */
  314 int
  315 simple_pw_set_socket (const char *name)
  316 {
  317   spwq_free (default_gpg_agent_info);
  318   default_gpg_agent_info = NULL;
  319   if (name)
  320     {
  321       default_gpg_agent_info = spwq_malloc (strlen (name) + 1);
  322       if (!default_gpg_agent_info)
  323         return SPWQ_OUT_OF_CORE;
  324       strcpy (default_gpg_agent_info, name);
  325     }
  326 
  327   return 0;
  328 }
  329 
  330 
  331 /* This is the default inquiry callback.  It merely handles the
  332    Pinentry notification.  */
  333 static gpg_error_t
  334 default_inq_cb (void *opaque, const char *line)
  335 {
  336   (void)opaque;
  337 
  338   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
  339     {
  340       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
  341       /* We do not return errors to avoid breaking other code.  */
  342     }
  343   else
  344     {
  345 #ifdef SPWQ_USE_LOGGING
  346       log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
  347 #endif
  348     }
  349 
  350   return 0;
  351 }
  352 
  353 
  354 /* Ask the gpg-agent for a passphrase and present the user with a
  355    DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
  356    If a CACHEID is not NULL it is used to locate the passphrase in
  357    the cache and store it under this ID.  If OPT_CHECK is true
  358    gpg-agent is asked to apply some checks on the passphrase security.
  359    If ERRORCODE is not NULL it should point a variable receiving an
  360    errorcode; this error code might be 0 if the user canceled the
  361    operation.  The function returns NULL to indicate an error.  */
  362 char *
  363 simple_pwquery (const char *cacheid,
  364                 const char *tryagain,
  365                 const char *prompt,
  366                 const char *description,
  367                 int opt_check,
  368                 int *errorcode)
  369 {
  370   int rc;
  371   assuan_context_t ctx;
  372   membuf_t data;
  373   char *result = NULL;
  374   char *pw = NULL;
  375   char *p;
  376   size_t n;
  377 
  378 
  379   rc = agent_open (&ctx);
  380   if (rc)
  381     goto leave;
  382 
  383   if (!cacheid)
  384     cacheid = "X";
  385   if (!tryagain)
  386     tryagain = "X";
  387   if (!prompt)
  388     prompt = "X";
  389   if (!description)
  390     description = "X";
  391 
  392   {
  393     char *line;
  394     /* We allocate 3 times the needed space so that there is enough
  395        space for escaping. */
  396     line = spwq_malloc (15 + 10
  397                         + 3*strlen (cacheid) + 1
  398                         + 3*strlen (tryagain) + 1
  399                         + 3*strlen (prompt) + 1
  400                         + 3*strlen (description) + 1
  401                         + 2);
  402     if (!line)
  403       {
  404         rc = SPWQ_OUT_OF_CORE;
  405         goto leave;
  406       }
  407     strcpy (line, "GET_PASSPHRASE ");
  408     p = line+15;
  409     if (opt_check)
  410       p = stpcpy (p, "--check ");
  411     p = copy_and_escape (p, cacheid);
  412     *p++ = ' ';
  413     p = copy_and_escape (p, tryagain);
  414     *p++ = ' ';
  415     p = copy_and_escape (p, prompt);
  416     *p++ = ' ';
  417     p = copy_and_escape (p, description);
  418     *p++ = '\n';
  419 
  420     init_membuf_secure (&data, 64);
  421 
  422     rc = assuan_transact (ctx, line, put_membuf_cb, &data,
  423                           default_inq_cb, NULL, NULL, NULL);
  424     spwq_free (line);
  425 
  426     /* Older Pinentries return the old assuan error code for canceled
  427        which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
  428        not to the code for a user cancel.  Fix this here. */
  429     if (rc && gpg_err_source (rc)
  430         && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
  431       rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
  432 
  433     if (rc)
  434       {
  435         p = get_membuf (&data, &n);
  436         if (p)
  437           wipememory (p, n);
  438         spwq_free (p);
  439       }
  440     else
  441       {
  442         put_membuf (&data, "", 1);
  443         result = get_membuf (&data, NULL);
  444         if (pw == NULL)
  445           rc = gpg_error_from_syserror ();
  446       }
  447   }
  448 
  449  leave:
  450   if (errorcode)
  451     *errorcode = rc;
  452   assuan_release (ctx);
  453   return result;
  454 }
  455 
  456 
  457 /* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID.  */
  458 int
  459 simple_pwclear (const char *cacheid)
  460 {
  461   char line[500];
  462   char *p;
  463 
  464   /* We need not more than 50 characters for the command and the
  465      terminating nul.  */
  466   if (strlen (cacheid) * 3 > sizeof (line) - 50)
  467     return SPWQ_PROTOCOL_ERROR;
  468 
  469   strcpy (line, "CLEAR_PASSPHRASE ");
  470   p = line + 17;
  471   p = copy_and_escape (p, cacheid);
  472   *p++ = '\n';
  473   *p++ = '\0';
  474 
  475   return simple_query (line);
  476 }
  477 
  478 
  479 /* Perform the simple query QUERY (which must be new-line and 0
  480    terminated) and return the error code.  */
  481 int
  482 simple_query (const char *query)
  483 {
  484   assuan_context_t ctx;
  485   int rc;
  486 
  487   rc = agent_open (&ctx);
  488   if (rc)
  489     return rc;
  490 
  491   rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL);
  492 
  493   assuan_release (ctx);
  494   return rc;
  495 }