Define PWMD_ERR_SOURCE as GPG_ERR_SOURCE_USER_1 and use it.
[pwmd.git] / src / pinentry.c
blobcb90f941b7dfa9b2bb81b04b48410332030be1a4
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"
37 #include "assuan-errors.h"
39 typedef struct {
40 size_t len;
41 void *buf;
42 } membuf_t;
44 static struct client_s *global_client;
45 static gint send_fd;
47 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which);
49 /* Borrowed from libassuan 1.0.4 svn 277 */
50 /* Helper to map old style Assuan error codes to gpg-error codes.
51 This is used internally to keep an compatible ABI. */
52 static assuan_error_t
53 _assuan_error (int oldcode)
55 unsigned int n;
56 int err_source = 0;
58 switch (oldcode)
60 case ASSUAN_General_Error: n = 257; break;
61 case ASSUAN_Accept_Failed: n = 258; break;
62 case ASSUAN_Connect_Failed: n = 259; break;
63 case ASSUAN_Invalid_Response: n = 260; break;
64 case ASSUAN_Invalid_Value: n = 261; break;
65 case ASSUAN_Line_Not_Terminated: n = 262; break;
66 case ASSUAN_Line_Too_Long: n = 263; break;
67 case ASSUAN_Nested_Commands: n = 264; break;
68 case ASSUAN_No_Data_Callback: n = 265; break;
69 case ASSUAN_No_Inquire_Callback: n = 266; break;
70 case ASSUAN_Not_A_Server: n = 267; break;
71 case ASSUAN_Not_Implemented: n = 69; break;
72 case ASSUAN_Parameter_Conflict: n = 280; break;
73 case ASSUAN_Problem_Starting_Server: n = 269; break;
74 case ASSUAN_Server_Fault: n = 80; break;
75 case ASSUAN_Syntax_Error: n = 276; break;
76 case ASSUAN_Too_Much_Data: n = 273; break;
77 case ASSUAN_Unexpected_Command: n = 274; break;
78 case ASSUAN_Unknown_Command: n = 275; break;
79 case ASSUAN_Canceled: n = 277; break;
80 case ASSUAN_No_Secret_Key: n = 17; break;
81 case ASSUAN_Not_Confirmed: n = 114; break;
83 case ASSUAN_Read_Error:
84 switch (errno)
86 case 0: n = 16381; /*GPG_ERR_MISSING_ERRNO*/ break;
87 case EAGAIN:
88 n = (6 | (1 << 15));
89 break;
90 default: n = 270; /*GPG_ERR_ASS_READ_ERROR*/ break;
92 break;
94 case ASSUAN_Write_Error:
95 switch (errno)
97 case 0: n = 16381; /*GPG_ERR_MISSING_ERRNO*/ break;
98 case EAGAIN:
99 n = (6 | (1 << 15));
100 break;
101 default: n = 271; /*GPG_ERR_ASS_WRITE_ERROR*/ break;
103 break;
105 case ASSUAN_Out_Of_Core:
106 switch (errno)
108 case 0: /* Should not happen but a user might have provided
109 an incomplete implemented malloc function. Give
110 him a chance to correct this fault but make sure
111 an error is indeed returned. */
112 n = 16381; /*GPG_ERR_MISSING_ERRNO*/
113 break;
114 case ENOMEM:
115 n = (86 | (1 << 15));
116 break;
117 default:
118 n = 16382; /*GPG_ERR_UNKNOWN_ERRNO*/
119 break;
121 break;
123 case -1: n = 16383 /*GPG_ERR_EOF*/; break;
125 default:
126 n = 257;
127 break;
130 return ((err_source << 24) | (n & 0x00ffffff));
133 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
135 membuf_t *mem = (membuf_t *)data;
136 void *p;
138 if (!buffer)
139 return 0;
141 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
142 return 1;
144 mem->buf = p;
145 memcpy((char *)mem->buf + mem->len, buffer, len);
146 mem->len += len;
147 return 0;
150 static gpg_error_t assuan_command(struct pinentry_s *pin, char **result,
151 const char *cmd)
153 membuf_t data;
154 gpg_error_t rc;
156 data.len = 0;
157 data.buf = NULL;
159 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
160 NULL, NULL);
162 if (rc) {
163 if (data.buf) {
164 xfree(data.buf);
165 data.buf = NULL;
168 else {
169 if (data.buf) {
170 mem_realloc_cb(&data, "", 1);
171 *result = (gchar *)data.buf;
175 return rc;
178 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
180 int rc;
181 assuan_context_t ctx;
182 int child_list[] = {-1};
183 char *display = getenv("DISPLAY");
184 const char *argv[6];
185 int have_display = 0;
186 char *tty = NULL;
188 //update_pinentry_settings(pin);
190 if (pin->display || display)
191 have_display = 1;
192 else {
193 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
195 if (!tty)
196 return _assuan_error(ASSUAN_Invalid_Value);
200 if (!have_display && !tty)
201 return _assuan_error(ASSUAN_Invalid_Value);
203 argv[0] = "pinentry";
204 argv[1] = have_display ? "--display" : "--ttyname";
205 argv[2] = have_display ? pin->display ? pin->display : display : tty;
206 argv[3] = NULL;
208 if (!have_display) {
209 gchar *p = getenv("TERM");
211 if (p) {
212 argv[3] = "--ttytype";
213 argv[4] = pin->ttytype ? pin->ttytype : p;
214 argv[5] = NULL;
216 else
217 return _assuan_error(ASSUAN_Invalid_Value);
220 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
221 argv, child_list);
223 if (rc)
224 return _assuan_error(rc);
226 pin->pid = assuan_get_pid(ctx);
227 pin->ctx = ctx;
228 return set_pinentry_strings(pin, 0);
231 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
232 const gchar *cmd)
234 gpg_error_t error = 0;
236 if (!pin->ctx)
237 error = launch_pinentry(pin);
239 return error ? error : assuan_command(pin, result, cmd);
242 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
244 char *buf;
245 gpg_error_t error;
246 gchar *title = NULL;
248 if (which == 1)
249 title = g_strdup(N_("Password mismatch, please try again."));
250 else if (!pin->title)
251 title = pin->title = g_strdup(N_("Password Manager Daemon"));
252 else
253 title = pin->title;
255 if (!pin->prompt)
256 pin->prompt = g_strdup(N_("Password:"));
258 if (!pin->desc && !which)
259 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
260 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
261 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
262 pin->filename);
264 if (which == 2)
265 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
266 else
267 buf = g_strdup_printf("SETERROR %s", pin->desc);
269 error = pinentry_command(pin, NULL, buf);
270 g_free(buf);
272 if (error)
273 goto done;
275 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
276 error = pinentry_command(pin, NULL, buf);
277 g_free(buf);
279 if (error)
280 goto done;
282 buf = g_strdup_printf("SETDESC %s", title);
283 error = pinentry_command(pin, NULL, buf);
284 g_free(buf);
286 done:
287 if (which == 1)
288 g_free(title);
290 return error;
293 static void pinentry_disconnect(struct pinentry_s *pin)
295 if (!pin)
296 return;
298 if (pin->ctx)
299 assuan_disconnect(pin->ctx);
301 pin->ctx = NULL;
302 pin->pid = 0;
305 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
307 gpg_error_t error;
309 *result = NULL;
310 error = pinentry_command(pin, result, "GETPIN");
312 if (error)
313 error = _assuan_error(error);
314 else if (!*result)
315 *result = xstrdup("");
317 return error;
320 static gpg_error_t getpin(struct pinentry_s *pin, gchar **result)
322 gint which = 0;
323 gpg_error_t error = set_pinentry_strings(pin, which);
324 gchar *result1 = NULL;
326 if (error) {
327 pinentry_disconnect(pin);
328 return error;
331 again:
332 error = do_getpin(pin, result);
334 if (error)
335 goto done;
337 if (pin->which == PINENTRY_SAVE) {
338 if (!result1) {
339 error = set_pinentry_strings(pin, 2);
341 if (error)
342 goto done;
344 result1 = g_strdup(*result);
345 goto again;
348 if (strcmp(result1, *result)) {
349 g_free(result1);
350 xfree(*result);
351 result1 = *result = NULL;
352 error = set_pinentry_strings(pin, 1);
354 if (error)
355 goto done;
357 goto again;
361 done:
362 g_free(result1);
363 pinentry_disconnect(pin);
364 return error;
367 static void catchsig(gint sig)
369 pinentry_key_s pk;
371 switch (sig) {
372 case SIGTERM:
373 case SIGALRM:
374 if (global_client->pinentry->pid) {
375 kill(SIGTERM, global_client->pinentry->pid);
376 global_client->pinentry->pid = 0;
379 memset(&pk, 0, sizeof(pk));
380 pk.error = sig == SIGTERM ? GPG_ERR_ASS_CANCELED : GPG_ERR_TIMEOUT;
381 pth_write(send_fd, &pk, sizeof(pk));
382 cleanup_pinentry(global_client->pinentry);
383 free_client(global_client);
384 _exit(1);
385 break;
389 gpg_error_t pinentry_fork(assuan_context_t ctx)
391 struct client_s *client = assuan_get_pointer(ctx);
392 gpg_error_t error;
393 gint p[2];
394 pid_t pid;
395 pinentry_key_s pk;
396 gsize len;
397 gchar *result;
399 if (pipe(p) == -1)
400 return gpg_error_from_syserror();
402 pid = pth_fork();
404 switch (pid) {
405 case -1:
406 error = gpg_error_from_syserror();
407 close(p[0]);
408 close(p[1]);
409 return error;
410 case 0:
411 signal(SIGALRM, catchsig);
412 signal(SIGTERM, catchsig);
413 close(p[0]);
414 send_fd = p[1];
415 global_client = client;
416 alarm(client->pinentry->timeout);
417 pk.error = getpin(client->pinentry, &result);
418 cleanup_pinentry(client->pinentry);
419 free_client(client);
421 if (pk.error) {
422 xfree(result);
423 len = pth_write(p[1], &pk, sizeof(pk));
424 close(p[1]);
426 if (len != sizeof(pk))
427 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
429 _exit(1);
432 pk.error = 0;
433 strncpy(pk.key, result, sizeof(pk.key));
434 xfree(result);
435 len = pth_write(p[1], &pk, sizeof(pk));
436 memset(&pk, 0, sizeof(pk));
437 close(p[1]);
439 if (len != sizeof(pk))
440 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
442 _exit(0);
443 default:
444 client->pinentry->fd = p[0];
445 client->pinentry->pid = pid;
446 client->pinentry->status = PINENTRY_INIT;
447 break;
451 * Don't call assuan_process_done() here. That should be done in
452 * open_command_finalize() after the key has been read().
454 return 0;
457 void cleanup_pinentry(struct pinentry_s *pin)
459 if (!pin)
460 return;
462 if (pin->ctx && pin->pid)
463 pinentry_disconnect(pin);
465 g_free(pin->ttyname);
466 g_free(pin->ttytype);
467 g_free(pin->desc);
468 g_free(pin->title);
469 g_free(pin->prompt);
470 g_free(pin->path);
471 g_free(pin->display);
472 g_free(pin);
475 void set_pinentry_defaults(struct pinentry_s *pin)
477 FILE *fp;
478 gchar buf[PATH_MAX];
479 struct passwd *pw = getpwuid(getuid());
480 gchar *p;
482 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
483 fp = fopen(buf, "r");
485 if (fp) {
486 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
487 gchar name[32] = {0}, value[256] = {0};
489 if (*p == '#')
490 continue;
492 if (p[strlen(p)-1] == '\n')
493 p[strlen(p)-1] = 0;
495 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
496 continue;
498 if (g_strcasecmp("TTYNAME", name) == 0)
499 pin->ttyname = g_strdup(value);
500 else if (g_strcasecmp("TTYTYPE", name) == 0)
501 pin->ttytype = g_strdup(value);
502 else if (g_strcasecmp("DISPLAY", name) == 0)
503 pin->display = g_strdup(value);
504 else if (g_strcasecmp("PATH", name) == 0)
505 pin->path = g_strdup(value);
508 fclose(fp);
511 pin->use = get_key_file_boolean("default", "enable_pinentry");