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/>.
26 #include <sys/types.h>
36 #include "pwmd-error.h"
38 #include "util-misc.h"
42 #include "util-string.h"
48 mem_realloc_cb (void *data
, const void *buffer
, size_t len
)
50 membuf_t
*mem
= (membuf_t
*) data
;
56 if ((p
= xrealloc (mem
->buf
, mem
->len
+ len
)) == NULL
)
60 memcpy ((char *) mem
->buf
+ mem
->len
, buffer
, len
);
66 assuan_command (struct agent_s
*a
, char **result
,
67 size_t * len
, const char *cmd
)
78 rc
= assuan_transact (a
->ctx
, cmd
, mem_realloc_cb
, &a
->data
, NULL
, NULL
,
86 mem_realloc_cb (&a
->data
, "", 1);
88 *result
= (char *) a
->data
.buf
;
101 get_gpg_connect_agent_path (void)
103 gpgme_engine_info_t engine
;
106 gpgme_get_engine_info (&engine
);
109 if (engine
->protocol
== GPGME_PROTOCOL_GPGCONF
)
112 engine
= engine
->next
;
118 s
= str_dup (engine
->file_name
);
122 p
= strrchr (s
, '/');
126 p
= str_asprintf ("%s/gpg-connect-agent", s
);
136 agent_connect (struct agent_s
*agent
)
139 assuan_context_t ctx
= NULL
;
140 static struct assuan_malloc_hooks mhooks
= { xmalloc
, xrealloc
, xfree
};
142 agent
->did_restart
= 0;
147 rc
= gpgme_new (&gctx
);
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");
162 if (!strv_printf (&args
, "%s", gpghome
))
167 if (!strv_printf (&args
, "%s/.gnupg", homedir
))
179 pthread_cleanup_push ((void *)strv_free
, args
);
182 gpgme_data_t idata
= NULL
;
186 rc
= gpgme_data_new (&idata
);
189 char *gca
= get_gpg_connect_agent_path ();
191 pthread_cleanup_push (xfree
, gca
);
193 rc
= gpgme_op_spawn (gctx
, gca
, (const char **)args
, NULL
,
195 GPGME_SPAWN_DETACHED
|GPGME_SPAWN_ALLOW_SET_FG
);
199 pthread_cleanup_pop (1);
204 ssize_t ret
= gpgme_data_write (idata
, "/BYE\n", 5);
207 rc
= gpg_error_from_syserror ();
212 s
= gpgme_data_release_and_get_mem (idata
, &len
);
217 pthread_cleanup_pop (1);
218 pthread_cleanup_pop (1);
225 rc
= assuan_new_ext (&ctx
, GPG_ERR_SOURCE_DEFAULT
, &mhooks
, assuan_log_cb
,
230 pthread_cleanup_push ((void *)assuan_release
, ctx
);
231 rc
= assuan_socket_connect (ctx
, agent
->socket
, ASSUAN_INVALID_PID
, 0);
235 assuan_release (agent
->ctx
);
242 assuan_release (ctx
);
245 pthread_cleanup_pop (0);
250 send_to_agent (struct agent_s
*agent
, char **result
, size_t *len
,
256 rc
= assuan_command (agent
, result
, len
, cmd
);
259 rc
= agent_connect (agent
);
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
);
270 rc
= agent_connect (agent
);
273 agent
->did_restart
= 1;
274 rc
= assuan_command (agent
, result
, len
, cmd
);
283 agent_command (struct agent_s
*agent
, char **result
, size_t * len
,
284 const char *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);
307 agent_disconnect (struct agent_s
*agent
)
313 assuan_release (agent
->ctx
);
319 agent_free (struct agent_s
*agent
)
324 agent_disconnect (agent
);
325 xfree (agent
->socket
);
330 agent_init (struct agent_s
**agent
)
335 char line
[PATH_MAX
], *p
;
338 char *gpghome
= config_get_string ("global", "gpg_homedir");
341 buf
= str_asprintf ("gpgconf --homedir %s --list-dirs", gpghome
);
343 buf
= str_asprintf ("gpgconf --homedir %s/.gnupg --list-dirs", homedir
);
346 fp
= popen (buf
, "r");
349 rc
= gpg_error_from_syserror ();
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);
373 return GPG_ERR_ASS_GENERAL
;
376 new = xcalloc (1, sizeof (struct agent_s
));
380 return GPG_ERR_ENOMEM
;
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
);
395 agent_kill_scd (struct agent_s
*agent
)
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
));