Update copyright year.
[pwmd.git] / src / agent.c
blob8226aa31a3de294bea72ad3d75cb6bbefd599a58
1 /*
2 Copyright (C) 2016-2019 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 Pwmd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <sys/types.h>
27 #include <signal.h>
28 #include <sys/wait.h>
29 #include <fcntl.h>
30 #include <dirent.h>
32 #ifdef HAVE_LIMITS_H
33 #include <limits.h>
34 #endif
36 #include "pwmd-error.h"
37 #include <gpgme.h>
38 #include "util-misc.h"
39 #include "mem.h"
40 #include "common.h"
41 #include "agent.h"
42 #include "util-string.h"
43 #include "mutex.h"
44 #include "rcfile.h"
45 #include "cache.h"
47 static gpg_error_t
48 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
66 assuan_command (struct agent_s *a, char **result,
67 size_t * len, const char *cmd)
69 gpg_error_t rc;
71 a->data.len = 0;
72 a->data.buf = NULL;
73 if (result)
74 *result = NULL;
75 if (len)
76 *len = 0;
78 rc = assuan_transact (a->ctx, cmd, mem_realloc_cb, &a->data, NULL, NULL,
79 NULL, NULL);
80 if (rc)
81 xfree (a->data.buf);
82 else
84 if (a->data.buf)
86 mem_realloc_cb (&a->data, "", 1);
87 if (result)
88 *result = (char *) a->data.buf;
89 else
90 xfree (a->data.buf);
92 if (len)
93 *len = a->data.len;
97 return rc;
100 static char *
101 get_gpg_connect_agent_path (void)
103 gpgme_engine_info_t engine;
104 char *s, *p;
106 gpgme_get_engine_info (&engine);
107 while (engine)
109 if (engine->protocol == GPGME_PROTOCOL_GPGCONF)
110 break;
112 engine = engine->next;
115 if (!engine)
116 return NULL;
118 s = str_dup (engine->file_name);
119 if (!s)
120 return NULL;
122 p = strrchr (s, '/');
123 if (p)
125 *p = 0;
126 p = str_asprintf ("%s/gpg-connect-agent", s);
127 xfree (s);
128 return p;
131 xfree (s);
132 return NULL;
135 gpg_error_t
136 agent_connect (struct agent_s *agent)
138 gpg_error_t rc;
139 assuan_context_t ctx = NULL;
140 static struct assuan_malloc_hooks mhooks = { xmalloc, xrealloc, xfree };
142 agent->did_restart = 0;
143 if (agent->restart)
145 gpgme_ctx_t gctx;
147 rc = gpgme_new (&gctx);
148 if (!rc)
150 char **args = NULL;
152 pthread_cleanup_push ((void *)gpgme_release, gctx);
153 rc = gpgme_set_protocol (gctx, GPGME_PROTOCOL_SPAWN);
154 if (!rc && strv_printf (&args, ""))
156 if (!rc && strv_printf (&args, "--homedir"))
158 char *gpghome = config_get_string ("global", "gpg_homedir");
160 if (gpghome)
162 if (!strv_printf (&args, "%s", gpghome))
163 rc = GPG_ERR_ENOMEM;
165 else
167 if (!strv_printf (&args, "%s/.gnupg", homedir))
168 rc = GPG_ERR_ENOMEM;
171 xfree (gpghome);
173 else if (!rc)
174 rc = GPG_ERR_ENOMEM;
176 else if (!rc)
177 rc = GPG_ERR_ENOMEM;
179 pthread_cleanup_push ((void *)strv_free, args);
180 if (!rc)
182 gpgme_data_t idata = NULL;
183 size_t len;
184 char *s;
186 rc = gpgme_data_new (&idata);
187 if (!rc)
189 char *gca = get_gpg_connect_agent_path ();
191 pthread_cleanup_push (xfree, gca);
192 if (gca)
193 rc = gpgme_op_spawn (gctx, gca, (const char **)args, NULL,
194 idata, NULL,
195 GPGME_SPAWN_DETACHED|GPGME_SPAWN_ALLOW_SET_FG);
196 else
197 rc = GPG_ERR_ENOMEM;
199 pthread_cleanup_pop (1);
202 if (!rc)
204 ssize_t ret = gpgme_data_write (idata, "/BYE\n", 5);
206 if (ret != 5)
207 rc = gpg_error_from_syserror ();
210 if (idata)
212 s = gpgme_data_release_and_get_mem (idata, &len);
213 gpgme_free (s);
217 pthread_cleanup_pop (1);
218 pthread_cleanup_pop (1);
221 if (rc)
222 return rc;
225 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, assuan_log_cb,
226 NULL);
227 if (rc)
228 return rc;
230 pthread_cleanup_push ((void *)assuan_release, ctx);
231 rc = assuan_socket_connect (ctx, agent->socket, ASSUAN_INVALID_PID, 0);
232 if (!rc)
234 if (agent->ctx)
235 assuan_release (agent->ctx);
237 agent->ctx = ctx;
239 else
241 if (ctx)
242 assuan_release (ctx);
245 pthread_cleanup_pop (0);
246 return rc;
249 static gpg_error_t
250 send_to_agent (struct agent_s *agent, char **result, size_t *len,
251 const char *cmd)
253 gpg_error_t rc = 0;
255 if (agent->ctx)
256 rc = assuan_command (agent, result, len, cmd);
257 else
259 rc = agent_connect (agent);
260 if (!rc)
261 rc = assuan_command (agent, result, len, cmd);
264 if (!agent->restart && gpg_err_source (rc) == GPG_ERR_SOURCE_DEFAULT
265 && (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED
266 || gpg_err_code (rc) == GPG_ERR_EPIPE))
268 log_write (_ ("gpg-agent connection died (rc=%u), reconnecting"), rc);
269 agent->restart = 1;
270 rc = agent_connect (agent);
271 if (!rc)
273 agent->did_restart = 1;
274 rc = assuan_command (agent, result, len, cmd);
278 agent->restart = 0;
279 return rc;
282 gpg_error_t
283 agent_command (struct agent_s *agent, char **result, size_t * len,
284 const char *fmt, ...)
286 va_list ap;
287 char *cmd = NULL;
288 gpg_error_t rc;
290 va_start (ap, fmt);
292 if (str_vasprintf (&cmd, fmt, ap) > 0)
294 pthread_cleanup_push (xfree, cmd);
295 rc = send_to_agent (agent, result, len, cmd);
296 pthread_cleanup_pop (0);
298 else
299 rc = GPG_ERR_ENOMEM;
301 xfree (cmd);
302 va_end (ap);
303 return rc;
306 void
307 agent_disconnect (struct agent_s *agent)
309 if (!agent)
310 return;
312 if (agent->ctx)
313 assuan_release (agent->ctx);
315 agent->ctx = NULL;
318 void
319 agent_free (struct agent_s *agent)
321 if (!agent)
322 return;
324 agent_disconnect (agent);
325 xfree (agent->socket);
326 xfree (agent);
329 gpg_error_t
330 agent_init (struct agent_s **agent)
332 struct agent_s *new;
333 FILE *fp;
334 char *buf;
335 char line[PATH_MAX], *p;
336 gpg_error_t rc = 0;
337 int ret;
338 char *gpghome = config_get_string ("global", "gpg_homedir");
340 if (gpghome)
341 buf = str_asprintf ("gpgconf --homedir %s --list-dirs", gpghome);
342 else
343 buf = str_asprintf ("gpgconf --homedir %s/.gnupg --list-dirs", homedir);
345 xfree (gpghome);
346 fp = popen (buf, "r");
347 if (!fp)
349 rc = gpg_error_from_syserror ();
350 xfree (buf);
351 return rc;
354 xfree (buf);
355 buf = NULL;
357 while ((p = fgets (line, sizeof(line), fp)))
359 if (line[strlen(line)-1] == '\n')
360 line[strlen(line)-1] = 0;
362 if (!strncmp (line, "agent-socket:", 13))
364 buf = str_dup (line+13);
365 break;
369 ret = pclose (fp);
370 if (ret)
372 xfree (buf);
373 return GPG_ERR_ASS_GENERAL;
376 new = xcalloc (1, sizeof (struct agent_s));
377 if (!new)
379 xfree (buf);
380 return GPG_ERR_ENOMEM;
383 new->socket = buf;
384 *agent = new;
385 return 0;
388 gpg_error_t
389 agent_set_option (struct agent_s * agent, const char *name, const char *value)
391 return agent_command (agent, NULL, NULL, "OPTION %s=%s", name, value);
394 gpg_error_t
395 agent_kill_scd (struct agent_s *agent)
397 gpg_error_t rc = 0;
399 if (config_get_boolean (NULL, "kill_scd"))
401 rc = agent_command (agent, NULL, NULL, "SCD KILLSCD");
402 if (rc && gpg_err_code (rc) != GPG_ERR_NO_SCDAEMON
403 && gpg_err_code (rc) != GPG_ERR_EPIPE
404 && gpg_err_code (rc) != GPG_ERR_EOF)
405 log_write ("%s: ERR %u: %s", __FUNCTION__, rc, pwmd_strerror (rc));
408 return rc;