Fix for the previous commit. When OPTION PINENTRY is changed, update
[pwmd.git] / src / pinentry.c
blobd1c832d4c0b561fc70b3269db2d84a65192fd9ab
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdlib.h>
20 #include <gcrypt.h>
21 #include <glib.h>
22 #include <errno.h>
23 #include <pth.h>
24 #include <pwd.h>
26 #define _ASSUAN_ONLY_GPG_ERRORS 1
27 #include <assuan.h>
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
33 #include "mem.h"
34 #include "common.h"
35 #include "pinentry.h"
36 #include "pwmd_error.h"
38 typedef struct {
39 size_t len;
40 void *buf;
41 } membuf_t;
43 static struct client_s *global_client;
44 static gint send_fd;
46 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which);
48 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
50 membuf_t *mem = (membuf_t *)data;
51 void *p;
53 if (!buffer)
54 return 0;
56 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
57 return 1;
59 mem->buf = p;
60 memcpy((char *)mem->buf + mem->len, buffer, len);
61 mem->len += len;
62 return 0;
65 static gpg_error_t assuan_command(struct pinentry_s *pin, gchar **result,
66 const gchar *cmd)
68 membuf_t data;
69 gpg_error_t rc;
71 data.len = 0;
72 data.buf = NULL;
74 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
75 NULL, NULL);
77 if (rc) {
78 if (data.buf) {
79 xfree(data.buf);
80 data.buf = NULL;
83 else {
84 if (data.buf) {
85 mem_realloc_cb(&data, "", 1);
86 *result = (gchar *)data.buf;
90 return rc;
93 static gpg_error_t set_pinentry_options(struct pinentry_s *pin)
95 gchar *display = getenv("DISPLAY");
96 gint have_display = 0;
97 gchar *tty = NULL, *ttytype = NULL;
98 gchar *opt, *val;
99 gpg_error_t rc;
100 gchar *result = NULL;
101 gchar cmd[ASSUAN_LINELENGTH];
103 if (pin->display || display)
104 have_display = 1;
105 else {
106 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
108 if (!tty)
109 return GPG_ERR_CANCELED;
112 if (!have_display && !tty)
113 return GPG_ERR_CANCELED;
115 if (!have_display) {
116 gchar *p = getenv("TERM");
118 ttytype = pin->ttytype ? pin->ttytype : p;
120 if (!ttytype)
121 return GPG_ERR_CANCELED;
124 opt = have_display ? "DISPLAY" : "TTYNAME";
125 val = have_display ? pin->display ? pin->display : display : tty;
126 snprintf(cmd, sizeof(cmd), "OPTION %s=%s", g_ascii_strdown(opt, strlen(opt)), val);
127 rc = assuan_command(pin, &result, cmd);
129 if (rc)
130 return rc;
132 if (!have_display) {
133 snprintf(cmd, sizeof(cmd), "OPTION ttytype=%s", ttytype);
134 rc = assuan_command(pin, &result, cmd);
137 return rc;
140 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
142 gpg_error_t rc;
143 assuan_context_t ctx;
144 gint child_list[] = {-1};
145 const gchar *argv[] = { "pinentry", NULL };
147 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
148 argv, child_list);
150 if (rc)
151 return rc;
153 pin->pid = assuan_get_pid(ctx);
154 pin->ctx = ctx;
155 rc = set_pinentry_options(pin);
156 return rc ? rc : set_pinentry_strings(pin, 0);
159 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
160 const gchar *cmd)
162 gpg_error_t error = 0;
164 if (!pin->ctx)
165 error = launch_pinentry(pin);
167 return error ? error : assuan_command(pin, result, cmd);
170 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
172 char *buf;
173 gpg_error_t error;
174 gchar *title = NULL;
176 if (which == 1)
177 title = g_strdup(N_("Password mismatch, please try again."));
178 else if (!pin->title)
179 title = pin->title = g_strdup(N_("Password Manager Daemon"));
180 else
181 title = pin->title;
183 if (!pin->prompt)
184 pin->prompt = g_strdup(N_("Password:"));
186 if (!pin->desc && !which)
187 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
188 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
189 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
190 pin->filename);
192 if (which == 2)
193 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
194 else
195 buf = g_strdup_printf("SETERROR %s", pin->desc);
197 error = pinentry_command(pin, NULL, buf);
198 g_free(buf);
200 if (error)
201 goto done;
203 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
204 error = pinentry_command(pin, NULL, buf);
205 g_free(buf);
207 if (error)
208 goto done;
210 buf = g_strdup_printf("SETDESC %s", title);
211 error = pinentry_command(pin, NULL, buf);
212 g_free(buf);
214 done:
215 if (which == 1)
216 g_free(title);
218 return error;
221 static void pinentry_disconnect(struct pinentry_s *pin)
223 if (!pin)
224 return;
226 if (pin->ctx)
227 assuan_disconnect(pin->ctx);
229 pin->ctx = NULL;
230 pin->pid = 0;
233 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
235 gpg_error_t error;
237 *result = NULL;
238 error = pinentry_command(pin, result, "GETPIN");
240 if (!*result)
241 *result = xstrdup("");
243 return error;
246 static gpg_error_t getpin(struct pinentry_s *pin, gchar **result)
248 gint which = 0;
249 gpg_error_t error = set_pinentry_strings(pin, which);
250 gchar *result1 = NULL;
252 if (error) {
253 pinentry_disconnect(pin);
254 return error;
257 again:
258 error = do_getpin(pin, result);
260 if (error)
261 goto done;
263 if (pin->which == PINENTRY_SAVE) {
264 if (!result1) {
265 error = set_pinentry_strings(pin, 2);
267 if (error)
268 goto done;
270 result1 = g_strdup(*result);
271 goto again;
274 if (strcmp(result1, *result)) {
275 g_free(result1);
276 xfree(*result);
277 result1 = *result = NULL;
278 error = set_pinentry_strings(pin, 1);
280 if (error)
281 goto done;
283 goto again;
287 done:
288 g_free(result1);
289 pinentry_disconnect(pin);
290 return error;
293 static void catchsig(gint sig)
295 pinentry_key_s pk;
297 switch (sig) {
298 case SIGTERM:
299 case SIGALRM:
300 memset(&pk, 0, sizeof(pk));
301 pk.error = sig == SIGTERM ? GPG_ERR_ASS_CANCELED : GPG_ERR_TIMEOUT;
302 pth_write(send_fd, &pk, sizeof(pk));
303 close(send_fd);
304 cleanup_pinentry(global_client->pinentry);
305 free_client(global_client);
306 _exit(EXIT_FAILURE);
307 break;
311 gpg_error_t pinentry_fork(assuan_context_t ctx)
313 struct client_s *client = assuan_get_pointer(ctx);
314 gpg_error_t error;
315 gint p[2];
316 pid_t pid;
317 pinentry_key_s pk;
318 gsize len;
319 gchar *result = NULL;
321 if (pipe(p) == -1)
322 return gpg_error_from_syserror();
324 pid = pth_fork();
326 switch (pid) {
327 case -1:
328 error = gpg_error_from_syserror();
329 close(p[0]);
330 close(p[1]);
331 return error;
332 case 0:
333 signal(SIGALRM, catchsig);
334 signal(SIGTERM, catchsig);
335 close(p[0]);
336 send_fd = p[1];
337 global_client = client;
338 alarm(client->pinentry->timeout);
339 pk.error = getpin(client->pinentry, &result);
340 cleanup_pinentry(client->pinentry);
341 free_client(client);
343 if (pk.error) {
344 xfree(result);
345 len = pth_write(p[1], &pk, sizeof(pk));
346 close(p[1]);
348 if (len != sizeof(pk))
349 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
351 _exit(1);
354 pk.error = 0;
355 strncpy(pk.key, result, sizeof(pk.key));
356 xfree(result);
357 len = pth_write(p[1], &pk, sizeof(pk));
358 memset(&pk, 0, sizeof(pk));
359 close(p[1]);
361 if (len != sizeof(pk))
362 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
364 _exit(0);
365 default:
366 close(p[1]);
367 client->pinentry->fd = p[0];
368 client->pinentry->pid = pid;
369 client->pinentry->status = PINENTRY_INIT;
370 break;
374 * Don't call assuan_process_done() here. That should be done in
375 * open_command_finalize() after the key has been read().
377 return 0;
380 void cleanup_pinentry(struct pinentry_s *pin)
382 if (!pin)
383 return;
385 if (pin->ctx && pin->pid)
386 pinentry_disconnect(pin);
388 g_free(pin->ttyname);
389 g_free(pin->ttytype);
390 g_free(pin->desc);
391 g_free(pin->title);
392 g_free(pin->prompt);
393 g_free(pin->path);
394 g_free(pin->display);
395 g_free(pin);
398 void set_pinentry_defaults(struct pinentry_s *pin)
400 FILE *fp;
401 gchar buf[PATH_MAX];
402 struct passwd *pw = getpwuid(getuid());
403 gchar *p;
405 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
406 fp = fopen(buf, "r");
408 if (fp) {
409 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
410 gchar name[32] = {0}, value[256] = {0};
412 if (*p == '#')
413 continue;
415 if (p[strlen(p)-1] == '\n')
416 p[strlen(p)-1] = 0;
418 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
419 continue;
421 if (g_strcasecmp("TTYNAME", name) == 0)
422 pin->ttyname = g_strdup(value);
423 else if (g_strcasecmp("TTYTYPE", name) == 0)
424 pin->ttytype = g_strdup(value);
425 else if (g_strcasecmp("DISPLAY", name) == 0)
426 pin->display = g_strdup(value);
427 else if (g_strcasecmp("PATH", name) == 0)
428 pin->path = g_strdup(value);
431 fclose(fp);