Free all clients in the child process after pth_fork().
[pwmd.git] / src / pinentry.c
blob5c23a8d866df85b51d02a905a90a70898707be9e
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 gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which);
45 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
47 membuf_t *mem = (membuf_t *)data;
48 void *p;
50 if (!buffer)
51 return 0;
53 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
54 return 1;
56 mem->buf = p;
57 memcpy((char *)mem->buf + mem->len, buffer, len);
58 mem->len += len;
59 return 0;
62 static gpg_error_t assuan_command(struct pinentry_s *pin, gchar **result,
63 const gchar *cmd)
65 membuf_t data;
66 gpg_error_t rc;
68 data.len = 0;
69 data.buf = NULL;
71 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
72 NULL, NULL);
74 if (rc) {
75 if (data.buf) {
76 xfree(data.buf);
77 data.buf = NULL;
80 else {
81 if (data.buf) {
82 mem_realloc_cb(&data, "", 1);
83 *result = (gchar *)data.buf;
87 return rc;
90 static gpg_error_t set_pinentry_options(struct pinentry_s *pin)
92 gchar *display = getenv("DISPLAY");
93 gint have_display = 0;
94 gchar *tty = NULL, *ttytype = NULL;
95 gchar *opt, *val;
96 gpg_error_t rc;
97 gchar *result = NULL;
98 gchar cmd[ASSUAN_LINELENGTH];
100 if (pin->display || display)
101 have_display = 1;
102 else {
103 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
105 if (!tty)
106 return GPG_ERR_CANCELED;
109 if (!have_display && !tty)
110 return GPG_ERR_CANCELED;
112 if (!have_display) {
113 gchar *p = getenv("TERM");
115 ttytype = pin->ttytype ? pin->ttytype : p;
117 if (!ttytype)
118 return GPG_ERR_CANCELED;
121 opt = have_display ? "DISPLAY" : "TTYNAME";
122 val = have_display ? pin->display ? pin->display : display : tty;
123 snprintf(cmd, sizeof(cmd), "OPTION %s=%s", g_ascii_strdown(opt, strlen(opt)), val);
124 rc = assuan_command(pin, &result, cmd);
126 if (rc)
127 return rc;
129 if (!have_display) {
130 snprintf(cmd, sizeof(cmd), "OPTION ttytype=%s", ttytype);
131 rc = assuan_command(pin, &result, cmd);
134 return rc;
137 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
139 gpg_error_t rc;
140 assuan_context_t ctx;
141 gint child_list[] = {-1};
142 const gchar *argv[] = { "pinentry", NULL };
144 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
145 argv, child_list);
147 if (rc)
148 return rc;
150 pin->pid = assuan_get_pid(ctx);
151 pin->ctx = ctx;
152 rc = set_pinentry_options(pin);
153 return rc ? rc : set_pinentry_strings(pin, 0);
156 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
157 const gchar *cmd)
159 gpg_error_t error = 0;
161 if (!pin->ctx)
162 error = launch_pinentry(pin);
164 return error ? error : assuan_command(pin, result, cmd);
167 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
169 char *buf;
170 gpg_error_t error;
171 gchar *title = NULL;
173 if (which == 1)
174 title = g_strdup(N_("Password mismatch, please try again."));
175 else if (!pin->title)
176 title = pin->title = g_strdup(N_("Password Manager Daemon"));
177 else
178 title = pin->title;
180 if (!pin->prompt)
181 pin->prompt = g_strdup(N_("Password:"));
183 if (!pin->desc && !which)
184 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
185 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
186 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
187 pin->filename);
189 if (which == 2)
190 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
191 else
192 buf = g_strdup_printf("SETERROR %s", pin->desc);
194 error = pinentry_command(pin, NULL, buf);
195 g_free(buf);
197 if (error)
198 goto done;
200 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
201 error = pinentry_command(pin, NULL, buf);
202 g_free(buf);
204 if (error)
205 goto done;
207 buf = g_strdup_printf("SETDESC %s", title);
208 error = pinentry_command(pin, NULL, buf);
209 g_free(buf);
211 done:
212 if (which == 1)
213 g_free(title);
215 return error;
218 static void pinentry_disconnect(struct pinentry_s *pin)
220 if (!pin)
221 return;
223 if (pin->ctx)
224 assuan_disconnect(pin->ctx);
226 pin->ctx = NULL;
227 pin->pid = 0;
230 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
232 gpg_error_t error;
234 *result = NULL;
235 error = pinentry_command(pin, result, "GETPIN");
237 if (!*result)
238 *result = xstrdup("");
240 return error;
243 gpg_error_t pinentry_getpin(struct pinentry_s *pin, gchar **result)
245 gint which = 0;
246 gpg_error_t error = set_pinentry_strings(pin, which);
247 gchar *result1 = NULL;
249 if (error) {
250 pinentry_disconnect(pin);
251 return error;
254 again:
255 error = do_getpin(pin, result);
257 if (error)
258 goto done;
260 if (pin->which == PINENTRY_SAVE) {
261 if (!result1) {
262 error = set_pinentry_strings(pin, 2);
264 if (error)
265 goto done;
267 result1 = g_strdup(*result);
268 goto again;
271 if (strcmp(result1, *result)) {
272 g_free(result1);
273 xfree(*result);
274 result1 = *result = NULL;
275 error = set_pinentry_strings(pin, 1);
277 if (error)
278 goto done;
280 goto again;
284 done:
285 g_free(result1);
286 pinentry_disconnect(pin);
287 return error;
290 gpg_error_t pinentry_fork(assuan_context_t ctx)
292 struct client_s *client = assuan_get_pointer(ctx);
293 struct pinentry_s *pin = client->pinentry;
294 gpg_error_t error;
295 gint p[2];
296 pid_t pid;
297 pinentry_key_s pk;
298 gsize len;
299 gchar *result = NULL;
301 if (pipe(p) == -1)
302 return gpg_error_from_syserror();
304 pid = pth_fork();
306 switch (pid) {
307 case -1:
308 error = gpg_error_from_syserror();
309 close(p[0]);
310 close(p[1]);
311 return error;
312 case 0:
313 close(p[0]);
314 free_client_list();
315 pk.error = pinentry_getpin(pin, &result);
317 if (pk.error) {
318 xfree(result);
319 len = pth_write(p[1], &pk, sizeof(pk));
320 close(p[1]);
322 if (len != sizeof(pk))
323 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
325 _exit(1);
328 pk.error = 0;
329 strncpy(pk.key, result, sizeof(pk.key));
330 xfree(result);
331 len = pth_write(p[1], &pk, sizeof(pk));
332 memset(&pk, 0, sizeof(pk));
333 close(p[1]);
335 if (len != sizeof(pk))
336 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
338 _exit(0);
339 default:
340 close(p[1]);
341 client->pinentry->fd = p[0];
342 client->pinentry->pid = pid;
343 client->pinentry->status = PINENTRY_INIT;
344 break;
348 * Don't call assuan_process_done() here. That should be done in
349 * open_command_finalize() after the key has been read().
351 return 0;
354 void cleanup_pinentry(struct pinentry_s *pin)
356 if (!pin)
357 return;
359 if (pin->ctx && pin->pid)
360 pinentry_disconnect(pin);
362 g_free(pin->ttyname);
363 g_free(pin->ttytype);
364 g_free(pin->desc);
365 g_free(pin->title);
366 g_free(pin->prompt);
367 g_free(pin->path);
368 g_free(pin->display);
369 g_free(pin->filename);
370 g_free(pin);
373 void set_pinentry_defaults(struct pinentry_s *pin)
375 FILE *fp;
376 gchar buf[PATH_MAX];
377 struct passwd *pw = getpwuid(getuid());
378 gchar *p;
380 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
381 fp = fopen(buf, "r");
383 if (fp) {
384 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
385 gchar name[32] = {0}, value[256] = {0};
387 if (*p == '#')
388 continue;
390 if (p[strlen(p)-1] == '\n')
391 p[strlen(p)-1] = 0;
393 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
394 continue;
396 if (g_strcasecmp("TTYNAME", name) == 0)
397 pin->ttyname = g_strdup(value);
398 else if (g_strcasecmp("TTYTYPE", name) == 0)
399 pin->ttytype = g_strdup(value);
400 else if (g_strcasecmp("DISPLAY", name) == 0)
401 pin->display = g_strdup(value);
402 else if (g_strcasecmp("PATH", name) == 0)
403 pin->path = g_strdup(value);
406 fclose(fp);