Added a note about a child node of the same name as the new content of
[pwmd.git] / src / pinentry.c
blob44c7455f8eefdbc43de4fa4b6cf22dbcb2a2e0b8
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007-2008 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 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
30 #include "mem.h"
31 #include "common.h"
32 #include "pinentry.h"
33 #include "pwmd_error.h"
35 typedef struct {
36 size_t len;
37 void *buf;
38 } membuf_t;
40 void free_client_list();
41 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which);
43 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
45 membuf_t *mem = (membuf_t *)data;
46 void *p;
48 if (!buffer)
49 return 0;
51 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
52 return 1;
54 mem->buf = p;
55 memcpy((char *)mem->buf + mem->len, buffer, len);
56 mem->len += len;
57 return 0;
60 static gpg_error_t assuan_command(struct pinentry_s *pin, gchar **result,
61 const gchar *cmd)
63 membuf_t data;
64 gpg_error_t rc;
66 data.len = 0;
67 data.buf = NULL;
69 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
70 NULL, NULL);
72 if (rc) {
73 if (data.buf) {
74 xfree(data.buf);
75 data.buf = NULL;
78 else {
79 if (data.buf) {
80 mem_realloc_cb(&data, "", 1);
81 *result = (gchar *)data.buf;
85 return rc;
88 static gpg_error_t set_pinentry_options(struct pinentry_s *pin)
90 gchar *display = getenv("DISPLAY");
91 gint have_display = 0;
92 gchar *tty = NULL, *ttytype = NULL;
93 gchar *opt, *val;
94 gpg_error_t rc;
95 gchar *result = NULL;
96 gchar cmd[ASSUAN_LINELENGTH];
98 if (pin->display || display)
99 have_display = 1;
100 else {
101 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
103 if (!tty)
104 return GPG_ERR_CANCELED;
107 if (!have_display && !tty)
108 return GPG_ERR_CANCELED;
110 if (!have_display) {
111 gchar *p = getenv("TERM");
113 ttytype = pin->ttytype ? pin->ttytype : p;
115 if (!ttytype)
116 return GPG_ERR_CANCELED;
119 opt = have_display ? "DISPLAY" : "TTYNAME";
120 val = have_display ? pin->display ? pin->display : display : tty;
121 g_snprintf(cmd, sizeof(cmd), "OPTION %s=%s", g_ascii_strdown(opt, strlen(opt)), val);
122 rc = assuan_command(pin, &result, cmd);
124 if (rc)
125 return rc;
127 if (!have_display) {
128 g_snprintf(cmd, sizeof(cmd), "OPTION ttytype=%s", ttytype);
129 rc = assuan_command(pin, &result, cmd);
132 return rc;
135 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
137 gpg_error_t rc;
138 assuan_context_t ctx;
139 gint child_list[] = {-1};
140 const gchar *argv[] = { "pinentry", NULL };
142 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
143 argv, child_list);
145 if (rc)
146 return rc;
148 pin->pid = assuan_get_pid(ctx);
149 pin->ctx = ctx;
150 rc = set_pinentry_options(pin);
151 return rc ? rc : set_pinentry_strings(pin, 0);
154 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
155 const gchar *cmd)
157 gpg_error_t rc = 0;
159 if (!pin->ctx)
160 rc = launch_pinentry(pin);
162 return rc ? rc : assuan_command(pin, result, cmd);
165 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
167 char *buf;
168 gpg_error_t rc;
169 gchar *title = NULL;
171 if (which == 1)
172 title = g_strdup(N_("Password mismatch, please try again."));
173 else if (!pin->title)
174 title = pin->title = g_strdup(N_("Password Manager Daemon"));
175 else
176 title = pin->title;
178 if (!pin->prompt)
179 pin->prompt = g_strdup(N_("Password:"));
181 if (!pin->desc && !which)
182 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
183 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
184 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
185 pin->filename);
187 if (which == 2)
188 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
189 else
190 buf = g_strdup_printf("SETERROR %s", pin->desc);
192 rc = pinentry_command(pin, NULL, buf);
193 g_free(buf);
195 if (rc)
196 goto done;
198 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
199 rc = pinentry_command(pin, NULL, buf);
200 g_free(buf);
202 if (rc)
203 goto done;
205 buf = g_strdup_printf("SETDESC %s", title);
206 rc = pinentry_command(pin, NULL, buf);
207 g_free(buf);
209 done:
210 if (which == 1)
211 g_free(title);
213 return rc;
216 static void pinentry_disconnect(struct pinentry_s *pin)
218 if (!pin)
219 return;
221 if (pin->ctx)
222 assuan_disconnect(pin->ctx);
224 pin->ctx = NULL;
225 pin->pid = 0;
228 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
230 gpg_error_t rc;
232 *result = NULL;
233 rc = pinentry_command(pin, result, "GETPIN");
235 if (!*result)
236 *result = xstrdup("");
238 return rc;
241 gpg_error_t pinentry_getpin(struct pinentry_s *pin, gchar **result)
243 gint which = 0;
244 gpg_error_t rc = set_pinentry_strings(pin, which);
245 gchar *result1 = NULL;
247 if (rc) {
248 pinentry_disconnect(pin);
249 return rc;
252 again:
253 rc = do_getpin(pin, result);
255 if (rc)
256 goto done;
258 if (pin->which == PINENTRY_SAVE) {
259 if (!result1) {
260 rc = set_pinentry_strings(pin, 2);
262 if (rc)
263 goto done;
265 result1 = g_strdup(*result);
266 goto again;
269 if (strcmp(result1, *result)) {
270 g_free(result1);
271 xfree(*result);
272 result1 = *result = NULL;
273 rc = set_pinentry_strings(pin, 1);
275 if (rc)
276 goto done;
278 goto again;
282 done:
283 g_free(result1);
284 pinentry_disconnect(pin);
285 return rc;
288 gpg_error_t pinentry_fork(assuan_context_t ctx)
290 struct client_s *client = assuan_get_pointer(ctx);
291 struct pinentry_s *pin = client->pinentry;
292 gpg_error_t rc;
293 gint p[2];
294 pid_t pid;
295 pinentry_key_s pk;
296 gsize len;
297 gchar *result = NULL;
299 if (pipe(p) == -1)
300 return gpg_error_from_syserror();
302 pid = pth_fork();
304 switch (pid) {
305 case -1:
306 rc = gpg_error_from_syserror();
307 close(p[0]);
308 close(p[1]);
309 return rc;
310 case 0:
311 close(p[0]);
312 free_client_list();
313 pk.error = pinentry_getpin(pin, &result);
315 if (pk.error) {
316 xfree(result);
319 * libassuan handles GPG_ERR_EOF in assuan_process_done() and
320 * will disconnect the client even if the error isn't related
321 * to it. Use GPG_ERR_CANCELED instead.
323 if (gpg_err_code(pk.error) == GPG_ERR_EOF)
324 pk.error = GPG_ERR_CANCELED;
326 len = pth_write(p[1], &pk, sizeof(pk));
327 close(p[1]);
329 if (len != sizeof(pk))
330 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
332 _exit(1);
335 pk.error = 0;
336 g_strlcpy(pk.key, result, sizeof(pk.key));
337 xfree(result);
338 len = pth_write(p[1], &pk, sizeof(pk));
339 memset(&pk, 0, sizeof(pk));
340 close(p[1]);
342 if (len != sizeof(pk))
343 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
345 _exit(0);
346 default:
347 close(p[1]);
348 client->pinentry->fd = p[0];
349 client->pinentry->pid = pid;
350 client->pinentry->status = PINENTRY_INIT;
351 break;
355 * Don't call assuan_process_done() here. That should be done in
356 * open_command_finalize() after the key has been read().
358 return 0;
361 void lock_pin_mutex(struct client_s *client)
363 if (pth_mutex_acquire(&pin_mutex, TRUE, NULL) == FALSE) {
364 if (client->ctx)
365 assuan_write_status(client->ctx, "LOCKED", N_("Waiting for lock"));
367 pth_mutex_acquire(&pin_mutex, FALSE, NULL);
370 client->pinentry->has_lock = TRUE;
373 void unlock_pin_mutex(struct pinentry_s *pin)
375 if (pin->has_lock == FALSE)
376 return;
378 pth_mutex_release(&pin_mutex);
379 pin->has_lock = FALSE;
382 void cleanup_pinentry(struct pinentry_s *pin)
384 if (!pin)
385 return;
387 unlock_pin_mutex(pin);
389 if (pin->ctx && pin->pid)
390 pinentry_disconnect(pin);
392 g_free(pin->ttyname);
393 g_free(pin->ttytype);
394 g_free(pin->desc);
395 g_free(pin->title);
396 g_free(pin->prompt);
397 g_free(pin->path);
398 g_free(pin->display);
399 g_free(pin->filename);
400 g_free(pin);
403 void set_pinentry_defaults(struct pinentry_s *pin)
405 FILE *fp;
406 gchar buf[PATH_MAX];
407 struct passwd *pw = getpwuid(getuid());
408 gchar *p;
410 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
411 fp = fopen(buf, "r");
413 if (fp) {
414 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
415 gchar name[32] = {0}, value[256] = {0};
417 if (*p == '#')
418 continue;
420 if (p[strlen(p)-1] == '\n')
421 p[strlen(p)-1] = 0;
423 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
424 continue;
426 if (g_strcasecmp("TTYNAME", name) == 0)
427 pin->ttyname = g_strdup(value);
428 else if (g_strcasecmp("TTYTYPE", name) == 0)
429 pin->ttytype = g_strdup(value);
430 else if (g_strcasecmp("DISPLAY", name) == 0)
431 pin->display = g_strdup(value);
432 else if (g_strcasecmp("PATH", name) == 0)
433 pin->path = g_strdup(value);
436 fclose(fp);