pinentry-dmenu

a pinentry program based on dmenu
git clone anongit@rnpnr.xyz:pinentry-dmenu.git
Log | Files | Refs | Feed | README | LICENSE

pinentry.c (31549B)


      1 /* pinentry.c - The PIN entry support library
      2    Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015 g10 Code GmbH
      3 
      4    This file is part of PINENTRY.
      5 
      6    PINENTRY is free software; you can redistribute it and/or modify it
      7    under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 2 of the License, or
      9    (at your option) any later version.
     10 
     11    PINENTRY is distributed in the hope that it will be useful, but
     12    WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14    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 <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 #include <assert.h>
     24 
     25 #include <assuan.h>
     26 
     27 #include "memory.h"
     28 #include "secmem-util.h"
     29 #include "argparse.h"
     30 #include "pinentry.h"
     31 #include "password-cache.h"
     32 
     33 /* Keep the name of our program here. */
     34 static char this_pgmname[50];
     35 
     36 struct pinentry pinentry;
     37 
     38 static void
     39 pinentry_reset (int use_defaults)
     40 {
     41   /* GPG Agent sets these options once when it starts the pinentry.
     42      Don't reset them.  */
     43   int grab = pinentry.grab;
     44   char *ttyname = pinentry.ttyname;
     45   char *ttytype = pinentry.ttytype;
     46   char *lc_ctype = pinentry.lc_ctype;
     47   char *lc_messages = pinentry.lc_messages;
     48   int allow_external_password_cache = pinentry.allow_external_password_cache;
     49   char *default_ok = pinentry.default_ok;
     50   char *default_cancel = pinentry.default_cancel;
     51   char *default_prompt = pinentry.default_prompt;
     52   char *default_pwmngr = pinentry.default_pwmngr;
     53   char *touch_file = pinentry.touch_file;
     54 
     55   /* These options are set from the command line.  Don't reset
     56      them.  */
     57   int debug = pinentry.debug;
     58   char *display = pinentry.display;
     59   int parent_wid = pinentry.parent_wid;
     60 
     61   pinentry_color_t color_fg = pinentry.color_fg;
     62   int color_fg_bright = pinentry.color_fg_bright;
     63   pinentry_color_t color_bg = pinentry.color_bg;
     64   pinentry_color_t color_so = pinentry.color_so;
     65   int color_so_bright = pinentry.color_so_bright;
     66 
     67   int timout = pinentry.timeout;
     68 
     69   /* Free any allocated memory.  */
     70   if (use_defaults)
     71     {
     72       free (pinentry.ttyname);
     73       free (pinentry.ttytype);
     74       free (pinentry.lc_ctype);
     75       free (pinentry.lc_messages);
     76       free (pinentry.default_ok);
     77       free (pinentry.default_cancel);
     78       free (pinentry.default_prompt);
     79       free (pinentry.default_pwmngr);
     80       free (pinentry.touch_file);
     81       free (pinentry.display);
     82     }
     83 
     84   free (pinentry.title);
     85   free (pinentry.description);
     86   free (pinentry.error);
     87   free (pinentry.prompt);
     88   free (pinentry.ok);
     89   free (pinentry.notok);
     90   free (pinentry.cancel);
     91   secmem_free (pinentry.pin);
     92   free (pinentry.repeat_passphrase);
     93   free (pinentry.repeat_error_string);
     94   free (pinentry.quality_bar);
     95   free (pinentry.quality_bar_tt);
     96   free (pinentry.keyinfo);
     97 
     98   /* Reset the pinentry structure.  */
     99   memset (&pinentry, 0, sizeof (pinentry));
    100 
    101   if (use_defaults)
    102     {
    103       /* Pinentry timeout in seconds.  */
    104       pinentry.timeout = 60;
    105 
    106       /* Global grab.  */
    107       pinentry.grab = 1;
    108 
    109       pinentry.color_fg = PINENTRY_COLOR_DEFAULT;
    110       pinentry.color_fg_bright = 0;
    111       pinentry.color_bg = PINENTRY_COLOR_DEFAULT;
    112       pinentry.color_so = PINENTRY_COLOR_DEFAULT;
    113       pinentry.color_so_bright = 0;
    114     }
    115   else
    116     /* Restore the options.  */
    117     {
    118       pinentry.grab = grab;
    119       pinentry.ttyname = ttyname;
    120       pinentry.ttytype = ttytype;
    121       pinentry.lc_ctype = lc_ctype;
    122       pinentry.lc_messages = lc_messages;
    123       pinentry.allow_external_password_cache = allow_external_password_cache;
    124       pinentry.default_ok = default_ok;
    125       pinentry.default_cancel = default_cancel;
    126       pinentry.default_prompt = default_prompt;
    127       pinentry.default_pwmngr = default_pwmngr;
    128       pinentry.touch_file = touch_file;
    129 
    130       pinentry.debug = debug;
    131       pinentry.display = display;
    132       pinentry.parent_wid = parent_wid;
    133 
    134       pinentry.color_fg = color_fg;
    135       pinentry.color_fg_bright = color_fg_bright;
    136       pinentry.color_bg = color_bg;
    137       pinentry.color_so = color_so;
    138       pinentry.color_so_bright = color_so_bright;
    139 
    140       pinentry.timeout = timout;
    141     }
    142 }
    143 
    144 static gpg_error_t
    145 pinentry_assuan_reset_handler (assuan_context_t ctx, char *line)
    146 {
    147   (void)ctx;
    148   (void)line;
    149 
    150   pinentry_reset (0);
    151 
    152   return 0;
    153 }
    154 
    155 
    156 
    157 static int lc_ctype_unknown_warning = 0;
    158 
    159 /* Copy TEXT or TEXTLEN to BUFFER and escape as required.  Return a
    160    pointer to the end of the new buffer.  Note that BUFFER must be
    161    large enough to keep the entire text; allocataing it 3 times of
    162    TEXTLEN is sufficient.  */
    163 static char *
    164 copy_and_escape (char *buffer, const void *text, size_t textlen)
    165 {
    166   int i;
    167   const unsigned char *s = (unsigned char *)text;
    168   char *p = buffer;
    169 
    170   for (i=0; i < textlen; i++)
    171     {
    172       if (s[i] < ' ' || s[i] == '+')
    173         {
    174           snprintf (p, 4, "%%%02X", s[i]);
    175           p += 3;
    176         }
    177       else if (s[i] == ' ')
    178         *p++ = '+';
    179       else
    180         *p++ = s[i];
    181     }
    182   return p;
    183 }
    184 
    185 
    186 
    187 /* Run a quality inquiry for PASSPHRASE of LENGTH.  (We need LENGTH
    188    because not all backends might be able to return a proper
    189    C-string.).  Returns: A value between -100 and 100 to give an
    190    estimate of the passphrase's quality.  Negative values are use if
    191    the caller won't even accept that passphrase.  Note that we expect
    192    just one data line which should not be escaped in any represent a
    193    numeric signed decimal value.  Extra data is currently ignored but
    194    should not be send at all.  */
    195 int
    196 pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
    197 {
    198   assuan_context_t ctx = pin->ctx_assuan;
    199   const char prefix[] = "INQUIRE QUALITY ";
    200   char *command;
    201   char *line;
    202   size_t linelen;
    203   int gotvalue = 0;
    204   int value = 0;
    205   int rc;
    206 
    207   if (!ctx)
    208     return 0; /* Can't run the callback.  */
    209 
    210   if (length > 300)
    211     length = 300;  /* Limit so that it definitely fits into an Assuan
    212                       line.  */
    213 
    214   command = secmem_malloc (strlen (prefix) + 3*length + 1);
    215   if (!command)
    216     return 0;
    217   strcpy (command, prefix);
    218   copy_and_escape (command + strlen(command), passphrase, length);
    219   rc = assuan_write_line (ctx, command);
    220   secmem_free (command);
    221   if (rc)
    222     {
    223       fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
    224       return 0;
    225     }
    226 
    227   for (;;)
    228     {
    229       do
    230         {
    231           rc = assuan_read_line (ctx, &line, &linelen);
    232           if (rc)
    233             {
    234               fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
    235               return 0;
    236             }
    237         }
    238       while (*line == '#' || !linelen);
    239       if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
    240           && (!line[3] || line[3] == ' '))
    241         break; /* END command received*/
    242       if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
    243           && (!line[3] || line[3] == ' '))
    244         break; /* CAN command received*/
    245       if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
    246           && (!line[3] || line[3] == ' '))
    247         break; /* ERR command received*/
    248       if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
    249         continue;
    250       gotvalue = 1;
    251       value = atoi (line+2);
    252     }
    253   if (value < -100)
    254     value = -100;
    255   else if (value > 100)
    256     value = 100;
    257 
    258   return value;
    259 }
    260 
    261 
    262 
    263 /* Try to make room for at least LEN bytes in the pinentry.  Returns
    264    new buffer on success and 0 on failure or when the old buffer is
    265    sufficient.  */
    266 char *
    267 pinentry_setbufferlen (pinentry_t pin, int len)
    268 {
    269   char *newp;
    270 
    271   if (pin->pin_len)
    272     assert (pin->pin);
    273   else
    274     assert (!pin->pin);
    275 
    276   if (len < 2048)
    277     len = 2048;
    278 
    279   if (len <= pin->pin_len)
    280     return pin->pin;
    281 
    282   newp = secmem_realloc (pin->pin, len);
    283   if (newp)
    284     {
    285       pin->pin = newp;
    286       pin->pin_len = len;
    287     }
    288   else
    289     {
    290       secmem_free (pin->pin);
    291       pin->pin = 0;
    292       pin->pin_len = 0;
    293     }
    294   return newp;
    295 }
    296 
    297 static void
    298 pinentry_setbuffer_clear (pinentry_t pin)
    299 {
    300   if (! pin->pin)
    301     {
    302       assert (pin->pin_len == 0);
    303       return;
    304     }
    305 
    306   assert (pin->pin_len > 0);
    307 
    308   secmem_free (pin->pin);
    309   pin->pin = NULL;
    310   pin->pin_len = 0;
    311 }
    312 
    313 static void
    314 pinentry_setbuffer_init (pinentry_t pin)
    315 {
    316   pinentry_setbuffer_clear (pin);
    317   pinentry_setbufferlen (pin, 0);
    318 }
    319 
    320 /* passphrase better be alloced with secmem_alloc.  */
    321 void
    322 pinentry_setbuffer_use (pinentry_t pin, char *passphrase, int len)
    323 {
    324   if (! passphrase)
    325     {
    326       assert (len == 0);
    327       pinentry_setbuffer_clear (pin);
    328 
    329       return;
    330     }
    331 
    332   if (passphrase && len == 0)
    333     len = strlen (passphrase) + 1;
    334 
    335   if (pin->pin)
    336     secmem_free (pin->pin);
    337 
    338   pin->pin = passphrase;
    339   pin->pin_len = len;
    340 }
    341 
    342 static struct assuan_malloc_hooks assuan_malloc_hooks = {
    343   secmem_malloc, secmem_realloc, secmem_free
    344 };
    345 
    346 /* Initialize the secure memory subsystem, drop privileges and return.
    347    Must be called early. */
    348 void
    349 pinentry_init (const char *pgmname)
    350 {
    351   /* Store away our name. */
    352   if (strlen (pgmname) > sizeof this_pgmname - 2)
    353     abort ();
    354   strcpy (this_pgmname, pgmname);
    355 
    356   gpgrt_check_version (NULL);
    357 
    358   /* Initialize secure memory.  1 is too small, so the default size
    359      will be used.  */
    360   secmem_init (1);
    361   secmem_set_flags (SECMEM_WARN);
    362   drop_privs ();
    363 
    364   if (atexit (secmem_term))
    365     {
    366       /* FIXME: Could not register at-exit function, bail out.  */
    367     }
    368 
    369   assuan_set_malloc_hooks (&assuan_malloc_hooks);
    370 }
    371 
    372 /* Simple test to check whether DISPLAY is set or the option --display
    373    was given.  Used to decide whether the GUI or curses should be
    374    initialized.  */
    375 int
    376 pinentry_have_display (int argc, char **argv)
    377 {
    378   for (; argc; argc--, argv++)
    379     if (!strcmp (*argv, "--display") || !strncmp (*argv, "--display=", 10))
    380       return 1;
    381   return 0;
    382 }
    383 
    384 
    385 
    386 /* Print usage information and and provide strings for help. */
    387 static const char *
    388 my_strusage( int level )
    389 {
    390   const char *p;
    391 
    392   switch (level)
    393     {
    394     case 11: p = this_pgmname; break;
    395     case 12: p = "pinentry"; break;
    396     case 13: p = PACKAGE_VERSION; break;
    397     case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
    398     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
    399     case 1:
    400     case 40:
    401       {
    402         static char *str;
    403 
    404         if (!str)
    405           {
    406             size_t n = 50 + strlen (this_pgmname);
    407             str = malloc (n);
    408             if (str)
    409               snprintf (str, n, "Usage: %s [options] (-h for help)",
    410                         this_pgmname);
    411           }
    412         p = str;
    413       }
    414       break;
    415     case 41:
    416       p = "Ask securely for a secret and print it to stdout.";
    417       break;
    418 
    419     case 42:
    420       p = "1"; /* Flag print 40 as part of 41. */
    421       break;
    422 
    423     default: p = NULL; break;
    424     }
    425   return p;
    426 }
    427 
    428 
    429 char *
    430 parse_color (char *arg, pinentry_color_t *color_p, int *bright_p)
    431 {
    432   static struct
    433   {
    434     const char *name;
    435     pinentry_color_t color;
    436   } colors[] = { { "none", PINENTRY_COLOR_NONE },
    437 		 { "default", PINENTRY_COLOR_DEFAULT },
    438 		 { "black", PINENTRY_COLOR_BLACK },
    439 		 { "red", PINENTRY_COLOR_RED },
    440 		 { "green", PINENTRY_COLOR_GREEN },
    441 		 { "yellow", PINENTRY_COLOR_YELLOW },
    442 		 { "blue", PINENTRY_COLOR_BLUE },
    443 		 { "magenta", PINENTRY_COLOR_MAGENTA },
    444 		 { "cyan", PINENTRY_COLOR_CYAN },
    445 		 { "white", PINENTRY_COLOR_WHITE } };
    446 
    447   int i;
    448   char *new_arg;
    449   pinentry_color_t color = PINENTRY_COLOR_DEFAULT;
    450 
    451   if (!arg)
    452     return NULL;
    453 
    454   new_arg = strchr (arg, ',');
    455   if (new_arg)
    456     new_arg++;
    457 
    458   if (bright_p)
    459     {
    460       const char *bname[] = { "bright-", "bright", "bold-", "bold" };
    461 
    462       *bright_p = 0;
    463       for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++)
    464 	if (!strncasecmp (arg, bname[i], strlen (bname[i])))
    465 	  {
    466 	    *bright_p = 1;
    467 	    arg += strlen (bname[i]);
    468 	  }
    469     }
    470 
    471   for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++)
    472     if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name)))
    473       color = colors[i].color;
    474 
    475   *color_p = color;
    476   return new_arg;
    477 }
    478 
    479 /* Parse the command line options.  May exit the program if only help
    480    or version output is requested.  */
    481 void
    482 pinentry_parse_opts (int argc, char *argv[])
    483 {
    484   static ARGPARSE_OPTS opts[] = {
    485     ARGPARSE_s_n('d', "debug",    "Turn on debugging output"),
    486     ARGPARSE_s_s('D', "display",  "|DISPLAY|Set the X display"),
    487     ARGPARSE_s_s('T', "ttyname",  "|FILE|Set the tty terminal node name"),
    488     ARGPARSE_s_s('N', "ttytype",  "|NAME|Set the tty terminal type"),
    489     ARGPARSE_s_s('C', "lc-ctype", "|STRING|Set the tty LC_CTYPE value"),
    490     ARGPARSE_s_s('M', "lc-messages", "|STRING|Set the tty LC_MESSAGES value"),
    491     ARGPARSE_s_i('o', "timeout",
    492                  "|SECS|Timeout waiting for input after this many seconds"),
    493     ARGPARSE_s_n('g', "no-global-grab",
    494                  "Grab keyboard only while window is focused"),
    495     ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"),
    496     ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"),
    497     ARGPARSE_end()
    498   };
    499   ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
    500 
    501   set_strusage (my_strusage);
    502 
    503   pinentry_reset (1);
    504 
    505   while (arg_parse  (&pargs, opts))
    506     {
    507       switch (pargs.r_opt)
    508         {
    509         case 'd':
    510           pinentry.debug = 1;
    511           break;
    512         case 'g':
    513           pinentry.grab = 0;
    514           break;
    515 
    516 	case 'D':
    517           /* Note, this is currently not used because the GUI engine
    518              has already been initialized when parsing these options. */
    519 	  pinentry.display = strdup (pargs.r.ret_str);
    520 	  if (!pinentry.display)
    521 	    {
    522 	      exit (EXIT_FAILURE);
    523 	    }
    524 	  break;
    525 	case 'T':
    526 	  pinentry.ttyname = strdup (pargs.r.ret_str);
    527 	  if (!pinentry.ttyname)
    528 	    {
    529 	      exit (EXIT_FAILURE);
    530 	    }
    531 	  break;
    532 	case 'N':
    533 	  pinentry.ttytype = strdup (pargs.r.ret_str);
    534 	  if (!pinentry.ttytype)
    535 	    {
    536 	      exit (EXIT_FAILURE);
    537 	    }
    538 	  break;
    539 	case 'C':
    540 	  pinentry.lc_ctype = strdup (pargs.r.ret_str);
    541 	  if (!pinentry.lc_ctype)
    542 	    {
    543 	      exit (EXIT_FAILURE);
    544 	    }
    545 	  break;
    546 	case 'M':
    547 	  pinentry.lc_messages = strdup (pargs.r.ret_str);
    548 	  if (!pinentry.lc_messages)
    549 	    {
    550 	      exit (EXIT_FAILURE);
    551 	    }
    552 	  break;
    553 	case 'W':
    554 	  pinentry.parent_wid = pargs.r.ret_ulong;
    555 	  break;
    556 
    557 	case 'c':
    558           {
    559             char *tmpstr = pargs.r.ret_str;
    560 
    561             tmpstr = parse_color (tmpstr, &pinentry.color_fg,
    562                                   &pinentry.color_fg_bright);
    563             tmpstr = parse_color (tmpstr, &pinentry.color_bg, NULL);
    564             tmpstr = parse_color (tmpstr, &pinentry.color_so,
    565                                   &pinentry.color_so_bright);
    566           }
    567 	  break;
    568 
    569 	case 'o':
    570 	  pinentry.timeout = pargs.r.ret_int;
    571 	  break;
    572 
    573         default:
    574           pargs.err = ARGPARSE_PRINT_WARNING;
    575 	  break;
    576         }
    577     }
    578 }
    579 
    580 
    581 static gpg_error_t
    582 option_handler (assuan_context_t ctx, const char *key, const char *value)
    583 {
    584   (void)ctx;
    585 
    586   if (!strcmp (key, "no-grab") && !*value)
    587     pinentry.grab = 0;
    588   else if (!strcmp (key, "grab") && !*value)
    589     pinentry.grab = 1;
    590   else if (!strcmp (key, "debug-wait"))
    591     {
    592     }
    593   else if (!strcmp (key, "display"))
    594     {
    595       if (pinentry.display)
    596 	free (pinentry.display);
    597       pinentry.display = strdup (value);
    598       if (!pinentry.display)
    599 	return gpg_error_from_syserror ();
    600     }
    601   else if (!strcmp (key, "ttyname"))
    602     {
    603       if (pinentry.ttyname)
    604 	free (pinentry.ttyname);
    605       pinentry.ttyname = strdup (value);
    606       if (!pinentry.ttyname)
    607 	return gpg_error_from_syserror ();
    608     }
    609   else if (!strcmp (key, "ttytype"))
    610     {
    611       if (pinentry.ttytype)
    612 	free (pinentry.ttytype);
    613       pinentry.ttytype = strdup (value);
    614       if (!pinentry.ttytype)
    615 	return gpg_error_from_syserror ();
    616     }
    617   else if (!strcmp (key, "lc-ctype"))
    618     {
    619       if (pinentry.lc_ctype)
    620 	free (pinentry.lc_ctype);
    621       pinentry.lc_ctype = strdup (value);
    622       if (!pinentry.lc_ctype)
    623 	return gpg_error_from_syserror ();
    624     }
    625   else if (!strcmp (key, "lc-messages"))
    626     {
    627       if (pinentry.lc_messages)
    628 	free (pinentry.lc_messages);
    629       pinentry.lc_messages = strdup (value);
    630       if (!pinentry.lc_messages)
    631 	return gpg_error_from_syserror ();
    632     }
    633   else if (!strcmp (key, "parent-wid"))
    634     {
    635       pinentry.parent_wid = atoi (value);
    636       /* FIXME: Use strtol and add some error handling.  */
    637     }
    638   else if (!strcmp (key, "touch-file"))
    639     {
    640       if (pinentry.touch_file)
    641         free (pinentry.touch_file);
    642       pinentry.touch_file = strdup (value);
    643       if (!pinentry.touch_file)
    644 	return gpg_error_from_syserror ();
    645     }
    646   else if (!strcmp (key, "default-ok"))
    647     {
    648       pinentry.default_ok = strdup (value);
    649       if (!pinentry.default_ok)
    650 	return gpg_error_from_syserror ();
    651     }
    652   else if (!strcmp (key, "default-cancel"))
    653     {
    654       pinentry.default_cancel = strdup (value);
    655       if (!pinentry.default_cancel)
    656 	return gpg_error_from_syserror ();
    657     }
    658   else if (!strcmp (key, "default-prompt"))
    659     {
    660       pinentry.default_prompt = strdup (value);
    661       if (!pinentry.default_prompt)
    662 	return gpg_error_from_syserror ();
    663     }
    664   else if (!strcmp (key, "default-pwmngr"))
    665     {
    666       pinentry.default_pwmngr = strdup (value);
    667       if (!pinentry.default_pwmngr)
    668 	return gpg_error_from_syserror ();
    669     }
    670   else if (!strcmp (key, "allow-external-password-cache") && !*value)
    671     {
    672       pinentry.allow_external_password_cache = 1;
    673       pinentry.tried_password_cache = 0;
    674     }
    675   else if (!strcmp (key, "allow-emacs-prompt") && !*value)
    676     {
    677       return gpg_error (GPG_ERR_NOT_SUPPORTED);
    678     }
    679   else
    680     return gpg_error (GPG_ERR_UNKNOWN_OPTION);
    681   return 0;
    682 }
    683 
    684 
    685 /* Note, that it is sufficient to allocate the target string D as
    686    long as the source string S, i.e.: strlen(s)+1; */
    687 static void
    688 strcpy_escaped (char *d, const char *s)
    689 {
    690   while (*s)
    691     {
    692       if (*s == '%' && s[1] && s[2])
    693         {
    694           s++;
    695           *d++ = xtoi_2 ( s);
    696           s += 2;
    697         }
    698       else
    699         *d++ = *s++;
    700     }
    701   *d = 0;
    702 }
    703 
    704 
    705 static gpg_error_t
    706 cmd_setdesc (assuan_context_t ctx, char *line)
    707 {
    708   char *newd;
    709 
    710   (void)ctx;
    711 
    712   newd = malloc (strlen (line) + 1);
    713   if (!newd)
    714     return gpg_error_from_syserror ();
    715 
    716   strcpy_escaped (newd, line);
    717   if (pinentry.description)
    718     free (pinentry.description);
    719   pinentry.description = newd;
    720   return 0;
    721 }
    722 
    723 
    724 static gpg_error_t
    725 cmd_setprompt (assuan_context_t ctx, char *line)
    726 {
    727   char *newp;
    728 
    729   (void)ctx;
    730 
    731   newp = malloc (strlen (line) + 1);
    732   if (!newp)
    733     return gpg_error_from_syserror ();
    734 
    735   strcpy_escaped (newp, line);
    736   if (pinentry.prompt)
    737     free (pinentry.prompt);
    738   pinentry.prompt = newp;
    739   return 0;
    740 }
    741 
    742 
    743 /* The data provided at LINE may be used by pinentry implementations
    744    to identify a key for caching strategies of its own.  The empty
    745    string and --clear mean that the key does not have a stable
    746    identifier.  */
    747 static gpg_error_t
    748 cmd_setkeyinfo (assuan_context_t ctx, char *line)
    749 {
    750   (void)ctx;
    751 
    752   if (pinentry.keyinfo)
    753     free (pinentry.keyinfo);
    754 
    755   if (*line && strcmp(line, "--clear") != 0)
    756     pinentry.keyinfo = strdup (line);
    757   else
    758     pinentry.keyinfo = NULL;
    759 
    760   return 0;
    761 }
    762 
    763 
    764 static gpg_error_t
    765 cmd_setrepeat (assuan_context_t ctx, char *line)
    766 {
    767   char *p;
    768 
    769   (void)ctx;
    770 
    771   p = malloc (strlen (line) + 1);
    772   if (!p)
    773     return gpg_error_from_syserror ();
    774 
    775   strcpy_escaped (p, line);
    776   free (pinentry.repeat_passphrase);
    777   pinentry.repeat_passphrase = p;
    778   return 0;
    779 }
    780 
    781 
    782 static gpg_error_t
    783 cmd_setrepeaterror (assuan_context_t ctx, char *line)
    784 {
    785   char *p;
    786 
    787   (void)ctx;
    788 
    789   p = malloc (strlen (line) + 1);
    790   if (!p)
    791     return gpg_error_from_syserror ();
    792 
    793   strcpy_escaped (p, line);
    794   free (pinentry.repeat_error_string);
    795   pinentry.repeat_error_string = p;
    796   return 0;
    797 }
    798 
    799 
    800 static gpg_error_t
    801 cmd_seterror (assuan_context_t ctx, char *line)
    802 {
    803   char *newe;
    804 
    805   (void)ctx;
    806 
    807   newe = malloc (strlen (line) + 1);
    808   if (!newe)
    809     return gpg_error_from_syserror ();
    810 
    811   strcpy_escaped (newe, line);
    812   if (pinentry.error)
    813     free (pinentry.error);
    814   pinentry.error = newe;
    815   return 0;
    816 }
    817 
    818 
    819 static gpg_error_t
    820 cmd_setok (assuan_context_t ctx, char *line)
    821 {
    822   char *newo;
    823 
    824   (void)ctx;
    825 
    826   newo = malloc (strlen (line) + 1);
    827   if (!newo)
    828     return gpg_error_from_syserror ();
    829 
    830   strcpy_escaped (newo, line);
    831   if (pinentry.ok)
    832     free (pinentry.ok);
    833   pinentry.ok = newo;
    834   return 0;
    835 }
    836 
    837 
    838 static gpg_error_t
    839 cmd_setnotok (assuan_context_t ctx, char *line)
    840 {
    841   char *newo;
    842 
    843   (void)ctx;
    844 
    845   newo = malloc (strlen (line) + 1);
    846   if (!newo)
    847     return gpg_error_from_syserror ();
    848 
    849   strcpy_escaped (newo, line);
    850   if (pinentry.notok)
    851     free (pinentry.notok);
    852   pinentry.notok = newo;
    853   return 0;
    854 }
    855 
    856 
    857 static gpg_error_t
    858 cmd_setcancel (assuan_context_t ctx, char *line)
    859 {
    860   char *newc;
    861 
    862   (void)ctx;
    863 
    864   newc = malloc (strlen (line) + 1);
    865   if (!newc)
    866     return gpg_error_from_syserror ();
    867 
    868   strcpy_escaped (newc, line);
    869   if (pinentry.cancel)
    870     free (pinentry.cancel);
    871   pinentry.cancel = newc;
    872   return 0;
    873 }
    874 
    875 
    876 static gpg_error_t
    877 cmd_settimeout (assuan_context_t ctx, char *line)
    878 {
    879   (void)ctx;
    880 
    881   if (line && *line)
    882     pinentry.timeout = atoi (line);
    883 
    884   return 0;
    885 }
    886 
    887 static gpg_error_t
    888 cmd_settitle (assuan_context_t ctx, char *line)
    889 {
    890   char *newt;
    891 
    892   (void)ctx;
    893 
    894   newt = malloc (strlen (line) + 1);
    895   if (!newt)
    896     return gpg_error_from_syserror ();
    897 
    898   strcpy_escaped (newt, line);
    899   if (pinentry.title)
    900     free (pinentry.title);
    901   pinentry.title = newt;
    902   return 0;
    903 }
    904 
    905 static gpg_error_t
    906 cmd_setqualitybar (assuan_context_t ctx, char *line)
    907 {
    908   char *newval;
    909 
    910   (void)ctx;
    911 
    912   if (!*line)
    913     line = "Quality:";
    914 
    915   newval = malloc (strlen (line) + 1);
    916   if (!newval)
    917     return gpg_error_from_syserror ();
    918 
    919   strcpy_escaped (newval, line);
    920   if (pinentry.quality_bar)
    921     free (pinentry.quality_bar);
    922   pinentry.quality_bar = newval;
    923   return 0;
    924 }
    925 
    926 /* Set the tooltip to be used for a quality bar.  */
    927 static gpg_error_t
    928 cmd_setqualitybar_tt (assuan_context_t ctx, char *line)
    929 {
    930   char *newval;
    931 
    932   (void)ctx;
    933 
    934   if (*line)
    935     {
    936       newval = malloc (strlen (line) + 1);
    937       if (!newval)
    938         return gpg_error_from_syserror ();
    939 
    940       strcpy_escaped (newval, line);
    941     }
    942   else
    943     newval = NULL;
    944   if (pinentry.quality_bar_tt)
    945     free (pinentry.quality_bar_tt);
    946   pinentry.quality_bar_tt = newval;
    947   return 0;
    948 }
    949 
    950 
    951 static gpg_error_t
    952 cmd_getpin (assuan_context_t ctx, char *line)
    953 {
    954   int result;
    955   int set_prompt = 0;
    956   int just_read_password_from_cache = 0;
    957 
    958   (void)line;
    959 
    960   pinentry_setbuffer_init (&pinentry);
    961   if (!pinentry.pin)
    962     return gpg_error (GPG_ERR_ENOMEM);
    963 
    964   /* Try reading from the password cache.  */
    965   if (/* If repeat passphrase is set, then we don't want to read from
    966 	 the cache.  */
    967       ! pinentry.repeat_passphrase
    968       /* Are we allowed to read from the cache?  */
    969       && pinentry.allow_external_password_cache
    970       && pinentry.keyinfo
    971       /* Only read from the cache if we haven't already tried it.  */
    972       && ! pinentry.tried_password_cache
    973       /* If the last read resulted in an error, then don't read from
    974 	 the cache.  */
    975       && ! pinentry.error)
    976     {
    977       char *password;
    978 
    979       pinentry.tried_password_cache = 1;
    980 
    981       password = password_cache_lookup (pinentry.keyinfo);
    982       if (password)
    983 	/* There is a cached password.  Try it.  */
    984 	{
    985 	  int len = strlen(password) + 1;
    986 	  if (len > pinentry.pin_len)
    987 	    len = pinentry.pin_len;
    988 
    989 	  memcpy (pinentry.pin, password, len);
    990 	  pinentry.pin[len] = '\0';
    991 
    992 	  secmem_free (password);
    993 
    994 	  pinentry.pin_from_cache = 1;
    995 
    996 	  assuan_write_status (ctx, "PASSWORD_FROM_CACHE", "");
    997 
    998 	  /* Result is the length of the password not including the
    999 	     NUL terminator.  */
   1000 	  result = len - 1;
   1001 
   1002 	  just_read_password_from_cache = 1;
   1003 
   1004 	  goto out;
   1005 	}
   1006     }
   1007 
   1008   /* The password was not cached (or we are not allowed to / cannot
   1009      use the cache).  Prompt the user.  */
   1010   pinentry.pin_from_cache = 0;
   1011 
   1012   if (!pinentry.prompt)
   1013     {
   1014       pinentry.prompt = pinentry.default_prompt?pinentry.default_prompt:"PIN:";
   1015       set_prompt = 1;
   1016     }
   1017   pinentry.locale_err = 0;
   1018   pinentry.specific_err = 0;
   1019   pinentry.close_button = 0;
   1020   pinentry.repeat_okay = 0;
   1021   pinentry.one_button = 0;
   1022   pinentry.ctx_assuan = ctx;
   1023   result = (*pinentry_cmd_handler) (&pinentry);
   1024   pinentry.ctx_assuan = NULL;
   1025   if (pinentry.error)
   1026     {
   1027       free (pinentry.error);
   1028       pinentry.error = NULL;
   1029     }
   1030   if (pinentry.repeat_passphrase)
   1031     {
   1032       free (pinentry.repeat_passphrase);
   1033       pinentry.repeat_passphrase = NULL;
   1034     }
   1035   if (set_prompt)
   1036     pinentry.prompt = NULL;
   1037 
   1038   pinentry.quality_bar = 0;  /* Reset it after the command.  */
   1039 
   1040   if (pinentry.close_button)
   1041     assuan_write_status (ctx, "BUTTON_INFO", "close");
   1042 
   1043   if (result < 0)
   1044     {
   1045       pinentry_setbuffer_clear (&pinentry);
   1046       if (pinentry.specific_err)
   1047         return pinentry.specific_err;
   1048       return (pinentry.locale_err
   1049 	      ? gpg_error (GPG_ERR_LOCALE_PROBLEM)
   1050 	      : gpg_error (GPG_ERR_CANCELED));
   1051     }
   1052 
   1053  out:
   1054   if (result)
   1055     {
   1056       if (pinentry.repeat_okay)
   1057         assuan_write_status (ctx, "PIN_REPEATED", "");
   1058       result = assuan_send_data (ctx, pinentry.pin, strlen(pinentry.pin));
   1059       if (!result)
   1060 	result = assuan_send_data (ctx, NULL, 0);
   1061 
   1062       if (/* GPG Agent says it's okay.  */
   1063 	  pinentry.allow_external_password_cache && pinentry.keyinfo
   1064 	  /* We didn't just read it from the cache.  */
   1065 	  && ! just_read_password_from_cache
   1066 	  /* And the user said it's okay.  */
   1067 	  && pinentry.may_cache_password)
   1068 	/* Cache the password.  */
   1069 	password_cache_save (pinentry.keyinfo, pinentry.pin);
   1070     }
   1071 
   1072   pinentry_setbuffer_clear (&pinentry);
   1073 
   1074   return result;
   1075 }
   1076 
   1077 
   1078 /* Note that the option --one-button is a hack to allow the use of old
   1079    pinentries while the caller is ignoring the result.  Given that
   1080    options have never been used or flagged as an error the new option
   1081    is an easy way to enable the messsage mode while not requiring to
   1082    update pinentry or to have the caller test for the message
   1083    command.  New applications which are free to require an updated
   1084    pinentry should use MESSAGE instead. */
   1085 static gpg_error_t
   1086 cmd_confirm (assuan_context_t ctx, char *line)
   1087 {
   1088   int result;
   1089 
   1090   pinentry.one_button = !!strstr (line, "--one-button");
   1091   pinentry.quality_bar = 0;
   1092   pinentry.close_button = 0;
   1093   pinentry.locale_err = 0;
   1094   pinentry.specific_err = 0;
   1095   pinentry.canceled = 0;
   1096   pinentry_setbuffer_clear (&pinentry);
   1097   result = (*pinentry_cmd_handler) (&pinentry);
   1098   if (pinentry.error)
   1099     {
   1100       free (pinentry.error);
   1101       pinentry.error = NULL;
   1102     }
   1103 
   1104   if (pinentry.close_button)
   1105     assuan_write_status (ctx, "BUTTON_INFO", "close");
   1106 
   1107   if (result)
   1108     return 0;
   1109 
   1110   if (pinentry.specific_err)
   1111     return pinentry.specific_err;
   1112 
   1113   if (pinentry.locale_err)
   1114     return gpg_error (GPG_ERR_LOCALE_PROBLEM);
   1115 
   1116   if (pinentry.one_button)
   1117     return 0;
   1118 
   1119   if (pinentry.canceled)
   1120     return gpg_error (GPG_ERR_CANCELED);
   1121   return gpg_error (GPG_ERR_NOT_CONFIRMED);
   1122 }
   1123 
   1124 
   1125 static gpg_error_t
   1126 cmd_message (assuan_context_t ctx, char *line)
   1127 {
   1128   (void)line;
   1129 
   1130   return cmd_confirm (ctx, "--one-button");
   1131 }
   1132 
   1133 /* GETINFO <what>
   1134 
   1135    Multipurpose function to return a variety of information.
   1136    Supported values for WHAT are:
   1137 
   1138      version     - Return the version of the program.
   1139      pid         - Return the process id of the server.
   1140  */
   1141 static gpg_error_t
   1142 cmd_getinfo (assuan_context_t ctx, char *line)
   1143 {
   1144   int rc;
   1145 
   1146   if (!strcmp (line, "version"))
   1147     {
   1148       const char *s = VERSION;
   1149       rc = assuan_send_data (ctx, s, strlen (s));
   1150     }
   1151   else if (!strcmp (line, "pid"))
   1152     {
   1153       char numbuf[50];
   1154 
   1155       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
   1156       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
   1157     }
   1158   else
   1159     rc = gpg_error (GPG_ERR_ASS_PARAMETER);
   1160   return rc;
   1161 }
   1162 
   1163 /* CLEARPASSPHRASE <cacheid>
   1164 
   1165    Clear the cache passphrase associated with the key identified by
   1166    cacheid.
   1167  */
   1168 static gpg_error_t
   1169 cmd_clear_passphrase (assuan_context_t ctx, char *line)
   1170 {
   1171   (void)ctx;
   1172 
   1173   if (! line)
   1174     return gpg_error (GPG_ERR_ASS_INV_VALUE);
   1175 
   1176   /* Remove leading and trailing white space.  */
   1177   while (*line == ' ')
   1178     line ++;
   1179   while (line[strlen (line) - 1] == ' ')
   1180     line[strlen (line) - 1] = 0;
   1181 
   1182   switch (password_cache_clear (line))
   1183     {
   1184     case 1: return 0;
   1185     case 0: return gpg_error (GPG_ERR_ASS_INV_VALUE);
   1186     default: return gpg_error (GPG_ERR_ASS_GENERAL);
   1187     }
   1188 }
   1189 
   1190 /* Tell the assuan library about our commands.  */
   1191 static gpg_error_t
   1192 register_commands (assuan_context_t ctx)
   1193 {
   1194   static struct
   1195   {
   1196     const char *name;
   1197     gpg_error_t (*handler) (assuan_context_t, char *line);
   1198   } table[] =
   1199     {
   1200       { "SETDESC",    cmd_setdesc },
   1201       { "SETPROMPT",  cmd_setprompt },
   1202       { "SETKEYINFO", cmd_setkeyinfo },
   1203       { "SETREPEAT",  cmd_setrepeat },
   1204       { "SETREPEATERROR", cmd_setrepeaterror },
   1205       { "SETERROR",   cmd_seterror },
   1206       { "SETOK",      cmd_setok },
   1207       { "SETNOTOK",   cmd_setnotok },
   1208       { "SETCANCEL",  cmd_setcancel },
   1209       { "GETPIN",     cmd_getpin },
   1210       { "CONFIRM",    cmd_confirm },
   1211       { "MESSAGE",    cmd_message },
   1212       { "SETQUALITYBAR", cmd_setqualitybar },
   1213       { "SETQUALITYBAR_TT", cmd_setqualitybar_tt },
   1214       { "GETINFO",    cmd_getinfo },
   1215       { "SETTITLE",   cmd_settitle },
   1216       { "SETTIMEOUT", cmd_settimeout },
   1217       { "CLEARPASSPHRASE", cmd_clear_passphrase },
   1218       { NULL }
   1219     };
   1220   int i, j;
   1221   gpg_error_t rc;
   1222 
   1223   for (i = j = 0; table[i].name; i++)
   1224     {
   1225       rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL);
   1226       if (rc)
   1227         return rc;
   1228     }
   1229   return 0;
   1230 }
   1231 
   1232 
   1233 int
   1234 pinentry_loop2 (int infd, int outfd)
   1235 {
   1236   gpg_error_t rc;
   1237   assuan_fd_t filedes[2];
   1238   assuan_context_t ctx;
   1239 
   1240   /* Extra check to make sure we have dropped privs. */
   1241   if (getuid() != geteuid())
   1242     abort ();
   1243 
   1244   rc = assuan_new (&ctx);
   1245   if (rc)
   1246     {
   1247       fprintf (stderr, "server context creation failed: %s\n",
   1248 	       gpg_strerror (rc));
   1249       return -1;
   1250     }
   1251 
   1252   /* For now we use a simple pipe based server so that we can work
   1253      from scripts.  We will later add options to run as a daemon and
   1254      wait for requests on a Unix domain socket.  */
   1255   filedes[0] = assuan_fdopen (infd);
   1256   filedes[1] = assuan_fdopen (outfd);
   1257   rc = assuan_init_pipe_server (ctx, filedes);
   1258   if (rc)
   1259     {
   1260       fprintf (stderr, "%s: failed to initialize the server: %s\n",
   1261                this_pgmname, gpg_strerror (rc));
   1262       return -1;
   1263     }
   1264   rc = register_commands (ctx);
   1265   if (rc)
   1266     {
   1267       fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n",
   1268                this_pgmname, gpg_strerror (rc));
   1269       return -1;
   1270     }
   1271 
   1272   assuan_register_option_handler (ctx, option_handler);
   1273   assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler);
   1274 
   1275   for (;;)
   1276     {
   1277       rc = assuan_accept (ctx);
   1278       if (rc == -1)
   1279           break;
   1280       else if (rc)
   1281         {
   1282           fprintf (stderr, "%s: Assuan accept problem: %s\n",
   1283                    this_pgmname, gpg_strerror (rc));
   1284           break;
   1285         }
   1286 
   1287       rc = assuan_process (ctx);
   1288       if (rc)
   1289         {
   1290           fprintf (stderr, "%s: Assuan processing failed: %s\n",
   1291                    this_pgmname, gpg_strerror (rc));
   1292           continue;
   1293         }
   1294     }
   1295 
   1296   assuan_release (ctx);
   1297   return 0;
   1298 }
   1299 
   1300 
   1301 /* Start the pinentry event loop.  The program will start to process
   1302    Assuan commands until it is finished or an error occurs.  If an
   1303    error occurs, -1 is returned.  Otherwise, 0 is returned.  */
   1304 int
   1305 pinentry_loop (void)
   1306 {
   1307   return pinentry_loop2 (STDIN_FILENO, STDOUT_FILENO);
   1308 }