chiark / gitweb /
gpg: Fix searching for mail addresses in keyrings.
[gnupg2.git] / tests / openpgp / fake-pinentry.c
1 /* Fake pinentry program for the OpenPGP test suite.
2  *
3  * Copyright (C) 2016 g10 code GmbH
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27
28 static FILE *log_stream;
29
30
31 static int
32 reply (const char *fmt, ...)
33 {
34   int result;
35   va_list ap;
36
37   if (log_stream)
38     {
39       fprintf (log_stream, "> ");
40       va_start (ap, fmt);
41       vfprintf (log_stream, fmt, ap);
42       va_end (ap);
43     }
44   va_start (ap, fmt);
45   result = vprintf (fmt, ap);
46   va_end (ap);
47
48   fflush (stdout);
49   return result;
50 }
51
52
53 /* Return the first line from FNAME, removing it from the file.  */
54 static char *
55 get_passphrase (const char *fname)
56 {
57   char *passphrase = NULL;
58   size_t fname_len;
59   char *fname_new;
60   FILE *source, *sink;
61   char linebuf[80];
62
63   fname_len = strlen (fname);
64   fname_new = malloc (fname_len + 5);
65   if (fname_new == NULL)
66     {
67       perror ("malloc");
68       exit (1);
69     }
70   snprintf (fname_new, fname_len + 5, "%s.new", fname);
71
72   source = fopen (fname, "r");
73   if (! source)
74     {
75       perror (fname);
76       exit (1);
77     }
78
79   sink = fopen (fname_new, "w");
80   if (! sink)
81     {
82       perror (fname_new);
83       exit (1);
84     }
85
86   while (fgets (linebuf, sizeof linebuf, source))
87     {
88       linebuf[sizeof linebuf - 1] = 0;
89       if (passphrase == NULL)
90         {
91           passphrase = strdup (linebuf);
92           if (passphrase == NULL)
93             {
94               perror ("strdup");
95               exit (1);
96             }
97         }
98       else
99         fputs (linebuf, sink);
100     }
101
102   if (ferror (source))
103     {
104       perror (fname);
105       exit (1);
106     }
107
108   if (ferror (sink))
109     {
110       perror (fname_new);
111       exit (1);
112     }
113
114   fclose (source);
115   fclose (sink);
116   if (remove (fname))
117     {
118       fprintf (stderr, "Failed to remove %s: %s",
119                fname, strerror (errno));
120       exit (1);
121     }
122
123   if (rename (fname_new, fname))
124     {
125       fprintf (stderr, "Failed to rename %s to %s: %s",
126                fname, fname_new, strerror (errno));
127       exit (1);
128     }
129   return passphrase;
130 }
131
132 \f
133 #define whitespacep(p)   (*(p) == ' ' || *(p) == '\t' \
134                           || *(p) == '\r' || *(p) == '\n')
135
136 /* rstrip line.  */
137 static void
138 rstrip (char *buffer)
139 {
140   char *p;
141   if (!*buffer)
142     return; /* This is to avoid p = buffer - 1 */
143   for (p = buffer + strlen (buffer) - 1; p >= buffer; p--)
144     {
145       if (! whitespacep (p))
146         break;
147       *p = 0;
148     }
149 }
150
151
152 /* Skip over options in LINE.
153
154    Blanks after the options are also removed.  Options are indicated
155    by two leading dashes followed by a string consisting of non-space
156    characters.  The special option "--" indicates an explicit end of
157    options; all what follows will not be considered an option.  The
158    first no-option string also indicates the end of option parsing. */
159 char *
160 skip_options (const char *line)
161 {
162   while (whitespacep (line))
163     line++;
164   while (*line == '-' && line[1] == '-')
165     {
166       while (*line && !whitespacep (line))
167         line++;
168       while (whitespacep (line))
169         line++;
170     }
171   return (char*) line;
172 }
173
174
175 /* Return a pointer to the argument of the option with NAME.  If such
176    an option is not given, NULL is returned. */
177 char *
178 option_value (const char *line, const char *name)
179 {
180   char *s;
181   int n = strlen (name);
182
183   s = strstr (line, name);
184   if (s && s >= skip_options (line))
185     return NULL;
186   if (s && (s == line || whitespacep (s-1))
187       && s[n] && (whitespacep (s+n) || s[n] == '='))
188     {
189       s += n + 1;
190       s += strspn (s, " ");
191       if (*s && !whitespacep(s))
192         return s;
193     }
194   return NULL;
195 }
196
197 int
198 main (int argc, char **argv)
199 {
200   char *args;
201   char *option_user_data = NULL;
202   int got_environment_user_data;
203   char *logfile;
204   char *passphrasefile;
205   char *passphrase;
206
207   /* We get our options via PINENTRY_USER_DATA.  */
208   (void) argc, (void) argv;
209
210   setvbuf (stdin, NULL, _IOLBF, BUFSIZ);
211   setvbuf (stdout, NULL, _IOLBF, BUFSIZ);
212
213   args = getenv ("PINENTRY_USER_DATA");
214   got_environment_user_data = !!args;
215   if (! args)
216     args = "";
217
218  restart:
219   logfile = option_value (args, "--logfile");
220   if (logfile)
221     {
222       char *p = logfile, more;
223       while (*p && ! whitespacep (p))
224         p++;
225       more = !! *p;
226       *p = 0;
227       args = more ? p+1 : p;
228
229       log_stream = fopen (logfile, "a");
230       if (! log_stream)
231         {
232           perror (logfile);
233           return 1;
234         }
235     }
236
237   passphrasefile = option_value (args, "--passphrasefile");
238   if (passphrasefile)
239     {
240       char *p = passphrasefile, more;
241       while (*p && ! whitespacep (p))
242         p++;
243       more = !! *p;
244       *p = 0;
245       args = more ? p+1 : p;
246
247       passphrase = get_passphrase (passphrasefile);
248       if (! passphrase)
249         {
250           reply ("# Passphrasefile '%s' is empty.  Terminating.\n",
251                  passphrasefile);
252           return 1;
253         }
254
255       rstrip (passphrase);
256     }
257   else
258     {
259       passphrase = skip_options (args);
260       if (*passphrase == 0)
261         passphrase = "no PINENTRY_USER_DATA -- using default passphrase";
262     }
263
264   reply ("# fake-pinentry(%u) started.  Passphrase='%s'.\n",
265          (unsigned int)getpid (), passphrase);
266   reply ("OK - what's up?\n");
267
268   while (! feof (stdin))
269     {
270       char buffer[1024];
271
272       if (fgets (buffer, sizeof buffer, stdin) == NULL)
273         break;
274
275       if (log_stream)
276         fprintf (log_stream, "< %s", buffer);
277
278       rstrip (buffer);
279
280 #define OPT_USER_DATA   "OPTION pinentry-user-data="
281
282       if (strncmp (buffer, "GETPIN", 6) == 0)
283         reply ("D %s\n", passphrase);
284       else if (strncmp (buffer, "BYE", 3) == 0)
285         {
286           reply ("OK\n");
287           break;
288         }
289       else if (strncmp (buffer, OPT_USER_DATA, strlen (OPT_USER_DATA)) == 0)
290         {
291           if (got_environment_user_data)
292             {
293               reply ("OK - I already got the data from the environment.\n");
294               continue;
295             }
296
297           if (log_stream)
298             fclose (log_stream);
299           log_stream = NULL;
300           free (option_user_data);
301           option_user_data = args = strdup (buffer + strlen (OPT_USER_DATA));
302           goto restart;
303         }
304
305       reply ("OK\n");
306     }
307
308 #undef OPT_USER_DATA
309
310   reply ("# Connection terminated.\n");
311   if (log_stream)
312     fclose (log_stream);
313
314   free (option_user_data);
315   return 0;
316 }