1 /* pinentry.c - The PIN entry support library
2 * Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015, 2016 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 <https://www.gnu.org/licenses/>.
18 * SPDX-License-Identifier: GPL-2.0+
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #ifndef HAVE_W32CE_SYSTEM
26 # include <errno.h>
27 #endif
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <assert.h>
34 #ifndef HAVE_W32_SYSTEM
35 # include <sys/utsname.h>
36 #endif
37 #ifndef HAVE_W32CE_SYSTEM
38 # include <locale.h>
39 #endif
40 #ifdef HAVE_LANGINFO_H
41 #include <langinfo.h>
42 #endif
43 #include <limits.h>
44 #ifdef HAVE_W32CE_SYSTEM
45 # include <windows.h>
46 #endif
47
48 #undef WITH_UTF8_CONVERSION
49 #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
50 # include <iconv.h>
51 # define WITH_UTF8_CONVERSION 1
52 #endif
53
54 #include <assuan.h>
55
56 #include "memory.h"
57 #include "secmem-util.h"
58 #include "argparse.h"
59 #include "pinentry.h"
60 #include "password-cache.h"
61
62 #ifdef INSIDE_EMACS
63 # include "pinentry-emacs.h"
64 #endif
65 #ifdef FALLBACK_CURSES
66 # include "pinentry-curses.h"
67 #endif
68
69 #ifdef HAVE_W32CE_SYSTEM
70 #define getpid() GetCurrentProcessId ()
71 #endif
72
73 /* Keep the name of our program here. */
74 static char this_pgmname[50];
75
76 struct pinentry pinentry;
77
78
79 static const char *flavor_flag;
80
81 /* Because gtk_init removes the --display arg from the command lines
82 * and our command line parser is called after gtk_init (so that it
83 * does not see gtk specific options) we don't have a way to get hold
84 * of the --display option. Our solution is to remember --disable in
85 * the call to pinentry_have_display and set it then in our
86 * parser. */
87 static char *remember_display;
88
89 /* Flag to remember whether a warning has been printed. */
90 #ifdef WITH_UTF8_CONVERSION
91 static int lc_ctype_unknown_warning;
92 #endif
93
94
95 static void
pinentry_reset(int use_defaults)96 pinentry_reset (int use_defaults)
97 {
98 /* GPG Agent sets these options once when it starts the pinentry.
99 Don't reset them. */
100 int grab = pinentry.grab;
101 char *ttyname = pinentry.ttyname;
102 char *ttytype = pinentry.ttytype_l;
103 char *ttyalert = pinentry.ttyalert;
104 char *lc_ctype = pinentry.lc_ctype;
105 char *lc_messages = pinentry.lc_messages;
106 int allow_external_password_cache = pinentry.allow_external_password_cache;
107 char *default_ok = pinentry.default_ok;
108 char *default_cancel = pinentry.default_cancel;
109 char *default_prompt = pinentry.default_prompt;
110 char *default_pwmngr = pinentry.default_pwmngr;
111 char *default_cf_visi = pinentry.default_cf_visi;
112 char *default_tt_visi = pinentry.default_tt_visi;
113 char *default_tt_hide = pinentry.default_tt_hide;
114 char *touch_file = pinentry.touch_file;
115 unsigned long owner_pid = pinentry.owner_pid;
116 int owner_uid = pinentry.owner_uid;
117 char *owner_host = pinentry.owner_host;
118
119 /* These options are set from the command line. Don't reset
120 them. */
121 int debug = pinentry.debug;
122 char *display = pinentry.display;
123 int parent_wid = pinentry.parent_wid;
124
125 pinentry_color_t color_fg = pinentry.color_fg;
126 int color_fg_bright = pinentry.color_fg_bright;
127 pinentry_color_t color_bg = pinentry.color_bg;
128 pinentry_color_t color_so = pinentry.color_so;
129 int color_so_bright = pinentry.color_so_bright;
130
131 int timeout = pinentry.timeout;
132
133 char *invisible_char = pinentry.invisible_char;
134
135
136 /* Free any allocated memory. */
137 if (use_defaults)
138 {
139 free (pinentry.ttyname);
140 free (pinentry.ttytype_l);
141 free (pinentry.ttyalert);
142 free (pinentry.lc_ctype);
143 free (pinentry.lc_messages);
144 free (pinentry.default_ok);
145 free (pinentry.default_cancel);
146 free (pinentry.default_prompt);
147 free (pinentry.default_pwmngr);
148 free (pinentry.default_cf_visi);
149 free (pinentry.default_tt_visi);
150 free (pinentry.default_tt_hide);
151 free (pinentry.touch_file);
152 free (pinentry.owner_host);
153 free (pinentry.display);
154 }
155
156 free (pinentry.title);
157 free (pinentry.description);
158 free (pinentry.error);
159 free (pinentry.prompt);
160 free (pinentry.ok);
161 free (pinentry.notok);
162 free (pinentry.cancel);
163 secmem_free (pinentry.pin);
164 free (pinentry.repeat_passphrase);
165 free (pinentry.repeat_error_string);
166 free (pinentry.quality_bar);
167 free (pinentry.quality_bar_tt);
168 free (pinentry.keyinfo);
169 free (pinentry.specific_err_info);
170
171 /* Reset the pinentry structure. */
172 memset (&pinentry, 0, sizeof (pinentry));
173
174 /* Restore options without a default we want to preserve. */
175 pinentry.invisible_char = invisible_char;
176
177 /* Restore other options or set defaults. */
178
179 if (use_defaults)
180 {
181 /* Pinentry timeout in seconds. */
182 pinentry.timeout = 60;
183
184 /* Global grab. */
185 pinentry.grab = 1;
186
187 pinentry.color_fg = PINENTRY_COLOR_DEFAULT;
188 pinentry.color_fg_bright = 0;
189 pinentry.color_bg = PINENTRY_COLOR_DEFAULT;
190 pinentry.color_so = PINENTRY_COLOR_DEFAULT;
191 pinentry.color_so_bright = 0;
192
193 pinentry.owner_uid = -1;
194 }
195 else /* Restore the options. */
196 {
197 pinentry.grab = grab;
198 pinentry.ttyname = ttyname;
199 pinentry.ttytype_l = ttytype;
200 pinentry.ttyalert = ttyalert;
201 pinentry.lc_ctype = lc_ctype;
202 pinentry.lc_messages = lc_messages;
203 pinentry.allow_external_password_cache = allow_external_password_cache;
204 pinentry.default_ok = default_ok;
205 pinentry.default_cancel = default_cancel;
206 pinentry.default_prompt = default_prompt;
207 pinentry.default_pwmngr = default_pwmngr;
208 pinentry.default_cf_visi = default_cf_visi;
209 pinentry.default_tt_visi = default_tt_visi;
210 pinentry.default_tt_hide = default_tt_hide;
211 pinentry.touch_file = touch_file;
212 pinentry.owner_pid = owner_pid;
213 pinentry.owner_uid = owner_uid;
214 pinentry.owner_host = owner_host;
215
216 pinentry.debug = debug;
217 pinentry.display = display;
218 pinentry.parent_wid = parent_wid;
219
220 pinentry.color_fg = color_fg;
221 pinentry.color_fg_bright = color_fg_bright;
222 pinentry.color_bg = color_bg;
223 pinentry.color_so = color_so;
224 pinentry.color_so_bright = color_so_bright;
225
226 pinentry.timeout = timeout;
227 }
228 }
229
230 static gpg_error_t
pinentry_assuan_reset_handler(assuan_context_t ctx,char * line)231 pinentry_assuan_reset_handler (assuan_context_t ctx, char *line)
232 {
233 (void)ctx;
234 (void)line;
235
236 pinentry_reset (0);
237
238 return 0;
239 }
240
241
242
243 #ifdef WITH_UTF8_CONVERSION
244 char *
pinentry_utf8_to_local(const char * lc_ctype,const char * text)245 pinentry_utf8_to_local (const char *lc_ctype, const char *text)
246 {
247 iconv_t cd;
248 const char *input = text;
249 size_t input_len = strlen (text) + 1;
250 char *output;
251 size_t output_len;
252 char *output_buf;
253 size_t processed;
254 char *old_ctype;
255 char *target_encoding;
256
257 /* If no locale setting could be determined, simply copy the
258 string. */
259 if (!lc_ctype)
260 {
261 if (! lc_ctype_unknown_warning)
262 {
263 fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
264 this_pgmname);
265 lc_ctype_unknown_warning = 1;
266 }
267 return strdup (text);
268 }
269
270 old_ctype = strdup (setlocale (LC_CTYPE, NULL));
271 if (!old_ctype)
272 return NULL;
273 setlocale (LC_CTYPE, lc_ctype);
274 target_encoding = nl_langinfo (CODESET);
275 if (!target_encoding)
276 target_encoding = "?";
277 setlocale (LC_CTYPE, old_ctype);
278 free (old_ctype);
279
280 /* This is overkill, but simplifies the iconv invocation greatly. */
281 output_len = input_len * MB_LEN_MAX;
282 output_buf = output = malloc (output_len);
283 if (!output)
284 return NULL;
285
286 cd = iconv_open (target_encoding, "UTF-8");
287 if (cd == (iconv_t) -1)
288 {
289 fprintf (stderr, "%s: can't convert from UTF-8 to %s: %s\n",
290 this_pgmname, target_encoding, strerror (errno));
291 free (output_buf);
292 return NULL;
293 }
294 processed = iconv (cd, (ICONV_CONST char **)&input, &input_len,
295 &output, &output_len);
296 iconv_close (cd);
297 if (processed == (size_t) -1 || input_len)
298 {
299 fprintf (stderr, "%s: error converting from UTF-8 to %s: %s\n",
300 this_pgmname, target_encoding, strerror (errno));
301 free (output_buf);
302 return NULL;
303 }
304 return output_buf;
305 }
306 #endif /*WITH_UTF8_CONVERSION*/
307
308
309 /* Convert TEXT which is encoded according to LC_CTYPE to UTF-8. With
310 SECURE set to true, use secure memory for the returned buffer.
311 Return NULL on error. */
312 #ifdef WITH_UTF8_CONVERSION
313 char *
pinentry_local_to_utf8(char * lc_ctype,char * text,int secure)314 pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure)
315 {
316 char *old_ctype;
317 char *source_encoding;
318 iconv_t cd;
319 const char *input = text;
320 size_t input_len = strlen (text) + 1;
321 char *output;
322 size_t output_len;
323 char *output_buf;
324 size_t processed;
325
326 /* If no locale setting could be determined, simply copy the
327 string. */
328 if (!lc_ctype)
329 {
330 if (! lc_ctype_unknown_warning)
331 {
332 fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
333 this_pgmname);
334 lc_ctype_unknown_warning = 1;
335 }
336 output_buf = secure? secmem_malloc (input_len) : malloc (input_len);
337 if (output_buf)
338 strcpy (output_buf, input);
339 return output_buf;
340 }
341
342 old_ctype = strdup (setlocale (LC_CTYPE, NULL));
343 if (!old_ctype)
344 return NULL;
345 setlocale (LC_CTYPE, lc_ctype);
346 source_encoding = nl_langinfo (CODESET);
347 setlocale (LC_CTYPE, old_ctype);
348 free (old_ctype);
349
350 /* This is overkill, but simplifies the iconv invocation greatly. */
351 output_len = input_len * MB_LEN_MAX;
352 output_buf = output = secure? secmem_malloc (output_len):malloc (output_len);
353 if (!output)
354 return NULL;
355
356 cd = iconv_open ("UTF-8", source_encoding);
357 if (cd == (iconv_t) -1)
358 {
359 fprintf (stderr, "%s: can't convert from %s to UTF-8: %s\n",
360 this_pgmname, source_encoding? source_encoding : "?",
361 strerror (errno));
362 if (secure)
363 secmem_free (output_buf);
364 else
365 free (output_buf);
366 return NULL;
367 }
368 processed = iconv (cd, (ICONV_CONST char **)&input, &input_len,
369 &output, &output_len);
370 iconv_close (cd);
371 if (processed == (size_t) -1 || input_len)
372 {
373 fprintf (stderr, "%s: error converting from %s to UTF-8: %s\n",
374 this_pgmname, source_encoding? source_encoding : "?",
375 strerror (errno));
376 if (secure)
377 secmem_free (output_buf);
378 else
379 free (output_buf);
380 return NULL;
381 }
382 return output_buf;
383 }
384 #endif /*WITH_UTF8_CONVERSION*/
385
386
387 /* Copy TEXT or TEXTLEN to BUFFER and escape as required. Return a
388 pointer to the end of the new buffer. Note that BUFFER must be
389 large enough to keep the entire text; allocataing it 3 times of
390 TEXTLEN is sufficient. */
391 static char *
copy_and_escape(char * buffer,const void * text,size_t textlen)392 copy_and_escape (char *buffer, const void *text, size_t textlen)
393 {
394 int i;
395 const unsigned char *s = (unsigned char *)text;
396 char *p = buffer;
397
398 for (i=0; i < textlen; i++)
399 {
400 if (s[i] < ' ' || s[i] == '+')
401 {
402 snprintf (p, 4, "%%%02X", s[i]);
403 p += 3;
404 }
405 else if (s[i] == ' ')
406 *p++ = '+';
407 else
408 *p++ = s[i];
409 }
410 return p;
411 }
412
413
414
415 /* Return a malloced copy of the commandline for PID. If this is not
416 * possible NULL is returned. */
417 #ifndef HAVE_W32_SYSTEM
418 static char *
get_cmdline(unsigned long pid)419 get_cmdline (unsigned long pid)
420 {
421 char buffer[200];
422 FILE *fp;
423 size_t i, n;
424
425 snprintf (buffer, sizeof buffer, "/proc/%lu/cmdline", pid);
426 buffer[sizeof buffer - 1] = 0;
427
428 fp = fopen (buffer, "rb");
429 if (!fp)
430 return NULL;
431 n = fread (buffer, 1, sizeof buffer - 1, fp);
432 if (n < sizeof buffer -1 && ferror (fp))
433 {
434 /* Some error occurred. */
435 fclose (fp);
436 return NULL;
437 }
438 fclose (fp);
439 if (n == 0)
440 return NULL;
441 /* Arguments are delimited by Nuls. We should do proper quoting but
442 * that can be a bit complicated, thus we simply replace the Nuls by
443 * spaces. */
444 for (i=0; i < n; i++)
445 if (!buffer[i] && i < n-1)
446 buffer[i] = ' ';
447 buffer[i] = 0; /* Make sure the last byte is the string terminator. */
448
449 return strdup (buffer);
450 }
451 #endif /*!HAVE_W32_SYSTEM*/
452
453
454 /* Atomically ask the kernel for information about process PID.
455 * Return a malloc'ed copy of the process name as long as the process
456 * uid matches UID. If it cannot determine that the process has uid
457 * UID, it returns NULL.
458 *
459 * This is not as informative as get_cmdline, but it verifies that the
460 * process does belong to the user in question.
461 */
462 #ifndef HAVE_W32_SYSTEM
463 static char *
get_pid_name_for_uid(unsigned long pid,int uid)464 get_pid_name_for_uid (unsigned long pid, int uid)
465 {
466 char buffer[400];
467 FILE *fp;
468 size_t end, n;
469 char *uidstr;
470
471 snprintf (buffer, sizeof buffer, "/proc/%lu/status", pid);
472 buffer[sizeof buffer - 1] = 0;
473
474 fp = fopen (buffer, "rb");
475 if (!fp)
476 return NULL;
477 n = fread (buffer, 1, sizeof buffer - 1, fp);
478 if (n < sizeof buffer -1 && ferror (fp))
479 {
480 /* Some error occurred. */
481 fclose (fp);
482 return NULL;
483 }
484 fclose (fp);
485 if (n == 0)
486 return NULL;
487 /* Fixme: Is it specified that "Name" is always the first line? For
488 * robustness I would prefer to have a real parser here. -wk */
489 if (strncmp (buffer, "Name:\t", 6))
490 return NULL;
491 end = strcspn (buffer + 6, "\n") + 6;
492 buffer[end] = 0;
493
494 /* check that uid matches what we expect */
495 uidstr = strstr (buffer + end + 1, "\nUid:\t");
496 if (!uidstr)
497 return NULL;
498 if (atoi (uidstr + 6) != uid)
499 return NULL;
500
501 return strdup (buffer + 6);
502 }
503 #endif /*!HAVE_W32_SYSTEM*/
504
505
506 /* Return a malloced string with the title. The caller mus free the
507 * string. If no title is available or the title string has an error
508 * NULL is returned. */
509 char *
pinentry_get_title(pinentry_t pe)510 pinentry_get_title (pinentry_t pe)
511 {
512 char *title;
513
514 if (pe->title)
515 title = strdup (pe->title);
516 #ifndef HAVE_W32_SYSTEM
517 else if (pe->owner_pid)
518 {
519 char buf[200];
520 struct utsname utsbuf;
521 char *pidname = NULL;
522 char *cmdline = NULL;
523
524 if (pe->owner_host &&
525 !uname (&utsbuf) && utsbuf.nodename &&
526 !strcmp (utsbuf.nodename, pe->owner_host))
527 {
528 pidname = get_pid_name_for_uid (pe->owner_pid, pe->owner_uid);
529 if (pidname)
530 cmdline = get_cmdline (pe->owner_pid);
531 }
532
533 if (pe->owner_host && (cmdline || pidname))
534 snprintf (buf, sizeof buf, "[%lu]@%s (%s)",
535 pe->owner_pid, pe->owner_host, cmdline ? cmdline : pidname);
536 else if (pe->owner_host)
537 snprintf (buf, sizeof buf, "[%lu]@%s",
538 pe->owner_pid, pe->owner_host);
539 else
540 snprintf (buf, sizeof buf, "[%lu] <unknown host>",
541 pe->owner_pid);
542 buf[sizeof buf - 1] = 0;
543 free (pidname);
544 free (cmdline);
545 title = strdup (buf);
546 }
547 #endif /*!HAVE_W32_SYSTEM*/
548 else
549 title = strdup (this_pgmname);
550
551 return title;
552 }
553
554
555 /* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH
556 because not all backends might be able to return a proper
557 C-string.). Returns: A value between -100 and 100 to give an
558 estimate of the passphrase's quality. Negative values are use if
559 the caller won't even accept that passphrase. Note that we expect
560 just one data line which should not be escaped in any represent a
561 numeric signed decimal value. Extra data is currently ignored but
562 should not be send at all. */
563 int
pinentry_inq_quality(pinentry_t pin,const char * passphrase,size_t length)564 pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
565 {
566 assuan_context_t ctx = pin->ctx_assuan;
567 const char prefix[] = "INQUIRE QUALITY ";
568 char *command;
569 char *line;
570 size_t linelen;
571 int gotvalue = 0;
572 int value = 0;
573 int rc;
574
575 if (!ctx)
576 return 0; /* Can't run the callback. */
577
578 if (length > 300)
579 length = 300; /* Limit so that it definitely fits into an Assuan
580 line. */
581
582 command = secmem_malloc (strlen (prefix) + 3*length + 1);
583 if (!command)
584 return 0;
585 strcpy (command, prefix);
586 copy_and_escape (command + strlen(command), passphrase, length);
587 rc = assuan_write_line (ctx, command);
588 secmem_free (command);
589 if (rc)
590 {
591 fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
592 return 0;
593 }
594
595 for (;;)
596 {
597 do
598 {
599 rc = assuan_read_line (ctx, &line, &linelen);
600 if (rc)
601 {
602 fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
603 return 0;
604 }
605 }
606 while (*line == '#' || !linelen);
607 if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
608 && (!line[3] || line[3] == ' '))
609 break; /* END command received*/
610 if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
611 && (!line[3] || line[3] == ' '))
612 break; /* CAN command received*/
613 if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
614 && (!line[3] || line[3] == ' '))
615 break; /* ERR command received*/
616 if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
617 continue;
618 gotvalue = 1;
619 value = atoi (line+2);
620 }
621 if (value < -100)
622 value = -100;
623 else if (value > 100)
624 value = 100;
625
626 return value;
627 }
628
629 /* Run a genpin inquiry */
630 char *
pinentry_inq_genpin(pinentry_t pin)631 pinentry_inq_genpin (pinentry_t pin)
632 {
633 assuan_context_t ctx = pin->ctx_assuan;
634 const char prefix[] = "INQUIRE GENPIN";
635 char *line;
636 size_t linelen;
637 int gotvalue = 0;
638 char *value = NULL;
639 int rc;
640
641 if (!ctx)
642 return 0; /* Can't run the callback. */
643
644 rc = assuan_write_line (ctx, prefix);
645 if (rc)
646 {
647 fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
648 return 0;
649 }
650
651 for (;;)
652 {
653 do
654 {
655 rc = assuan_read_line (ctx, &line, &linelen);
656 if (rc)
657 {
658 fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
659 return 0;
660 }
661 }
662 while (*line == '#' || !linelen);
663 if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
664 && (!line[3] || line[3] == ' '))
665 break; /* END command received*/
666 if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
667 && (!line[3] || line[3] == ' '))
668 break; /* CAN command received*/
669 if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
670 && (!line[3] || line[3] == ' '))
671 break; /* ERR command received*/
672 if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
673 continue;
674 gotvalue = 1;
675 value = strdup (line + 2);
676 }
677
678 return value;
679 }
680
681 /* Try to make room for at least LEN bytes in the pinentry. Returns
682 new buffer on success and 0 on failure or when the old buffer is
683 sufficient. */
684 char *
pinentry_setbufferlen(pinentry_t pin,int len)685 pinentry_setbufferlen (pinentry_t pin, int len)
686 {
687 char *newp;
688
689 if (pin->pin_len)
690 assert (pin->pin);
691 else
692 assert (!pin->pin);
693
694 if (len < 2048)
695 len = 2048;
696
697 if (len <= pin->pin_len)
698 return pin->pin;
699
700 newp = secmem_realloc (pin->pin, len);
701 if (newp)
702 {
703 pin->pin = newp;
704 pin->pin_len = len;
705 }
706 else
707 {
708 secmem_free (pin->pin);
709 pin->pin = 0;
710 pin->pin_len = 0;
711 }
712 return newp;
713 }
714
715 static void
pinentry_setbuffer_clear(pinentry_t pin)716 pinentry_setbuffer_clear (pinentry_t pin)
717 {
718 if (! pin->pin)
719 {
720 assert (pin->pin_len == 0);
721 return;
722 }
723
724 assert (pin->pin_len > 0);
725
726 secmem_free (pin->pin);
727 pin->pin = NULL;
728 pin->pin_len = 0;
729 }
730
731 static void
pinentry_setbuffer_init(pinentry_t pin)732 pinentry_setbuffer_init (pinentry_t pin)
733 {
734 pinentry_setbuffer_clear (pin);
735 pinentry_setbufferlen (pin, 0);
736 }
737
738 /* passphrase better be alloced with secmem_alloc. */
739 void
pinentry_setbuffer_use(pinentry_t pin,char * passphrase,int len)740 pinentry_setbuffer_use (pinentry_t pin, char *passphrase, int len)
741 {
742 if (! passphrase)
743 {
744 assert (len == 0);
745 pinentry_setbuffer_clear (pin);
746
747 return;
748 }
749
750 if (passphrase && len == 0)
751 len = strlen (passphrase) + 1;
752
753 if (pin->pin)
754 secmem_free (pin->pin);
755
756 pin->pin = passphrase;
757 pin->pin_len = len;
758 }
759
760 static struct assuan_malloc_hooks assuan_malloc_hooks = {
761 secmem_malloc, secmem_realloc, secmem_free
762 };
763
764 /* Initialize the secure memory subsystem, drop privileges and return.
765 Must be called early. */
766 void
pinentry_init(const char * pgmname)767 pinentry_init (const char *pgmname)
768 {
769 /* Store away our name. */
770 if (strlen (pgmname) > sizeof this_pgmname - 2)
771 abort ();
772 strcpy (this_pgmname, pgmname);
773
774 gpgrt_check_version (NULL);
775
776 /* Initialize secure memory. 1 is too small, so the default size
777 will be used. */
778 secmem_init (1);
779 secmem_set_flags (SECMEM_WARN);
780 drop_privs ();
781
782 if (atexit (secmem_term))
783 {
784 /* FIXME: Could not register at-exit function, bail out. */
785 }
786
787 assuan_set_malloc_hooks (&assuan_malloc_hooks);
788 }
789
790 /* Simple test to check whether DISPLAY is set or the option --display
791 was given. Used to decide whether the GUI or curses should be
792 initialized. */
793 int
pinentry_have_display(int argc,char ** argv)794 pinentry_have_display (int argc, char **argv)
795 {
796 int found = 0;
797
798 for (; argc; argc--, argv++)
799 {
800 if (!strcmp (*argv, "--display"))
801 {
802 if (argv[1] && !remember_display)
803 {
804 remember_display = strdup (argv[1]);
805 if (!remember_display)
806 {
807 #ifndef HAVE_W32CE_SYSTEM
808 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
809 #endif
810 exit (EXIT_FAILURE);
811 }
812 }
813 found = 1;
814 break;
815 }
816 else if (!strncmp (*argv, "--display=", 10))
817 {
818 if (!remember_display)
819 {
820 remember_display = strdup (*argv+10);
821 if (!remember_display)
822 {
823 #ifndef HAVE_W32CE_SYSTEM
824 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
825 #endif
826 exit (EXIT_FAILURE);
827 }
828 }
829 found = 1;
830 break;
831 }
832 }
833
834 #ifndef HAVE_W32CE_SYSTEM
835 {
836 const char *s;
837 s = getenv ("DISPLAY");
838 if (s && *s)
839 found = 1;
840 }
841 #endif
842
843 return found;
844 }
845
846
847
848 /* Print usage information and and provide strings for help. */
849 static const char *
my_strusage(int level)850 my_strusage( int level )
851 {
852 const char *p;
853
854 switch (level)
855 {
856 case 11: p = this_pgmname; break;
857 case 12: p = "pinentry"; break;
858 case 13: p = PACKAGE_VERSION; break;
859 case 14: p = "Copyright (C) 2016 g10 Code GmbH"; break;
860 case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
861 case 1:
862 case 40:
863 {
864 static char *str;
865
866 if (!str)
867 {
868 size_t n = 50 + strlen (this_pgmname);
869 str = malloc (n);
870 if (str)
871 {
872 snprintf (str, n, "Usage: %s [options] (-h for help)",
873 this_pgmname);
874 str[n-1] = 0;
875 }
876 }
877 p = str;
878 }
879 break;
880 case 41:
881 p = "Ask securely for a secret and print it to stdout.";
882 break;
883
884 case 42:
885 p = "1"; /* Flag print 40 as part of 41. */
886 break;
887
888 default: p = NULL; break;
889 }
890 return p;
891 }
892
893
894 char *
parse_color(char * arg,pinentry_color_t * color_p,int * bright_p)895 parse_color (char *arg, pinentry_color_t *color_p, int *bright_p)
896 {
897 static struct
898 {
899 const char *name;
900 pinentry_color_t color;
901 } colors[] = { { "none", PINENTRY_COLOR_NONE },
902 { "default", PINENTRY_COLOR_DEFAULT },
903 { "black", PINENTRY_COLOR_BLACK },
904 { "red", PINENTRY_COLOR_RED },
905 { "green", PINENTRY_COLOR_GREEN },
906 { "yellow", PINENTRY_COLOR_YELLOW },
907 { "blue", PINENTRY_COLOR_BLUE },
908 { "magenta", PINENTRY_COLOR_MAGENTA },
909 { "cyan", PINENTRY_COLOR_CYAN },
910 { "white", PINENTRY_COLOR_WHITE } };
911
912 int i;
913 char *new_arg;
914 pinentry_color_t color = PINENTRY_COLOR_DEFAULT;
915
916 if (!arg)
917 return NULL;
918
919 new_arg = strchr (arg, ',');
920 if (new_arg)
921 new_arg++;
922
923 if (bright_p)
924 {
925 const char *bname[] = { "bright-", "bright", "bold-", "bold" };
926
927 *bright_p = 0;
928 for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++)
929 if (!strncasecmp (arg, bname[i], strlen (bname[i])))
930 {
931 *bright_p = 1;
932 arg += strlen (bname[i]);
933 }
934 }
935
936 for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++)
937 if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name)))
938 color = colors[i].color;
939
940 *color_p = color;
941 return new_arg;
942 }
943
944 /* Parse the command line options. May exit the program if only help
945 or version output is requested. */
946 void
pinentry_parse_opts(int argc,char * argv[])947 pinentry_parse_opts (int argc, char *argv[])
948 {
949 static ARGPARSE_OPTS opts[] = {
950 ARGPARSE_s_n('d', "debug", "Turn on debugging output"),
951 ARGPARSE_s_s('D', "display", "|DISPLAY|Set the X display"),
952 ARGPARSE_s_s('T', "ttyname", "|FILE|Set the tty terminal node name"),
953 ARGPARSE_s_s('N', "ttytype", "|NAME|Set the tty terminal type"),
954 ARGPARSE_s_s('C', "lc-ctype", "|STRING|Set the tty LC_CTYPE value"),
955 ARGPARSE_s_s('M', "lc-messages", "|STRING|Set the tty LC_MESSAGES value"),
956 ARGPARSE_s_i('o', "timeout",
957 "|SECS|Timeout waiting for input after this many seconds"),
958 ARGPARSE_s_n('g', "no-global-grab",
959 "Grab keyboard only while window is focused"),
960 ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"),
961 ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"),
962 ARGPARSE_s_s('a', "ttyalert", "|STRING|Set the alert mode (none, beep or flash)"),
963 ARGPARSE_end()
964 };
965 ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
966
967 set_strusage (my_strusage);
968
969 pinentry_reset (1);
970
971 while (arg_parse (&pargs, opts))
972 {
973 switch (pargs.r_opt)
974 {
975 case 'd':
976 pinentry.debug = 1;
977 break;
978 case 'g':
979 pinentry.grab = 0;
980 break;
981
982 case 'D':
983 /* Note, this is currently not used because the GUI engine
984 has already been initialized when parsing these options. */
985 pinentry.display = strdup (pargs.r.ret_str);
986 if (!pinentry.display)
987 {
988 #ifndef HAVE_W32CE_SYSTEM
989 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
990 #endif
991 exit (EXIT_FAILURE);
992 }
993 break;
994 case 'T':
995 pinentry.ttyname = strdup (pargs.r.ret_str);
996 if (!pinentry.ttyname)
997 {
998 #ifndef HAVE_W32CE_SYSTEM
999 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
1000 #endif
1001 exit (EXIT_FAILURE);
1002 }
1003 break;
1004 case 'N':
1005 pinentry.ttytype_l = strdup (pargs.r.ret_str);
1006 if (!pinentry.ttytype_l)
1007 {
1008 #ifndef HAVE_W32CE_SYSTEM
1009 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
1010 #endif
1011 exit (EXIT_FAILURE);
1012 }
1013 break;
1014 case 'C':
1015 pinentry.lc_ctype = strdup (pargs.r.ret_str);
1016 if (!pinentry.lc_ctype)
1017 {
1018 #ifndef HAVE_W32CE_SYSTEM
1019 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
1020 #endif
1021 exit (EXIT_FAILURE);
1022 }
1023 break;
1024 case 'M':
1025 pinentry.lc_messages = strdup (pargs.r.ret_str);
1026 if (!pinentry.lc_messages)
1027 {
1028 #ifndef HAVE_W32CE_SYSTEM
1029 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
1030 #endif
1031 exit (EXIT_FAILURE);
1032 }
1033 break;
1034 case 'W':
1035 pinentry.parent_wid = pargs.r.ret_ulong;
1036 break;
1037
1038 case 'c':
1039 {
1040 char *tmpstr = pargs.r.ret_str;
1041
1042 tmpstr = parse_color (tmpstr, &pinentry.color_fg,
1043 &pinentry.color_fg_bright);
1044 tmpstr = parse_color (tmpstr, &pinentry.color_bg, NULL);
1045 tmpstr = parse_color (tmpstr, &pinentry.color_so,
1046 &pinentry.color_so_bright);
1047 }
1048 break;
1049
1050 case 'o':
1051 pinentry.timeout = pargs.r.ret_int;
1052 break;
1053
1054 case 'a':
1055 pinentry.ttyalert = strdup (pargs.r.ret_str);
1056 if (!pinentry.ttyalert)
1057 {
1058 #ifndef HAVE_W32CE_SYSTEM
1059 fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
1060 #endif
1061 exit (EXIT_FAILURE);
1062 }
1063 break;
1064
1065 default:
1066 pargs.err = ARGPARSE_PRINT_WARNING;
1067 break;
1068 }
1069 }
1070
1071 if (!pinentry.display && remember_display)
1072 {
1073 pinentry.display = remember_display;
1074 remember_display = NULL;
1075 }
1076 }
1077
1078
1079 /* Set the optional flag used with getinfo. */
1080 void
pinentry_set_flavor_flag(const char * string)1081 pinentry_set_flavor_flag (const char *string)
1082 {
1083 flavor_flag = string;
1084 }
1085
1086
1087
1088
1089 static gpg_error_t
option_handler(assuan_context_t ctx,const char * key,const char * value)1090 option_handler (assuan_context_t ctx, const char *key, const char *value)
1091 {
1092 (void)ctx;
1093
1094 if (!strcmp (key, "no-grab") && !*value)
1095 pinentry.grab = 0;
1096 else if (!strcmp (key, "grab") && !*value)
1097 pinentry.grab = 1;
1098 else if (!strcmp (key, "debug-wait"))
1099 {
1100 #ifndef HAVE_W32_SYSTEM
1101 fprintf (stderr, "%s: waiting for debugger - my pid is %u ...\n",
1102 this_pgmname, (unsigned int) getpid());
1103 sleep (*value?atoi (value):5);
1104 fprintf (stderr, "%s: ... okay\n", this_pgmname);
1105 #endif
1106 }
1107 else if (!strcmp (key, "display"))
1108 {
1109 if (pinentry.display)
1110 free (pinentry.display);
1111 pinentry.display = strdup (value);
1112 if (!pinentry.display)
1113 return gpg_error_from_syserror ();
1114 }
1115 else if (!strcmp (key, "ttyname"))
1116 {
1117 if (pinentry.ttyname)
1118 free (pinentry.ttyname);
1119 pinentry.ttyname = strdup (value);
1120 if (!pinentry.ttyname)
1121 return gpg_error_from_syserror ();
1122 }
1123 else if (!strcmp (key, "ttytype"))
1124 {
1125 if (pinentry.ttytype_l)
1126 free (pinentry.ttytype_l);
1127 pinentry.ttytype_l = strdup (value);
1128 if (!pinentry.ttytype_l)
1129 return gpg_error_from_syserror ();
1130 }
1131 else if (!strcmp (key, "ttyalert"))
1132 {
1133 if (pinentry.ttyalert)
1134 free (pinentry.ttyalert);
1135 pinentry.ttyalert = strdup (value);
1136 if (!pinentry.ttyalert)
1137 return gpg_error_from_syserror ();
1138 }
1139 else if (!strcmp (key, "lc-ctype"))
1140 {
1141 if (pinentry.lc_ctype)
1142 free (pinentry.lc_ctype);
1143 pinentry.lc_ctype = strdup (value);
1144 if (!pinentry.lc_ctype)
1145 return gpg_error_from_syserror ();
1146 }
1147 else if (!strcmp (key, "lc-messages"))
1148 {
1149 if (pinentry.lc_messages)
1150 free (pinentry.lc_messages);
1151 pinentry.lc_messages = strdup (value);
1152 if (!pinentry.lc_messages)
1153 return gpg_error_from_syserror ();
1154 }
1155 else if (!strcmp (key, "owner"))
1156 {
1157 long along;
1158 char *endp;
1159
1160 free (pinentry.owner_host);
1161 pinentry.owner_host = NULL;
1162 pinentry.owner_uid = -1;
1163 pinentry.owner_pid = 0;
1164
1165 errno = 0;
1166 along = strtol (value, &endp, 10);
1167 if (along && !errno)
1168 {
1169 pinentry.owner_pid = (unsigned long)along;
1170 if (*endp)
1171 {
1172 errno = 0;
1173 if (*endp == '/') { /* we have a uid */
1174 endp++;
1175 along = strtol (endp, &endp, 10);
1176 if (along >= 0 && !errno)
1177 pinentry.owner_uid = (int)along;
1178 }
1179 if (endp)
1180 {
1181 while (*endp == ' ')
1182 endp++;
1183 if (*endp)
1184 {
1185 pinentry.owner_host = strdup (endp);
1186 for (endp=pinentry.owner_host;
1187 *endp && *endp != ' '; endp++)
1188 ;
1189 *endp = 0;
1190 }
1191 }
1192 }
1193 }
1194 }
1195 else if (!strcmp (key, "parent-wid"))
1196 {
1197 pinentry.parent_wid = atoi (value);
1198 /* FIXME: Use strtol and add some error handling. */
1199 }
1200 else if (!strcmp (key, "touch-file"))
1201 {
1202 if (pinentry.touch_file)
1203 free (pinentry.touch_file);
1204 pinentry.touch_file = strdup (value);
1205 if (!pinentry.touch_file)
1206 return gpg_error_from_syserror ();
1207 }
1208 else if (!strcmp (key, "default-ok"))
1209 {
1210 pinentry.default_ok = strdup (value);
1211 if (!pinentry.default_ok)
1212 return gpg_error_from_syserror ();
1213 }
1214 else if (!strcmp (key, "default-cancel"))
1215 {
1216 pinentry.default_cancel = strdup (value);
1217 if (!pinentry.default_cancel)
1218 return gpg_error_from_syserror ();
1219 }
1220 else if (!strcmp (key, "default-prompt"))
1221 {
1222 pinentry.default_prompt = strdup (value);
1223 if (!pinentry.default_prompt)
1224 return gpg_error_from_syserror ();
1225 }
1226 else if (!strcmp (key, "default-pwmngr"))
1227 {
1228 pinentry.default_pwmngr = strdup (value);
1229 if (!pinentry.default_pwmngr)
1230 return gpg_error_from_syserror ();
1231 }
1232 else if (!strcmp (key, "default-cf-visi"))
1233 {
1234 pinentry.default_cf_visi = strdup (value);
1235 if (!pinentry.default_cf_visi)
1236 return gpg_error_from_syserror ();
1237 }
1238 else if (!strcmp (key, "default-tt-visi"))
1239 {
1240 pinentry.default_tt_visi = strdup (value);
1241 if (!pinentry.default_tt_visi)
1242 return gpg_error_from_syserror ();
1243 }
1244 else if (!strcmp (key, "default-tt-hide"))
1245 {
1246 pinentry.default_tt_hide = strdup (value);
1247 if (!pinentry.default_tt_hide)
1248 return gpg_error_from_syserror ();
1249 }
1250 else if (!strcmp (key, "allow-external-password-cache") && !*value)
1251 {
1252 pinentry.allow_external_password_cache = 1;
1253 pinentry.tried_password_cache = 0;
1254 }
1255 else if (!strcmp (key, "allow-emacs-prompt") && !*value)
1256 {
1257 #ifdef INSIDE_EMACS
1258 pinentry_enable_emacs_cmd_handler ();
1259 #endif
1260 }
1261 else if (!strcmp (key, "invisible-char"))
1262 {
1263 if (pinentry.invisible_char)
1264 free (pinentry.invisible_char);
1265 pinentry.invisible_char = strdup (value);
1266 if (!pinentry.invisible_char)
1267 return gpg_error_from_syserror ();
1268 }
1269 else
1270 return gpg_error (GPG_ERR_UNKNOWN_OPTION);
1271 return 0;
1272 }
1273
1274
1275 /* Note, that it is sufficient to allocate the target string D as
1276 long as the source string S, i.e.: strlen(s)+1; */
1277 static void
strcpy_escaped(char * d,const char * s)1278 strcpy_escaped (char *d, const char *s)
1279 {
1280 while (*s)
1281 {
1282 if (*s == '%' && s[1] && s[2])
1283 {
1284 s++;
1285 *d++ = xtoi_2 ( s);
1286 s += 2;
1287 }
1288 else
1289 *d++ = *s++;
1290 }
1291 *d = 0;
1292 }
1293
1294
1295 static void
write_status_error(assuan_context_t ctx,pinentry_t pe)1296 write_status_error (assuan_context_t ctx, pinentry_t pe)
1297 {
1298 char buf[500];
1299 const char *pgm;
1300
1301 pgm = strchr (this_pgmname, '-');
1302 if (pgm && pgm[1])
1303 pgm++;
1304 else
1305 pgm = this_pgmname;
1306
1307 snprintf (buf, sizeof buf, "%s.%s %d %s",
1308 pgm,
1309 pe->specific_err_loc? pe->specific_err_loc : "?",
1310 pe->specific_err,
1311 pe->specific_err_info? pe->specific_err_info : "");
1312 buf[sizeof buf -1] = 0;
1313 assuan_write_status (ctx, "ERROR", buf);
1314 }
1315
1316
1317 static gpg_error_t
cmd_setdesc(assuan_context_t ctx,char * line)1318 cmd_setdesc (assuan_context_t ctx, char *line)
1319 {
1320 char *newd;
1321
1322 (void)ctx;
1323
1324 newd = malloc (strlen (line) + 1);
1325 if (!newd)
1326 return gpg_error_from_syserror ();
1327
1328 strcpy_escaped (newd, line);
1329 if (pinentry.description)
1330 free (pinentry.description);
1331 pinentry.description = newd;
1332 return 0;
1333 }
1334
1335
1336 static gpg_error_t
cmd_setprompt(assuan_context_t ctx,char * line)1337 cmd_setprompt (assuan_context_t ctx, char *line)
1338 {
1339 char *newp;
1340
1341 (void)ctx;
1342
1343 newp = malloc (strlen (line) + 1);
1344 if (!newp)
1345 return gpg_error_from_syserror ();
1346
1347 strcpy_escaped (newp, line);
1348 if (pinentry.prompt)
1349 free (pinentry.prompt);
1350 pinentry.prompt = newp;
1351 return 0;
1352 }
1353
1354
1355 /* The data provided at LINE may be used by pinentry implementations
1356 to identify a key for caching strategies of its own. The empty
1357 string and --clear mean that the key does not have a stable
1358 identifier. */
1359 static gpg_error_t
cmd_setkeyinfo(assuan_context_t ctx,char * line)1360 cmd_setkeyinfo (assuan_context_t ctx, char *line)
1361 {
1362 (void)ctx;
1363
1364 if (pinentry.keyinfo)
1365 free (pinentry.keyinfo);
1366
1367 if (*line && strcmp(line, "--clear") != 0)
1368 pinentry.keyinfo = strdup (line);
1369 else
1370 pinentry.keyinfo = NULL;
1371
1372 return 0;
1373 }
1374
1375
1376 static gpg_error_t
cmd_setrepeat(assuan_context_t ctx,char * line)1377 cmd_setrepeat (assuan_context_t ctx, char *line)
1378 {
1379 char *p;
1380
1381 (void)ctx;
1382
1383 p = malloc (strlen (line) + 1);
1384 if (!p)
1385 return gpg_error_from_syserror ();
1386
1387 strcpy_escaped (p, line);
1388 free (pinentry.repeat_passphrase);
1389 pinentry.repeat_passphrase = p;
1390 return 0;
1391 }
1392
1393
1394 static gpg_error_t
cmd_setrepeaterror(assuan_context_t ctx,char * line)1395 cmd_setrepeaterror (assuan_context_t ctx, char *line)
1396 {
1397 char *p;
1398
1399 (void)ctx;
1400
1401 p = malloc (strlen (line) + 1);
1402 if (!p)
1403 return gpg_error_from_syserror ();
1404
1405 strcpy_escaped (p, line);
1406 free (pinentry.repeat_error_string);
1407 pinentry.repeat_error_string = p;
1408 return 0;
1409 }
1410
1411
1412 static gpg_error_t
cmd_seterror(assuan_context_t ctx,char * line)1413 cmd_seterror (assuan_context_t ctx, char *line)
1414 {
1415 char *newe;
1416
1417 (void)ctx;
1418
1419 newe = malloc (strlen (line) + 1);
1420 if (!newe)
1421 return gpg_error_from_syserror ();
1422
1423 strcpy_escaped (newe, line);
1424 if (pinentry.error)
1425 free (pinentry.error);
1426 pinentry.error = newe;
1427 return 0;
1428 }
1429
1430
1431 static gpg_error_t
cmd_setok(assuan_context_t ctx,char * line)1432 cmd_setok (assuan_context_t ctx, char *line)
1433 {
1434 char *newo;
1435
1436 (void)ctx;
1437
1438 newo = malloc (strlen (line) + 1);
1439 if (!newo)
1440 return gpg_error_from_syserror ();
1441
1442 strcpy_escaped (newo, line);
1443 if (pinentry.ok)
1444 free (pinentry.ok);
1445 pinentry.ok = newo;
1446 return 0;
1447 }
1448
1449
1450 static gpg_error_t
cmd_setnotok(assuan_context_t ctx,char * line)1451 cmd_setnotok (assuan_context_t ctx, char *line)
1452 {
1453 char *newo;
1454
1455 (void)ctx;
1456
1457 newo = malloc (strlen (line) + 1);
1458 if (!newo)
1459 return gpg_error_from_syserror ();
1460
1461 strcpy_escaped (newo, line);
1462 if (pinentry.notok)
1463 free (pinentry.notok);
1464 pinentry.notok = newo;
1465 return 0;
1466 }
1467
1468
1469 static gpg_error_t
cmd_setcancel(assuan_context_t ctx,char * line)1470 cmd_setcancel (assuan_context_t ctx, char *line)
1471 {
1472 char *newc;
1473
1474 (void)ctx;
1475
1476 newc = malloc (strlen (line) + 1);
1477 if (!newc)
1478 return gpg_error_from_syserror ();
1479
1480 strcpy_escaped (newc, line);
1481 if (pinentry.cancel)
1482 free (pinentry.cancel);
1483 pinentry.cancel = newc;
1484 return 0;
1485 }
1486
1487
1488 static gpg_error_t
cmd_settimeout(assuan_context_t ctx,char * line)1489 cmd_settimeout (assuan_context_t ctx, char *line)
1490 {
1491 (void)ctx;
1492
1493 if (line && *line)
1494 pinentry.timeout = atoi (line);
1495
1496 return 0;
1497 }
1498
1499 static gpg_error_t
cmd_settitle(assuan_context_t ctx,char * line)1500 cmd_settitle (assuan_context_t ctx, char *line)
1501 {
1502 char *newt;
1503
1504 (void)ctx;
1505
1506 newt = malloc (strlen (line) + 1);
1507 if (!newt)
1508 return gpg_error_from_syserror ();
1509
1510 strcpy_escaped (newt, line);
1511 if (pinentry.title)
1512 free (pinentry.title);
1513 pinentry.title = newt;
1514 return 0;
1515 }
1516
1517 static gpg_error_t
cmd_setqualitybar(assuan_context_t ctx,char * line)1518 cmd_setqualitybar (assuan_context_t ctx, char *line)
1519 {
1520 char *newval;
1521
1522 (void)ctx;
1523
1524 if (!*line)
1525 line = "Quality:";
1526
1527 newval = malloc (strlen (line) + 1);
1528 if (!newval)
1529 return gpg_error_from_syserror ();
1530
1531 strcpy_escaped (newval, line);
1532 if (pinentry.quality_bar)
1533 free (pinentry.quality_bar);
1534 pinentry.quality_bar = newval;
1535 return 0;
1536 }
1537
1538 /* Set the tooltip to be used for a quality bar. */
1539 static gpg_error_t
cmd_setqualitybar_tt(assuan_context_t ctx,char * line)1540 cmd_setqualitybar_tt (assuan_context_t ctx, char *line)
1541 {
1542 char *newval;
1543
1544 (void)ctx;
1545
1546 if (*line)
1547 {
1548 newval = malloc (strlen (line) + 1);
1549 if (!newval)
1550 return gpg_error_from_syserror ();
1551
1552 strcpy_escaped (newval, line);
1553 }
1554 else
1555 newval = NULL;
1556 if (pinentry.quality_bar_tt)
1557 free (pinentry.quality_bar_tt);
1558 pinentry.quality_bar_tt = newval;
1559 return 0;
1560 }
1561
1562 /* Set the tooltip to be used for a generate action. */
1563 static gpg_error_t
cmd_setgenpin_tt(assuan_context_t ctx,char * line)1564 cmd_setgenpin_tt (assuan_context_t ctx, char *line)
1565 {
1566 char *newval;
1567
1568 (void)ctx;
1569
1570 if (*line)
1571 {
1572 newval = malloc (strlen (line) + 1);
1573 if (!newval)
1574 return gpg_error_from_syserror ();
1575
1576 strcpy_escaped (newval, line);
1577 }
1578 else
1579 newval = NULL;
1580 if (pinentry.genpin_tt)
1581 free (pinentry.genpin_tt);
1582 pinentry.genpin_tt = newval;
1583 return 0;
1584 }
1585
1586 /* Set the label to be used for a generate action. */
1587 static gpg_error_t
cmd_setgenpin_label(assuan_context_t ctx,char * line)1588 cmd_setgenpin_label (assuan_context_t ctx, char *line)
1589 {
1590 char *newval;
1591
1592 (void)ctx;
1593
1594 if (*line)
1595 {
1596 newval = malloc (strlen (line) + 1);
1597 if (!newval)
1598 return gpg_error_from_syserror ();
1599
1600 strcpy_escaped (newval, line);
1601 }
1602 else
1603 newval = NULL;
1604 if (pinentry.genpin_label)
1605 free (pinentry.genpin_label);
1606 pinentry.genpin_label = newval;
1607 return 0;
1608 }
1609
1610 static gpg_error_t
cmd_getpin(assuan_context_t ctx,char * line)1611 cmd_getpin (assuan_context_t ctx, char *line)
1612 {
1613 int result;
1614 int set_prompt = 0;
1615 int just_read_password_from_cache = 0;
1616
1617 (void)line;
1618
1619 pinentry_setbuffer_init (&pinentry);
1620 if (!pinentry.pin)
1621 return gpg_error (GPG_ERR_ENOMEM);
1622
1623 /* Try reading from the password cache. */
1624 if (/* If repeat passphrase is set, then we don't want to read from
1625 the cache. */
1626 ! pinentry.repeat_passphrase
1627 /* Are we allowed to read from the cache? */
1628 && pinentry.allow_external_password_cache
1629 && pinentry.keyinfo
1630 /* Only read from the cache if we haven't already tried it. */
1631 && ! pinentry.tried_password_cache
1632 /* If the last read resulted in an error, then don't read from
1633 the cache. */
1634 && ! pinentry.error)
1635 {
1636 char *password;
1637 int give_up_on_password_store = 0;
1638
1639 pinentry.tried_password_cache = 1;
1640
1641 password = password_cache_lookup (pinentry.keyinfo, &give_up_on_password_store);
1642 if (give_up_on_password_store)
1643 pinentry.allow_external_password_cache = 0;
1644
1645 if (password)
1646 /* There is a cached password. Try it. */
1647 {
1648 int len = strlen(password) + 1;
1649 if (len > pinentry.pin_len)
1650 len = pinentry.pin_len;
1651
1652 memcpy (pinentry.pin, password, len);
1653 pinentry.pin[len] = '\0';
1654
1655 secmem_free (password);
1656
1657 pinentry.pin_from_cache = 1;
1658
1659 assuan_write_status (ctx, "PASSWORD_FROM_CACHE", "");
1660
1661 /* Result is the length of the password not including the
1662 NUL terminator. */
1663 result = len - 1;
1664
1665 just_read_password_from_cache = 1;
1666
1667 goto out;
1668 }
1669 }
1670
1671 /* The password was not cached (or we are not allowed to / cannot
1672 use the cache). Prompt the user. */
1673 pinentry.pin_from_cache = 0;
1674
1675 if (!pinentry.prompt)
1676 {
1677 pinentry.prompt = pinentry.default_prompt?pinentry.default_prompt:"PIN:";
1678 set_prompt = 1;
1679 }
1680 pinentry.locale_err = 0;
1681 pinentry.specific_err = 0;
1682 pinentry.specific_err_loc = NULL;
1683 free (pinentry.specific_err_info);
1684 pinentry.specific_err_info = NULL;
1685 pinentry.close_button = 0;
1686 pinentry.repeat_okay = 0;
1687 pinentry.one_button = 0;
1688 pinentry.ctx_assuan = ctx;
1689 result = (*pinentry_cmd_handler) (&pinentry);
1690 pinentry.ctx_assuan = NULL;
1691 if (pinentry.error)
1692 {
1693 free (pinentry.error);
1694 pinentry.error = NULL;
1695 }
1696 if (pinentry.repeat_passphrase)
1697 {
1698 free (pinentry.repeat_passphrase);
1699 pinentry.repeat_passphrase = NULL;
1700 }
1701 if (set_prompt)
1702 pinentry.prompt = NULL;
1703
1704 pinentry.quality_bar = 0; /* Reset it after the command. */
1705
1706 if (pinentry.close_button)
1707 assuan_write_status (ctx, "BUTTON_INFO", "close");
1708
1709 if (result < 0)
1710 {
1711 pinentry_setbuffer_clear (&pinentry);
1712 if (pinentry.specific_err)
1713 {
1714 write_status_error (ctx, &pinentry);
1715
1716 if (gpg_err_code (pinentry.specific_err) == GPG_ERR_FULLY_CANCELED)
1717 assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
1718
1719 return pinentry.specific_err;
1720 }
1721 return (pinentry.locale_err
1722 ? gpg_error (GPG_ERR_LOCALE_PROBLEM)
1723 : gpg_error (GPG_ERR_CANCELED));
1724 }
1725
1726 out:
1727 if (result)
1728 {
1729 if (pinentry.repeat_okay)
1730 assuan_write_status (ctx, "PIN_REPEATED", "");
1731 result = assuan_send_data (ctx, pinentry.pin, strlen(pinentry.pin));
1732 if (!result)
1733 result = assuan_send_data (ctx, NULL, 0);
1734
1735 if (/* GPG Agent says it's okay. */
1736 pinentry.allow_external_password_cache && pinentry.keyinfo
1737 /* We didn't just read it from the cache. */
1738 && ! just_read_password_from_cache
1739 /* And the user said it's okay. */
1740 && pinentry.may_cache_password)
1741 /* Cache the password. */
1742 password_cache_save (pinentry.keyinfo, pinentry.pin);
1743 }
1744
1745 pinentry_setbuffer_clear (&pinentry);
1746
1747 return result;
1748 }
1749
1750
1751 /* Note that the option --one-button is a hack to allow the use of old
1752 pinentries while the caller is ignoring the result. Given that
1753 options have never been used or flagged as an error the new option
1754 is an easy way to enable the messsage mode while not requiring to
1755 update pinentry or to have the caller test for the message
1756 command. New applications which are free to require an updated
1757 pinentry should use MESSAGE instead. */
1758 static gpg_error_t
cmd_confirm(assuan_context_t ctx,char * line)1759 cmd_confirm (assuan_context_t ctx, char *line)
1760 {
1761 int result;
1762
1763 pinentry.one_button = !!strstr (line, "--one-button");
1764 pinentry.quality_bar = 0;
1765 pinentry.close_button = 0;
1766 pinentry.locale_err = 0;
1767 pinentry.specific_err = 0;
1768 pinentry.specific_err_loc = NULL;
1769 free (pinentry.specific_err_info);
1770 pinentry.specific_err_info = NULL;
1771 pinentry.canceled = 0;
1772 pinentry_setbuffer_clear (&pinentry);
1773 result = (*pinentry_cmd_handler) (&pinentry);
1774 if (pinentry.error)
1775 {
1776 free (pinentry.error);
1777 pinentry.error = NULL;
1778 }
1779
1780 if (pinentry.close_button)
1781 assuan_write_status (ctx, "BUTTON_INFO", "close");
1782
1783 if (result > 0)
1784 return 0; /* OK */
1785
1786 if (pinentry.specific_err)
1787 {
1788 write_status_error (ctx, &pinentry);
1789
1790 if (gpg_err_code (pinentry.specific_err) == GPG_ERR_FULLY_CANCELED)
1791 assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
1792
1793 return pinentry.specific_err;
1794 }
1795
1796 if (pinentry.locale_err)
1797 return gpg_error (GPG_ERR_LOCALE_PROBLEM);
1798
1799 if (pinentry.one_button)
1800 return 0; /* OK */
1801
1802 if (pinentry.canceled)
1803 return gpg_error (GPG_ERR_CANCELED);
1804 return gpg_error (GPG_ERR_NOT_CONFIRMED);
1805 }
1806
1807
1808 static gpg_error_t
cmd_message(assuan_context_t ctx,char * line)1809 cmd_message (assuan_context_t ctx, char *line)
1810 {
1811 (void)line;
1812
1813 return cmd_confirm (ctx, "--one-button");
1814 }
1815
1816
1817 /* Return a staically allocated string with information on the mode,
1818 * uid, and gid of DEVICE. On error "?" is returned if DEVICE is
1819 * NULL, "-" is returned. */
1820 static const char *
device_stat_string(const char * device)1821 device_stat_string (const char *device)
1822 {
1823 #ifdef HAVE_STAT
1824 static char buf[40];
1825 struct stat st;
1826
1827 if (!device || !*device)
1828 return "-";
1829
1830 if (stat (device, &st))
1831 return "?"; /* Error */
1832 snprintf (buf, sizeof buf, "%lo/%lu/%lu",
1833 (unsigned long)st.st_mode,
1834 (unsigned long)st.st_uid,
1835 (unsigned long)st.st_gid);
1836 return buf;
1837 #else
1838 return "-";
1839 #endif
1840 }
1841
1842
1843 /* GETINFO <what>
1844
1845 Multipurpose function to return a variety of information.
1846 Supported values for WHAT are:
1847
1848 version - Return the version of the program.
1849 pid - Return the process id of the server.
1850 flavor - Return information about the used pinentry flavor
1851 ttyinfo - Return DISPLAY, ttyinfo and an emacs pinentry status
1852 */
1853 static gpg_error_t
cmd_getinfo(assuan_context_t ctx,char * line)1854 cmd_getinfo (assuan_context_t ctx, char *line)
1855 {
1856 int rc;
1857 const char *s;
1858 char buffer[150];
1859
1860 if (!strcmp (line, "version"))
1861 {
1862 s = VERSION;
1863 rc = assuan_send_data (ctx, s, strlen (s));
1864 }
1865 else if (!strcmp (line, "pid"))
1866 {
1867
1868 snprintf (buffer, sizeof buffer, "%lu", (unsigned long)getpid ());
1869 buffer[sizeof buffer -1] = 0;
1870 rc = assuan_send_data (ctx, buffer, strlen (buffer));
1871 }
1872 else if (!strcmp (line, "flavor"))
1873 {
1874 if (!strncmp (this_pgmname, "pinentry-", 9) && this_pgmname[9])
1875 s = this_pgmname + 9;
1876 else
1877 s = this_pgmname;
1878
1879 snprintf (buffer, sizeof buffer, "%s%s%s",
1880 s,
1881 flavor_flag? ":":"",
1882 flavor_flag? flavor_flag : "");
1883 buffer[sizeof buffer -1] = 0;
1884 rc = assuan_send_data (ctx, buffer, strlen (buffer));
1885 /* if (!rc) */
1886 /* rc = assuan_write_status (ctx, "FEATURES", "tabbing foo bar"); */
1887 }
1888 else if (!strcmp (line, "ttyinfo"))
1889 {
1890 char emacs_status[10];
1891 #ifdef INSIDE_EMACS
1892 snprintf (emacs_status, sizeof emacs_status,
1893 "%d", pinentry_emacs_status ());
1894 #else
1895 strcpy (emacs_status, "-");
1896 #endif
1897 snprintf (buffer, sizeof buffer, "%s %s %s %s %lu/%lu %s",
1898 pinentry.ttyname? pinentry.ttyname : "-",
1899 pinentry.ttytype_l? pinentry.ttytype_l : "-",
1900 pinentry.display? pinentry.display : "-",
1901 device_stat_string (pinentry.ttyname),
1902 #ifdef HAVE_DOSISH_SYSTEM
1903 0l, 0l,
1904 #else
1905 (unsigned long)geteuid (), (unsigned long)getegid (),
1906 #endif
1907 emacs_status
1908 );
1909 buffer[sizeof buffer -1] = 0;
1910 rc = assuan_send_data (ctx, buffer, strlen (buffer));
1911 }
1912 else
1913 rc = gpg_error (GPG_ERR_ASS_PARAMETER);
1914 return rc;
1915 }
1916
1917 /* CLEARPASSPHRASE <cacheid>
1918
1919 Clear the cache passphrase associated with the key identified by
1920 cacheid.
1921 */
1922 static gpg_error_t
cmd_clear_passphrase(assuan_context_t ctx,char * line)1923 cmd_clear_passphrase (assuan_context_t ctx, char *line)
1924 {
1925 (void)ctx;
1926
1927 if (! line)
1928 return gpg_error (GPG_ERR_ASS_INV_VALUE);
1929
1930 /* Remove leading and trailing white space. */
1931 while (*line == ' ')
1932 line ++;
1933 while (line[strlen (line) - 1] == ' ')
1934 line[strlen (line) - 1] = 0;
1935
1936 switch (password_cache_clear (line))
1937 {
1938 case 1: return 0;
1939 case 0: return gpg_error (GPG_ERR_ASS_INV_VALUE);
1940 default: return gpg_error (GPG_ERR_ASS_GENERAL);
1941 }
1942 }
1943
1944 /* Tell the assuan library about our commands. */
1945 static gpg_error_t
register_commands(assuan_context_t ctx)1946 register_commands (assuan_context_t ctx)
1947 {
1948 static struct
1949 {
1950 const char *name;
1951 gpg_error_t (*handler) (assuan_context_t, char *line);
1952 } table[] =
1953 {
1954 { "SETDESC", cmd_setdesc },
1955 { "SETPROMPT", cmd_setprompt },
1956 { "SETKEYINFO", cmd_setkeyinfo },
1957 { "SETREPEAT", cmd_setrepeat },
1958 { "SETREPEATERROR", cmd_setrepeaterror },
1959 { "SETERROR", cmd_seterror },
1960 { "SETOK", cmd_setok },
1961 { "SETNOTOK", cmd_setnotok },
1962 { "SETCANCEL", cmd_setcancel },
1963 { "GETPIN", cmd_getpin },
1964 { "CONFIRM", cmd_confirm },
1965 { "MESSAGE", cmd_message },
1966 { "SETQUALITYBAR", cmd_setqualitybar },
1967 { "SETQUALITYBAR_TT", cmd_setqualitybar_tt },
1968 { "SETGENPIN", cmd_setgenpin_label },
1969 { "SETGENPIN_TT", cmd_setgenpin_tt },
1970 { "GETINFO", cmd_getinfo },
1971 { "SETTITLE", cmd_settitle },
1972 { "SETTIMEOUT", cmd_settimeout },
1973 { "CLEARPASSPHRASE", cmd_clear_passphrase },
1974 { NULL }
1975 };
1976 int i, j;
1977 gpg_error_t rc;
1978
1979 for (i = j = 0; table[i].name; i++)
1980 {
1981 rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL);
1982 if (rc)
1983 return rc;
1984 }
1985 return 0;
1986 }
1987
1988
1989 int
pinentry_loop2(int infd,int outfd)1990 pinentry_loop2 (int infd, int outfd)
1991 {
1992 gpg_error_t rc;
1993 assuan_fd_t filedes[2];
1994 assuan_context_t ctx;
1995
1996 /* Extra check to make sure we have dropped privs. */
1997 #ifndef HAVE_DOSISH_SYSTEM
1998 if (getuid() != geteuid())
1999 abort ();
2000 #endif
2001
2002 rc = assuan_new (&ctx);
2003 if (rc)
2004 {
2005 fprintf (stderr, "server context creation failed: %s\n",
2006 gpg_strerror (rc));
2007 return -1;
2008 }
2009
2010 /* For now we use a simple pipe based server so that we can work
2011 from scripts. We will later add options to run as a daemon and
2012 wait for requests on a Unix domain socket. */
2013 filedes[0] = assuan_fdopen (infd);
2014 filedes[1] = assuan_fdopen (outfd);
2015 rc = assuan_init_pipe_server (ctx, filedes);
2016 if (rc)
2017 {
2018 fprintf (stderr, "%s: failed to initialize the server: %s\n",
2019 this_pgmname, gpg_strerror (rc));
2020 return -1;
2021 }
2022 rc = register_commands (ctx);
2023 if (rc)
2024 {
2025 fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n",
2026 this_pgmname, gpg_strerror (rc));
2027 return -1;
2028 }
2029
2030 assuan_register_option_handler (ctx, option_handler);
2031 #if 0
2032 assuan_set_log_stream (ctx, stderr);
2033 #endif
2034 assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler);
2035
2036 for (;;)
2037 {
2038 rc = assuan_accept (ctx);
2039 if (rc == -1)
2040 break;
2041 else if (rc)
2042 {
2043 fprintf (stderr, "%s: Assuan accept problem: %s\n",
2044 this_pgmname, gpg_strerror (rc));
2045 break;
2046 }
2047
2048 rc = assuan_process (ctx);
2049 if (rc)
2050 {
2051 fprintf (stderr, "%s: Assuan processing failed: %s\n",
2052 this_pgmname, gpg_strerror (rc));
2053 continue;
2054 }
2055 }
2056
2057 assuan_release (ctx);
2058 return 0;
2059 }
2060
2061
2062 /* Start the pinentry event loop. The program will start to process
2063 Assuan commands until it is finished or an error occurs. If an
2064 error occurs, -1 is returned. Otherwise, 0 is returned. */
2065 int
pinentry_loop(void)2066 pinentry_loop (void)
2067 {
2068 return pinentry_loop2 (STDIN_FILENO, STDOUT_FILENO);
2069 }
2070