Line data Source code
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 349 : reply (const char *fmt, ...)
33 : {
34 : int result;
35 : va_list ap;
36 :
37 349 : if (log_stream)
38 : {
39 26 : fprintf (log_stream, "> ");
40 26 : va_start (ap, fmt);
41 26 : vfprintf (log_stream, fmt, ap);
42 26 : va_end (ap);
43 : }
44 349 : va_start (ap, fmt);
45 349 : result = vprintf (fmt, ap);
46 349 : va_end (ap);
47 :
48 349 : fflush (stdout);
49 349 : return result;
50 : }
51 :
52 :
53 : /* Return the first line from FNAME, removing it from the file. */
54 : static char *
55 1 : get_passphrase (const char *fname)
56 : {
57 1 : char *passphrase = NULL;
58 : size_t fname_len;
59 : char *fname_new;
60 : FILE *source, *sink;
61 : char linebuf[80];
62 :
63 1 : fname_len = strlen (fname);
64 1 : fname_new = malloc (fname_len + 5);
65 1 : if (fname_new == NULL)
66 : {
67 0 : perror ("malloc");
68 0 : exit (1);
69 : }
70 1 : snprintf (fname_new, fname_len + 5, "%s.new", fname);
71 :
72 1 : source = fopen (fname, "r");
73 1 : if (! source)
74 : {
75 0 : perror (fname);
76 0 : exit (1);
77 : }
78 :
79 1 : sink = fopen (fname_new, "w");
80 1 : if (! sink)
81 : {
82 0 : perror (fname_new);
83 0 : exit (1);
84 : }
85 :
86 3 : while (fgets (linebuf, sizeof linebuf, source))
87 : {
88 1 : linebuf[sizeof linebuf - 1] = 0;
89 1 : if (passphrase == NULL)
90 : {
91 1 : passphrase = strdup (linebuf);
92 1 : if (passphrase == NULL)
93 : {
94 0 : perror ("strdup");
95 0 : exit (1);
96 : }
97 : }
98 : else
99 0 : fputs (linebuf, sink);
100 : }
101 :
102 1 : if (ferror (source))
103 : {
104 0 : perror (fname);
105 0 : exit (1);
106 : }
107 :
108 1 : if (ferror (sink))
109 : {
110 0 : perror (fname_new);
111 0 : exit (1);
112 : }
113 :
114 1 : fclose (source);
115 1 : fclose (sink);
116 1 : if (remove (fname))
117 : {
118 0 : fprintf (stderr, "Failed to remove %s: %s",
119 0 : fname, strerror (errno));
120 0 : exit (1);
121 : }
122 :
123 1 : if (rename (fname_new, fname))
124 : {
125 0 : fprintf (stderr, "Failed to rename %s to %s: %s",
126 0 : fname, fname_new, strerror (errno));
127 0 : exit (1);
128 : }
129 1 : return passphrase;
130 : }
131 :
132 :
133 : #define whitespacep(p) (*(p) == ' ' || *(p) == '\t' \
134 : || *(p) == '\r' || *(p) == '\n')
135 :
136 : /* rstrip line. */
137 : static void
138 299 : rstrip (char *buffer)
139 : {
140 : char *p;
141 299 : if (!*buffer)
142 299 : return; /* This is to avoid p = buffer - 1 */
143 598 : for (p = buffer + strlen (buffer) - 1; p >= buffer; p--)
144 : {
145 598 : if (! whitespacep (p))
146 299 : break;
147 299 : *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 14 : skip_options (const char *line)
161 : {
162 28 : while (whitespacep (line))
163 0 : line++;
164 31 : while (*line == '-' && line[1] == '-')
165 : {
166 195 : while (*line && !whitespacep (line))
167 189 : line++;
168 7 : while (whitespacep (line))
169 1 : line++;
170 : }
171 14 : 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 26 : option_value (const char *line, const char *name)
179 : {
180 : char *s;
181 26 : int n = strlen (name);
182 :
183 26 : s = strstr (line, name);
184 26 : if (s && s >= skip_options (line))
185 0 : return NULL;
186 26 : if (s && (s == line || whitespacep (s-1))
187 2 : && s[n] && (whitespacep (s+n) || s[n] == '='))
188 : {
189 2 : s += n + 1;
190 2 : s += strspn (s, " ");
191 2 : if (*s && !whitespacep(s))
192 2 : return s;
193 : }
194 24 : return NULL;
195 : }
196 :
197 : int
198 13 : main (int argc, char **argv)
199 : {
200 : char *args;
201 13 : 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 13 : setvbuf (stdin, NULL, _IOLBF, BUFSIZ);
211 13 : setvbuf (stdout, NULL, _IOLBF, BUFSIZ);
212 :
213 13 : args = getenv ("PINENTRY_USER_DATA");
214 13 : got_environment_user_data = !!args;
215 13 : if (! args)
216 8 : args = "";
217 :
218 : restart:
219 13 : logfile = option_value (args, "--logfile");
220 13 : if (logfile)
221 : {
222 1 : char *p = logfile, more;
223 51 : while (*p && ! whitespacep (p))
224 49 : p++;
225 1 : more = !! *p;
226 1 : *p = 0;
227 1 : args = more ? p+1 : p;
228 :
229 1 : log_stream = fopen (logfile, "a");
230 1 : if (! log_stream)
231 : {
232 0 : perror (logfile);
233 0 : return 1;
234 : }
235 : }
236 :
237 13 : passphrasefile = option_value (args, "--passphrasefile");
238 13 : if (passphrasefile)
239 : {
240 1 : char *p = passphrasefile, more;
241 50 : while (*p && ! whitespacep (p))
242 48 : p++;
243 1 : more = !! *p;
244 1 : *p = 0;
245 1 : args = more ? p+1 : p;
246 :
247 1 : passphrase = get_passphrase (passphrasefile);
248 1 : if (! passphrase)
249 : {
250 0 : reply ("# Passphrasefile '%s' is empty. Terminating.\n",
251 : passphrasefile);
252 0 : return 1;
253 : }
254 :
255 1 : rstrip (passphrase);
256 : }
257 : else
258 : {
259 12 : passphrase = skip_options (args);
260 12 : if (*passphrase == 0)
261 8 : passphrase = "no PINENTRY_USER_DATA -- using default passphrase";
262 : }
263 :
264 13 : reply ("# fake-pinentry(%u) started. Passphrase='%s'.\n",
265 13 : (unsigned int)getpid (), passphrase);
266 13 : reply ("OK - what's up?\n");
267 :
268 13 : while (! feof (stdin))
269 : {
270 : char buffer[1024];
271 :
272 298 : if (fgets (buffer, sizeof buffer, stdin) == NULL)
273 13 : break;
274 :
275 298 : if (log_stream)
276 22 : fprintf (log_stream, "< %s", buffer);
277 :
278 298 : rstrip (buffer);
279 :
280 : #define OPT_USER_DATA "OPTION pinentry-user-data="
281 :
282 298 : if (strncmp (buffer, "GETPIN", 6) == 0)
283 12 : reply ("D %s\n", passphrase);
284 286 : else if (strncmp (buffer, "BYE", 3) == 0)
285 : {
286 13 : reply ("OK\n");
287 13 : break;
288 : }
289 273 : else if (strncmp (buffer, OPT_USER_DATA, strlen (OPT_USER_DATA)) == 0)
290 : {
291 5 : if (got_environment_user_data)
292 : {
293 5 : reply ("OK - I already got the data from the environment.\n");
294 5 : continue;
295 : }
296 :
297 0 : if (log_stream)
298 0 : fclose (log_stream);
299 0 : log_stream = NULL;
300 0 : free (option_user_data);
301 0 : option_user_data = args = strdup (buffer + strlen (OPT_USER_DATA));
302 0 : goto restart;
303 : }
304 :
305 280 : reply ("OK\n");
306 : }
307 :
308 : #undef OPT_USER_DATA
309 :
310 13 : reply ("# Connection terminated.\n");
311 13 : if (log_stream)
312 1 : fclose (log_stream);
313 :
314 13 : free (option_user_data);
315 13 : return 0;
316 : }
|