2 Copyright (C) 2016, 2017
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/>.
27 #include <sys/types.h>
37 #include "pwmd-error.h"
39 #include "util-misc.h"
43 #include "util-string.h"
49 mem_realloc_cb (void *data
, const void *buffer
, size_t len
)
51 membuf_t
*mem
= (membuf_t
*) data
;
57 if ((p
= xrealloc (mem
->buf
, mem
->len
+ len
)) == NULL
)
61 memcpy ((char *) mem
->buf
+ mem
->len
, buffer
, len
);
67 assuan_command (struct agent_s
*a
, char **result
,
68 size_t * len
, const char *cmd
)
79 rc
= assuan_transact (a
->ctx
, cmd
, mem_realloc_cb
, &a
->data
, NULL
, NULL
,
87 mem_realloc_cb (&a
->data
, "", 1);
89 *result
= (char *) a
->data
.buf
;
102 get_gpg_connect_agent_path (void)
104 gpgme_engine_info_t engine
;
107 gpgme_get_engine_info (&engine
);
110 if (engine
->protocol
== GPGME_PROTOCOL_GPGCONF
)
113 engine
= engine
->next
;
119 s
= str_dup (engine
->file_name
);
123 p
= strrchr (s
, '/');
127 p
= str_asprintf ("%s/gpg-connect-agent", s
);
137 agent_connect (struct agent_s
*agent
)
140 assuan_context_t ctx
= NULL
;
141 static struct assuan_malloc_hooks mhooks
= { xmalloc
, xrealloc
, xfree
};
143 agent
->did_restart
= 0;
148 rc
= gpgme_new (&gctx
);
153 pthread_cleanup_push ((void *)gpgme_release
, gctx
);
154 rc
= gpgme_set_protocol (gctx
, GPGME_PROTOCOL_SPAWN
);
155 if (!rc
&& strv_printf (&args
, ""))
157 if (!rc
&& strv_printf (&args
, "--homedir"))
159 char *gpghome
= config_get_string ("global", "gpg_homedir");
163 if (!strv_printf (&args
, "%s", gpghome
))
168 if (!strv_printf (&args
, "%s/.gnupg", homedir
))
180 pthread_cleanup_push ((void *)strv_free
, args
);
183 gpgme_data_t idata
= NULL
;
187 rc
= gpgme_data_new (&idata
);
190 char *gca
= get_gpg_connect_agent_path ();
192 pthread_cleanup_push (xfree
, gca
);
194 rc
= gpgme_op_spawn (gctx
, gca
, (const char **)args
, NULL
,
196 GPGME_SPAWN_DETACHED
|GPGME_SPAWN_ALLOW_SET_FG
);
200 pthread_cleanup_pop (1);
205 ssize_t ret
= gpgme_data_write (idata
, "/BYE\n", 5);
208 rc
= gpg_error_from_syserror ();
213 s
= gpgme_data_release_and_get_mem (idata
, &len
);
218 pthread_cleanup_pop (1);
219 pthread_cleanup_pop (1);
226 rc
= assuan_new_ext (&ctx
, GPG_ERR_SOURCE_DEFAULT
, &mhooks
, assuan_log_cb
,
231 pthread_cleanup_push ((void *)assuan_release
, ctx
);
232 rc
= assuan_socket_connect (ctx
, agent
->socket
, ASSUAN_INVALID_PID
, 0);
236 assuan_release (agent
->ctx
);
243 assuan_release (ctx
);
246 pthread_cleanup_pop (0);
251 send_to_agent (struct agent_s
*agent
, char **result
, size_t *len
,
257 rc
= assuan_command (agent
, result
, len
, cmd
);
260 rc
= agent_connect (agent
);
262 rc
= assuan_command (agent
, result
, len
, cmd
);
265 if (!agent
->restart
&& gpg_err_source (rc
) == GPG_ERR_SOURCE_DEFAULT
266 && (gpg_err_code (rc
) == GPG_ERR_ASS_CONNECT_FAILED
267 || gpg_err_code (rc
) == GPG_ERR_EPIPE
))
269 log_write (_ ("gpg-agent connection died (rc=%u), reconnecting"), rc
);
271 rc
= agent_connect (agent
);
274 agent
->did_restart
= 1;
275 rc
= assuan_command (agent
, result
, len
, cmd
);
284 agent_command (struct agent_s
*agent
, char **result
, size_t * len
,
285 const char *fmt
, ...)
293 if (str_vasprintf (&cmd
, fmt
, ap
) > 0)
295 pthread_cleanup_push (xfree
, cmd
);
296 rc
= send_to_agent (agent
, result
, len
, cmd
);
297 pthread_cleanup_pop (0);
308 agent_disconnect (struct agent_s
*agent
)
314 assuan_release (agent
->ctx
);
320 agent_free (struct agent_s
*agent
)
325 agent_disconnect (agent
);
326 xfree (agent
->socket
);
331 agent_init (struct agent_s
**agent
)
336 char line
[PATH_MAX
], *p
;
339 char *gpghome
= config_get_string ("global", "gpg_homedir");
342 buf
= str_asprintf ("gpgconf --homedir %s --list-dirs", gpghome
);
344 buf
= str_asprintf ("gpgconf --homedir %s/.gnupg --list-dirs", homedir
);
347 fp
= popen (buf
, "r");
350 rc
= gpg_error_from_syserror ();
358 while ((p
= fgets (line
, sizeof(line
), fp
)))
360 if (line
[strlen(line
)-1] == '\n')
361 line
[strlen(line
)-1] = 0;
363 if (!strncmp (line
, "agent-socket:", 13))
365 buf
= str_dup (line
+13);
374 return GPG_ERR_ASS_GENERAL
;
377 new = xcalloc (1, sizeof (struct agent_s
));
381 return GPG_ERR_ENOMEM
;
390 agent_set_option (struct agent_s
* agent
, const char *name
, const char *value
)
392 return agent_command (agent
, NULL
, NULL
, "OPTION %s=%s", name
, value
);
396 agent_kill_scd (struct agent_s
*agent
)
400 if (config_get_boolean (NULL
, "kill_scd"))
402 rc
= agent_command (agent
, NULL
, NULL
, "SCD KILLSCD");
403 if (rc
&& gpg_err_code (rc
) != GPG_ERR_NO_SCDAEMON
404 && gpg_err_code (rc
) != GPG_ERR_EPIPE
405 && gpg_err_code (rc
) != GPG_ERR_EOF
)
406 log_write ("%s: ERR %u: %s", __FUNCTION__
, rc
, pwmd_strerror (rc
));