Another STORE command fix to hopefully fix what commit a118653 was
[pwmd.git] / src / pinentry.c
blob366278f07d7f6cccd24db0e0687d3f6fb65c8525
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, char **result,
66 const char *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 launch_pinentry(struct pinentry_s *pin)
95 int rc;
96 assuan_context_t ctx;
97 int child_list[] = {-1};
98 char *display = getenv("DISPLAY");
99 const char *argv[6];
100 int have_display = 0;
101 char *tty = NULL;
103 //update_pinentry_settings(pin);
105 if (pin->display || display)
106 have_display = 1;
107 else {
108 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
110 if (!tty)
111 return GPG_ERR_INV_VALUE;
115 if (!have_display && !tty)
116 return GPG_ERR_INV_VALUE;
118 argv[0] = "pinentry";
119 argv[1] = have_display ? "--display" : "--ttyname";
120 argv[2] = have_display ? pin->display ? pin->display : display : tty;
121 argv[3] = NULL;
123 if (!have_display) {
124 gchar *p = getenv("TERM");
126 if (p) {
127 argv[3] = "--ttytype";
128 argv[4] = pin->ttytype ? pin->ttytype : p;
129 argv[5] = NULL;
131 else
132 return GPG_ERR_INV_VALUE;
135 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
136 argv, child_list);
138 if (rc)
139 return rc;
141 pin->pid = assuan_get_pid(ctx);
142 pin->ctx = ctx;
143 return set_pinentry_strings(pin, 0);
146 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
147 const gchar *cmd)
149 gpg_error_t error = 0;
151 if (!pin->ctx)
152 error = launch_pinentry(pin);
154 return error ? error : assuan_command(pin, result, cmd);
157 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
159 char *buf;
160 gpg_error_t error;
161 gchar *title = NULL;
163 if (which == 1)
164 title = g_strdup(N_("Password mismatch, please try again."));
165 else if (!pin->title)
166 title = pin->title = g_strdup(N_("Password Manager Daemon"));
167 else
168 title = pin->title;
170 if (!pin->prompt)
171 pin->prompt = g_strdup(N_("Password:"));
173 if (!pin->desc && !which)
174 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
175 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
176 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
177 pin->filename);
179 if (which == 2)
180 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
181 else
182 buf = g_strdup_printf("SETERROR %s", pin->desc);
184 error = pinentry_command(pin, NULL, buf);
185 g_free(buf);
187 if (error)
188 goto done;
190 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
191 error = pinentry_command(pin, NULL, buf);
192 g_free(buf);
194 if (error)
195 goto done;
197 buf = g_strdup_printf("SETDESC %s", title);
198 error = pinentry_command(pin, NULL, buf);
199 g_free(buf);
201 done:
202 if (which == 1)
203 g_free(title);
205 return error;
208 static void pinentry_disconnect(struct pinentry_s *pin)
210 if (!pin)
211 return;
213 if (pin->ctx)
214 assuan_disconnect(pin->ctx);
216 pin->ctx = NULL;
217 pin->pid = 0;
220 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
222 gpg_error_t error;
224 *result = NULL;
225 error = pinentry_command(pin, result, "GETPIN");
227 if (!*result)
228 *result = xstrdup("");
230 return error;
233 static gpg_error_t getpin(struct pinentry_s *pin, gchar **result)
235 gint which = 0;
236 gpg_error_t error = set_pinentry_strings(pin, which);
237 gchar *result1 = NULL;
239 if (error) {
240 pinentry_disconnect(pin);
241 return error;
244 again:
245 error = do_getpin(pin, result);
247 if (error)
248 goto done;
250 if (pin->which == PINENTRY_SAVE) {
251 if (!result1) {
252 error = set_pinentry_strings(pin, 2);
254 if (error)
255 goto done;
257 result1 = g_strdup(*result);
258 goto again;
261 if (strcmp(result1, *result)) {
262 g_free(result1);
263 xfree(*result);
264 result1 = *result = NULL;
265 error = set_pinentry_strings(pin, 1);
267 if (error)
268 goto done;
270 goto again;
274 done:
275 g_free(result1);
276 pinentry_disconnect(pin);
277 return error;
280 static void catchsig(gint sig)
282 pinentry_key_s pk;
284 switch (sig) {
285 case SIGTERM:
286 case SIGALRM:
287 memset(&pk, 0, sizeof(pk));
288 pk.error = sig == SIGTERM ? GPG_ERR_ASS_CANCELED : GPG_ERR_TIMEOUT;
289 pth_write(send_fd, &pk, sizeof(pk));
290 close(send_fd);
291 cleanup_pinentry(global_client->pinentry);
292 free_client(global_client);
293 _exit(EXIT_FAILURE);
294 break;
298 gpg_error_t pinentry_fork(assuan_context_t ctx)
300 struct client_s *client = assuan_get_pointer(ctx);
301 gpg_error_t error;
302 gint p[2];
303 pid_t pid;
304 pinentry_key_s pk;
305 gsize len;
306 gchar *result;
308 if (pipe(p) == -1)
309 return gpg_error_from_syserror();
311 pid = pth_fork();
313 switch (pid) {
314 case -1:
315 error = gpg_error_from_syserror();
316 close(p[0]);
317 close(p[1]);
318 return error;
319 case 0:
320 signal(SIGALRM, catchsig);
321 signal(SIGTERM, catchsig);
322 close(p[0]);
323 send_fd = p[1];
324 global_client = client;
325 alarm(client->pinentry->timeout);
326 pk.error = getpin(client->pinentry, &result);
327 cleanup_pinentry(client->pinentry);
328 free_client(client);
330 if (pk.error) {
331 xfree(result);
332 len = pth_write(p[1], &pk, sizeof(pk));
333 close(p[1]);
335 if (len != sizeof(pk))
336 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
338 _exit(1);
341 pk.error = 0;
342 strncpy(pk.key, result, sizeof(pk.key));
343 xfree(result);
344 len = pth_write(p[1], &pk, sizeof(pk));
345 memset(&pk, 0, sizeof(pk));
346 close(p[1]);
348 if (len != sizeof(pk))
349 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
351 _exit(0);
352 default:
353 close(p[1]);
354 client->pinentry->fd = p[0];
355 client->pinentry->pid = pid;
356 client->pinentry->status = PINENTRY_INIT;
357 break;
361 * Don't call assuan_process_done() here. That should be done in
362 * open_command_finalize() after the key has been read().
364 return 0;
367 void cleanup_pinentry(struct pinentry_s *pin)
369 if (!pin)
370 return;
372 if (pin->ctx && pin->pid)
373 pinentry_disconnect(pin);
375 g_free(pin->ttyname);
376 g_free(pin->ttytype);
377 g_free(pin->desc);
378 g_free(pin->title);
379 g_free(pin->prompt);
380 g_free(pin->path);
381 g_free(pin->display);
382 g_free(pin);
385 void set_pinentry_defaults(struct pinentry_s *pin)
387 FILE *fp;
388 gchar buf[PATH_MAX];
389 struct passwd *pw = getpwuid(getuid());
390 gchar *p;
392 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
393 fp = fopen(buf, "r");
395 if (fp) {
396 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
397 gchar name[32] = {0}, value[256] = {0};
399 if (*p == '#')
400 continue;
402 if (p[strlen(p)-1] == '\n')
403 p[strlen(p)-1] = 0;
405 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
406 continue;
408 if (g_strcasecmp("TTYNAME", name) == 0)
409 pin->ttyname = g_strdup(value);
410 else if (g_strcasecmp("TTYTYPE", name) == 0)
411 pin->ttytype = g_strdup(value);
412 else if (g_strcasecmp("DISPLAY", name) == 0)
413 pin->display = g_strdup(value);
414 else if (g_strcasecmp("PATH", name) == 0)
415 pin->path = g_strdup(value);
418 fclose(fp);
421 pin->use = get_key_file_boolean("default", "enable_pinentry");