Update m4 macros.
[libpwmd.git] / src / agent.c
blob4d89475340c2e51bad98858678a5d106d47a7bca
1 /*
2 Copyright (C) 2016
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Pwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <pwd.h>
27 #include <sys/types.h>
28 #include <signal.h>
29 #include <sys/wait.h>
30 #include <fcntl.h>
31 #include <dirent.h>
33 #ifdef HAVE_LIMITS_H
34 #include <limits.h>
35 #endif
37 #include "pwmd-error.h"
38 #include <gpgme.h>
39 #include "util-misc.h"
40 #include "mem.h"
41 #include "common.h"
42 #include "agent.h"
43 #include "util-string.h"
44 #include "mutex.h"
45 #include "rcfile.h"
46 #include "cache.h"
48 static gpg_error_t
49 mem_realloc_cb (void *data, const void *buffer, size_t len)
51 membuf_t *mem = (membuf_t *) data;
52 void *p;
54 if (!buffer)
55 return 0;
57 if ((p = xrealloc (mem->buf, mem->len + len)) == NULL)
58 return 1;
60 mem->buf = p;
61 memcpy ((char *) mem->buf + mem->len, buffer, len);
62 mem->len += len;
63 return 0;
66 static gpg_error_t
67 status_cb (void *data, const char *line)
69 struct agent_s *agent = data;
71 agent->inquire_maxlen = 0;
73 if (!strncmp (line, "INQUIRE_MAXLEN ", 15))
74 agent->inquire_maxlen = atoi (line + 15);
76 return 0;
79 static gpg_error_t
80 inquire_cb (void *user, const char *keyword)
82 struct agent_s *agent = user;
84 (void)keyword;
85 return assuan_send_data (agent->ctx, agent->inquire->line,
86 agent->inquire->len);
89 static gpg_error_t
90 assuan_command (struct agent_s *a, char **result,
91 size_t * len, const char *cmd)
93 gpg_error_t rc;
95 a->data.len = 0;
96 a->data.buf = NULL;
97 if (result)
98 *result = NULL;
99 if (len)
100 *len = 0;
102 rc = assuan_transact (a->ctx, cmd, mem_realloc_cb, &a->data,
103 inquire_cb, a, status_cb, a);
104 if (rc)
105 xfree (a->data.buf);
106 else
108 if (a->data.buf)
110 mem_realloc_cb (&a->data, "", 1);
111 if (result)
112 *result = (char *) a->data.buf;
113 else
114 xfree (a->data.buf);
116 if (len)
117 *len = a->data.len;
121 return rc;
124 static char *
125 get_gpg_connect_agent_path (void)
127 gpgme_engine_info_t engine;
128 char *s, *p;
130 gpgme_get_engine_info (&engine);
131 while (engine)
133 if (engine->protocol == GPGME_PROTOCOL_GPGCONF)
134 break;
136 engine = engine->next;
139 if (!engine)
140 return NULL;
142 s = str_dup (engine->file_name);
143 if (!s)
144 return NULL;
146 p = strrchr (s, '/');
147 if (p)
149 *p = 0;
150 p = str_asprintf ("%s/gpg-connect-agent", s);
151 xfree (s);
152 return p;
155 xfree (s);
156 return NULL;
159 gpg_error_t
160 agent_connect (struct agent_s *agent)
162 gpg_error_t rc;
163 assuan_context_t ctx = NULL;
164 static struct assuan_malloc_hooks mhooks = { xmalloc, xrealloc, xfree };
166 agent->did_restart = 0;
167 if (agent->restart)
169 gpgme_ctx_t gctx;
171 rc = gpgme_new (&gctx);
172 if (!rc)
174 char **args = NULL;
176 pthread_cleanup_push ((void *)gpgme_release, gctx);
177 rc = gpgme_set_protocol (gctx, GPGME_PROTOCOL_SPAWN);
178 if (!rc && strv_printf (&args, ""))
180 if (!rc && strv_printf (&args, "--homedir"))
182 char *gpghome = config_get_string ("global", "gpg_homedir");
184 if (gpghome)
186 if (!strv_printf (&args, "%s", gpghome))
187 rc = GPG_ERR_ENOMEM;
189 else
191 if (!strv_printf (&args, "%s/.gnupg", homedir))
192 rc = GPG_ERR_ENOMEM;
195 xfree (gpghome);
197 else if (!rc)
198 rc = GPG_ERR_ENOMEM;
200 else if (!rc)
201 rc = GPG_ERR_ENOMEM;
203 pthread_cleanup_push ((void *)strv_free, args);
204 if (!rc)
206 gpgme_data_t idata = NULL;
207 size_t len;
208 char *s;
210 rc = gpgme_data_new (&idata);
211 if (!rc)
213 char *gca = get_gpg_connect_agent_path ();
215 pthread_cleanup_push (xfree, gca);
216 if (gca)
217 rc = gpgme_op_spawn (gctx, gca, (const char **)args, NULL,
218 idata, NULL,
219 GPGME_SPAWN_DETACHED|GPGME_SPAWN_ALLOW_SET_FG);
220 else
221 rc = GPG_ERR_ENOMEM;
223 pthread_cleanup_pop (1);
226 if (!rc)
228 ssize_t ret = gpgme_data_write (idata, "/BYE\n", 5);
230 if (ret != 5)
231 rc = gpg_error_from_syserror ();
234 if (idata)
236 s = gpgme_data_release_and_get_mem (idata, &len);
237 gpgme_free (s);
241 pthread_cleanup_pop (1);
242 pthread_cleanup_pop (1);
245 if (rc)
246 return rc;
249 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, assuan_log_cb,
250 NULL);
251 if (rc)
252 return rc;
254 pthread_cleanup_push ((void *)assuan_release, ctx);
255 rc = assuan_socket_connect (ctx, agent->socket, ASSUAN_INVALID_PID, 0);
256 if (!rc)
258 if (agent->ctx)
259 assuan_release (agent->ctx);
261 agent->ctx = ctx;
263 else
265 if (ctx)
266 assuan_release (ctx);
269 pthread_cleanup_pop (0);
270 return rc;
273 static gpg_error_t
274 send_to_agent (struct agent_s *agent, char **result, size_t *len,
275 const char *cmd)
277 gpg_error_t rc = 0;
279 if (agent->ctx)
280 rc = assuan_command (agent, result, len, cmd);
281 else
283 rc = agent_connect (agent);
284 if (!rc)
285 rc = assuan_command (agent, result, len, cmd);
288 if (!agent->restart && gpg_err_source (rc) == GPG_ERR_SOURCE_DEFAULT
289 && (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED
290 || gpg_err_code (rc) == GPG_ERR_EPIPE))
292 log_write (_ ("gpg-agent connection died (rc=%u), reconnecting"), rc);
293 agent->restart = 1;
294 rc = agent_connect (agent);
295 if (!rc)
297 agent->did_restart = 1;
298 rc = assuan_command (agent, result, len, cmd);
302 agent->restart = 0;
303 return rc;
306 gpg_error_t
307 agent_command (struct agent_s *agent, char **result, size_t * len,
308 const char *fmt, ...)
310 va_list ap;
311 char *cmd = NULL;
312 gpg_error_t rc;
314 va_start (ap, fmt);
316 if (str_vasprintf (&cmd, fmt, ap) > 0)
318 pthread_cleanup_push (xfree, cmd);
319 rc = send_to_agent (agent, result, len, cmd);
320 pthread_cleanup_pop (0);
322 else
323 rc = GPG_ERR_ENOMEM;
325 xfree (cmd);
326 va_end (ap);
327 return rc;
330 void
331 agent_disconnect (struct agent_s *agent)
333 if (!agent)
334 return;
336 if (agent->ctx)
337 assuan_release (agent->ctx);
339 agent->ctx = NULL;
342 void
343 agent_free (struct agent_s *agent)
345 if (!agent)
346 return;
348 agent_disconnect (agent);
349 xfree (agent->socket);
350 xfree (agent);
353 gpg_error_t
354 agent_init (struct agent_s **agent)
356 struct agent_s *new;
357 FILE *fp;
358 char *buf;
359 char line[PATH_MAX], *p;
360 gpg_error_t rc = 0;
361 int ret;
362 char *gpghome = config_get_string ("global", "gpg_homedir");
364 if (gpghome)
365 buf = str_asprintf ("gpgconf --homedir %s --list-dirs", gpghome);
366 else
367 buf = str_asprintf ("gpgconf --homedir %s/.gnupg --list-dirs", homedir);
369 xfree (gpghome);
370 fp = popen (buf, "r");
371 if (!fp)
373 rc = gpg_error_from_syserror ();
374 xfree (buf);
375 return rc;
378 xfree (buf);
380 while ((p = fgets (line, sizeof(line), fp)))
382 if (line[strlen(line)-1] == '\n')
383 line[strlen(line)-1] = 0;
385 if (!strncmp (line, "agent-socket:", 13))
387 buf = str_dup (line+13);
388 break;
392 ret = pclose (fp);
393 if (ret)
395 xfree (buf);
396 return GPG_ERR_ASS_GENERAL;
399 new = xcalloc (1, sizeof (struct agent_s));
400 if (!new)
402 xfree (buf);
403 return GPG_ERR_ENOMEM;
406 new->socket = buf;
407 *agent = new;
408 return 0;
411 gpg_error_t
412 agent_set_option (struct agent_s * agent, const char *name, const char *value)
414 return agent_command (agent, NULL, NULL, "OPTION %s=%s", name, value);
417 gpg_error_t
418 agent_kill_scd (struct agent_s *agent)
420 gpg_error_t rc = 0;
422 if (config_get_boolean (NULL, "kill_scd"))
424 rc = agent_command (agent, NULL, NULL, "SCD KILLSCD");
425 if (rc && gpg_err_code (rc) != GPG_ERR_NO_SCDAEMON
426 && gpg_err_code (rc) != GPG_ERR_EPIPE
427 && gpg_err_code (rc) != GPG_ERR_EOF)
428 log_write ("%s: ERR %u: %s", __FUNCTION__, rc, pwmd_strerror (rc));
431 return rc;