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 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);
80 inquire_cb (void *user
, const char *keyword
)
82 struct agent_s
*agent
= user
;
85 return assuan_send_data (agent
->ctx
, agent
->inquire
->line
,
90 assuan_command (struct agent_s
*a
, char **result
,
91 size_t * len
, const char *cmd
)
102 rc
= assuan_transact (a
->ctx
, cmd
, mem_realloc_cb
, &a
->data
,
103 inquire_cb
, a
, status_cb
, a
);
110 mem_realloc_cb (&a
->data
, "", 1);
112 *result
= (char *) a
->data
.buf
;
125 get_gpg_connect_agent_path (void)
127 gpgme_engine_info_t engine
;
130 gpgme_get_engine_info (&engine
);
133 if (engine
->protocol
== GPGME_PROTOCOL_GPGCONF
)
136 engine
= engine
->next
;
142 s
= str_dup (engine
->file_name
);
146 p
= strrchr (s
, '/');
150 p
= str_asprintf ("%s/gpg-connect-agent", s
);
160 agent_connect (struct agent_s
*agent
)
163 assuan_context_t ctx
= NULL
;
164 static struct assuan_malloc_hooks mhooks
= { xmalloc
, xrealloc
, xfree
};
166 agent
->did_restart
= 0;
171 rc
= gpgme_new (&gctx
);
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");
186 if (!strv_printf (&args
, "%s", gpghome
))
191 if (!strv_printf (&args
, "%s/.gnupg", homedir
))
203 pthread_cleanup_push ((void *)strv_free
, args
);
206 gpgme_data_t idata
= NULL
;
210 rc
= gpgme_data_new (&idata
);
213 char *gca
= get_gpg_connect_agent_path ();
215 pthread_cleanup_push (xfree
, gca
);
217 rc
= gpgme_op_spawn (gctx
, gca
, (const char **)args
, NULL
,
219 GPGME_SPAWN_DETACHED
|GPGME_SPAWN_ALLOW_SET_FG
);
223 pthread_cleanup_pop (1);
228 ssize_t ret
= gpgme_data_write (idata
, "/BYE\n", 5);
231 rc
= gpg_error_from_syserror ();
236 s
= gpgme_data_release_and_get_mem (idata
, &len
);
241 pthread_cleanup_pop (1);
242 pthread_cleanup_pop (1);
249 rc
= assuan_new_ext (&ctx
, GPG_ERR_SOURCE_DEFAULT
, &mhooks
, assuan_log_cb
,
254 pthread_cleanup_push ((void *)assuan_release
, ctx
);
255 rc
= assuan_socket_connect (ctx
, agent
->socket
, ASSUAN_INVALID_PID
, 0);
259 assuan_release (agent
->ctx
);
266 assuan_release (ctx
);
269 pthread_cleanup_pop (0);
274 send_to_agent (struct agent_s
*agent
, char **result
, size_t *len
,
280 rc
= assuan_command (agent
, result
, len
, cmd
);
283 rc
= agent_connect (agent
);
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
);
294 rc
= agent_connect (agent
);
297 agent
->did_restart
= 1;
298 rc
= assuan_command (agent
, result
, len
, cmd
);
307 agent_command (struct agent_s
*agent
, char **result
, size_t * len
,
308 const char *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);
331 agent_disconnect (struct agent_s
*agent
)
337 assuan_release (agent
->ctx
);
343 agent_free (struct agent_s
*agent
)
348 agent_disconnect (agent
);
349 xfree (agent
->socket
);
354 agent_init (struct agent_s
**agent
)
359 char line
[PATH_MAX
], *p
;
362 char *gpghome
= config_get_string ("global", "gpg_homedir");
365 buf
= str_asprintf ("gpgconf --homedir %s --list-dirs", gpghome
);
367 buf
= str_asprintf ("gpgconf --homedir %s/.gnupg --list-dirs", homedir
);
370 fp
= popen (buf
, "r");
373 rc
= gpg_error_from_syserror ();
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);
396 return GPG_ERR_ASS_GENERAL
;
399 new = xcalloc (1, sizeof (struct agent_s
));
403 return GPG_ERR_ENOMEM
;
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
);
418 agent_kill_scd (struct agent_s
*agent
)
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
));