Merge branch 'develop'

master scdtools-0.3.4
Damien Goutte-Gattat 6 years ago
commit cc791296c9
  1. 5
      NEWS
  2. 13
      configure.ac
  3. 256
      src/gpg-util.c
  4. 2
      src/scdrand.c

@ -1,3 +1,8 @@
Changes in scdtools 0.3.4
* Fix search for GPG-Agent socket when GNUPG_HOME is set?
Changes in scdtools 0.3.3
* Look for GPG-Agent socket in [/var]/run.

@ -1,6 +1,6 @@
dnl Configure template for the scdrand package
AC_INIT([scdtools], [0.3.3],
AC_INIT([scdtools], [0.3.4],
[dgouttegattat@incenp.org])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_MACRO_DIR([m4])
@ -33,6 +33,15 @@ AM_PATH_GPG_ERROR([1.11],
LIBS="$LIBS $GPG_ERROR_LIBS"],
[AC_MSG_ERROR([libgpg-error not found])])
dnl Path to gpg-connect-agent
AC_ARG_WITH([gpg-connect-agent],
[AS_HELP_STRING([--with-gpg-connect-agent],
[path to gpg-connect-agent @<:@default=/usr/bin/gpg-connect-agent@:>@])],
[gpg_connect_agent=$with_gpg_connect_agent],
[gpg_connect_agent=/usr/bin/gpg-connect-agent])
AC_DEFINE_UNQUOTED([GPG_CONNECT_AGENT_PATH], ["$gpg_connect_agent"],
[Path to gpg-connect-agent binary.])
dnl Default private DO for scdtotp
AC_ARG_WITH([default-do],
[AS_HELP_STRING([--with-default-do=N],
@ -59,4 +68,6 @@ Configuration complete
Prefix: '${prefix}'
Compiler: '${CC} ${CFLAGS} ${CPPFLAGS}'
gpg-connect-agent: '${gpg_connect_agent}'
"

@ -1,6 +1,6 @@
/*
* scdtools - Tools for Scdaemon and OpenPGP smartcards
* Copyright (C) 2014,2015,2016 Damien Goutte-Gattat
* Copyright (C) 2014,2015,2016,2017 Damien Goutte-Gattat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -36,94 +36,137 @@
#include "gpg-util.h"
/*
* Find the socket of GnuPG Agent. First look for the GPG_AGENT_INFO
* environment variable, if unset look for the standard socket in
* GnuPG's home directory, if not found use gpg-connect-agent.
*
* The socket name is stored in a newly allocated buffer that should
* be freed by the caller.
* Check that a socket exists at the specified location.
*
* Return NULL if the socket could not be found.
* Return 0 if the socket does exist, or a gpg_error_t error code.
*/
static char *
get_agent_socket_name(void)
static gpg_error_t
check_socket(const char *socket_name)
{
char *env_info, *socket_name = NULL;
char buf[255] = "";
struct stat st;
gpg_error_t e;
/* First look for GPG_AGENT_INFO in the environment. */
if ( (env_info = getenv("GPG_AGENT_INFO")) ) {
if ( (socket_name = strdup(env_info)) ) {
char *colon_index;
/*
* GPG_AGENT_INFO should be of the form
* "path_to_socket:x:y", where x is the PID
* of the GnuPG Agent process and y is the
* protocol version (currently 1).
*/
if ( (colon_index = strchr(socket_name, ':')) )
*colon_index = '\0';
}
}
if ( stat(socket_name, &st) == -1 )
e = gpg_err_code_from_errno(errno);
else if ( ! S_ISSOCK(st.st_mode) )
e = gpg_err_code_from_errno(ENOTSOCK);
else
e = GPG_ERR_NO_ERROR;
if ( ! socket_name ) {
/* Then look for the standard socket
* in GnuPG's home directory. */
struct passwd *pwd;
return e;
}
if ( (env_info = getenv("GNUPGHOME")) )
snprintf(buf, sizeof(buf), "%s/S.gpg-agent", env_info);
else if ( (env_info = getenv("HOME")) )
snprintf(buf, sizeof(buf), "%s/.gnupg/S.gpg-agent", env_info);
else if ( (pwd = getpwuid(getuid())) )
snprintf(buf, sizeof(buf), "%s/.gnupg/S.gpg-agent", pwd->pw_dir);
/*
* Convenience function: it does the same as check_socket above
* but accept a printf-like format string to construct the path
* before testing it.
*/
static gpg_error_t
check_socket_fmt(char *buffer, size_t len, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, len, fmt, ap);
va_end(ap);
return check_socket(buffer);
}
/*
* Find the socket of GnuPG Agent. First look for the GPG_AGENT_INFO
* environment variable, if unset look for the socket at some
* standard locations, if not found use gpg-connect-agent.
*
* Return 0 if the socket is found, or a gpg_error_t error code.
*/
static gpg_error_t
get_agent_socket_name(char *buffer, size_t len)
{
char *env_info = NULL;
gpg_error_t e = GPG_ERR_NO_AGENT;
/* First look for GPG_AGENT_INFO in the environment
* (GnuPG < 2.1). */
if ( (env_info = getenv("GPG_AGENT_INFO")) ) {
unsigned proto;
char fmt[32];
/*
* GPG_AGENT_INFO should be of the form
* "path_to_socket:x:y", where x is the PID
* of the GnuPG Agent process and y is the
* protocol version (currently always 1).
*/
snprintf(fmt, sizeof(fmt), "%%%lus:%%*u:%%i", len);
if ( sscanf(env_info, fmt, buffer, &proto) != 2 )
e = gcry_error(GPG_ERR_INV_VALUE);
else if ( proto != 1 )
e = gcry_error(GPG_ERR_UNSUPPORTED_PROTOCOL);
else
e = check_socket(buffer);
/* Check whether the socket does exist. */
if ( *buf && stat(buf, &st) != -1 && S_ISSOCK(st.st_mode) )
socket_name = strdup(buf);
/*
* If the GPG_AGENT_INFO variable exists, it must
* point to a suitable socket; otherwise, abort
* here without looking elsewhere.
*/
if ( e )
return e;
}
if ( ! socket_name ) {
/* Then look for the standard socket outside of GnuPG's
* home directory (for GnuPG >= 2.1.17). */
/* If GNUPGHOME is defined, try looking for a socket in
* the specified directory. */
if ( e && (env_info = getenv("GNUPGHOME")) )
e = check_socket_fmt(buffer, len, "%s/S.gpg-agent", env_info);
/* Without GNUPGHOME, try looking in standard directories. */
if ( e && ! env_info ) {
static const char *prefixes[] = { "/run", "/var/run", NULL };
struct passwd *pwd;
int i;
for ( i = 0; prefixes[i] && ! socket_name ; i++ ) {
snprintf(buf, sizeof(buf), "%s/user/%u/gnupg/S.gpg-agent",
prefixes[i], getuid());
if ( ! (env_info = getenv("HOME")) && (pwd = getpwuid(getuid())) )
env_info = pwd->pw_dir;
if ( stat(buf, &st) != -1 && S_ISSOCK(st.st_mode) )
socket_name = strdup(buf);
}
if ( env_info )
e = check_socket_fmt(buffer, len, "%s/.gnupg/S.gpg-agent",
env_info);
/* If no socket was found in GnuPG's home directory,
* try looking under [/var]/run (for GnuPG >= 2.1.13). */
for ( i = 0; e && prefixes[i]; i++ )
e = check_socket_fmt(buffer, len, "%s/user/%u/gnupg/S.gpg-agent",
prefixes[i], getuid());
}
if ( ! socket_name ) {
/*
* Finally, try to ask the agent directly. This method could
* replace all the methods above, but since it requires spawing
* a 0new process, we use it only as a fallback. We expect that
* most of the time, the agent should already be running when
* our programs are called, and thus the socket would already
* exist and be found by the above code.
*/
/*
* Finally, try asking the agent directly. This methold could
* replace all the methods above, but since it requires spawning
* a new process, we use it only as a fallback. We expect that
* most of the time, the agent should already be running when our
* programs are called, and thus the socket would already exist
* and be found by the above code.
*/
if ( e ) {
FILE *f;
f = popen("gpg-connect-agent 'GETINFO socket_name' /bye", "r");
f = popen(GPG_CONNECT_AGENT_PATH " 'GETINFO socket_name' /bye", "r");
if ( f ) {
if ( fscanf(f, "D %254s\nOK\n", buf) == 1 )
socket_name = strdup(buf);
char fmt[32];
snprintf(fmt, sizeof(fmt), "D %%%lus\nOK\n", len);
if ( fscanf(f, fmt, buffer) == 1 )
e = check_socket(buffer);
pclose(f);
}
}
return socket_name;
return e;
}
/*
* Establish a connection with a running GnuPG Agent.
*
@ -138,11 +181,11 @@ get_agent_socket_name(void)
gpg_error_t
connect_to_agent(assuan_context_t *ctx, int init_env)
{
char *socket_name;
char socket_name[255];
gpg_error_t e;
if ( ! (socket_name = get_agent_socket_name()) )
return gcry_error(GPG_ERR_NO_AGENT);
if ( (e = get_agent_socket_name(socket_name, sizeof(socket_name))) )
return e;
if ( ! (e = assuan_new(ctx)) ) {
if ( ! (e = assuan_socket_connect(*ctx, socket_name, ASSUAN_INVALID_PID, 0)) ) {
@ -153,52 +196,87 @@ connect_to_agent(assuan_context_t *ctx, int init_env)
assuan_release(*ctx);
}
free(socket_name);
return e;
}
struct pstring {
size_t len;
char *buffer;
};
/*
* Callback for the below function.
*/
static gpg_error_t
socket_name_cb(void *arg, const void *line, size_t len)
{
char **socket_name = (char **)arg;
struct pstring *p = (struct pstring *)arg;
(void)len;
if ( len > p->len - 1 )
return gcry_error(GPG_ERR_BUFFER_TOO_SHORT);
if ( ! (*socket_name = strdup(line)) )
return gcry_error_from_errno(errno);
strncpy(p->buffer, line, p->len);
return 0;
}
/*
* Find the socket of Scdaemon. Attempt to connect to a running
* GnuPG Agent and inquire about Scdaemon's running status and
* socket name.
*
* @param socket_name A pointer to a string to store the name of
* Scdaemon's socket. Should be freed by the
* caller after use.
* Find the socket of Scdaemon. Look for the socket in some
* standard locations, and if not found ask to the agent.
*
* @return 0 if Scdaemon's socket was found, or a gpg_error_t
* error code otherwise.
*/
static gpg_error_t
get_scd_socket_name(char **scd_socket_name)
get_scd_socket_name(char *buffer, size_t len)
{
assuan_context_t ctx;
gpg_error_t e;
char *env_info = NULL;
gpg_error_t e = GPG_ERR_NO_SCDAEMON;
/*
* This function roughly follows the same logic as the
* get_agent_socket_name above:
* - first look for $GNUPGHOME/S.scdaemon;
* - then and if GNUPGHOME is not set, look in
* GnuPG's default home directory and under [/var]/run;
* - as a fallback, send a request to the GnuPG Agent.
*/
if ( (env_info = getenv("GNUPGHOME")) )
e = check_socket_fmt(buffer, len, "%s/S.scdaemon", env_info);
if ( e && ! env_info ) {
static const char *prefixes[] = { "/run", "/var/run", NULL };
struct passwd *pwd;
int i;
if ( ! (env_info = getenv("HOME")) && (pwd = getpwuid(getuid())) )
env_info = pwd->pw_dir;
if ( env_info )
e = check_socket_fmt(buffer, len, "%s/.gnupg/S.scdaemon", env_info);
for ( i = 0; e && prefixes[i]; i++ )
e = check_socket_fmt(buffer, len, "%s/user/%u/gnupg/S.scdaemon",
prefixes[i], getuid());
}
if ( e ) {
assuan_context_t ctx;
if ( ! (e = connect_to_agent(&ctx, 0)) ) {
if ( ! (e = connect_to_agent(&ctx, 0)) ) {
if ( ! (e = assuan_transact(ctx, "GETINFO scd_running",
NULL, NULL, NULL, NULL, NULL, NULL)) ) {
struct pstring p;
if ( ! (e = assuan_transact(ctx, "GETINFO scd_running",
NULL, NULL, NULL, NULL, NULL, NULL)) ) {
p.len = len;
p.buffer = buffer;
e = assuan_transact(ctx, "SCD GETINFO socket_name",
socket_name_cb, &p, NULL, NULL, NULL, NULL);
e = assuan_transact(ctx, "SCD GETINFO socket_name",
socket_name_cb, scd_socket_name, NULL, NULL, NULL, NULL);
if ( ! e )
e = check_socket(buffer);
}
}
assuan_release(ctx);
@ -219,10 +297,10 @@ get_scd_socket_name(char **scd_socket_name)
gpg_error_t
connect_to_scdaemon(assuan_context_t *ctx)
{
char *socket_name;
char socket_name[255];
gpg_error_t e;
if ( (e = get_scd_socket_name(&socket_name)) )
if ( (e = get_scd_socket_name(socket_name, sizeof(socket_name))) )
return e;

@ -86,7 +86,7 @@ info(void)
{
printf("\
scdrand (scdtools %s)\n\
Copyright (C) 2016 Damien Goutte-Gattat\n\
Copyright (C) 2017 Damien Goutte-Gattat\n\
\n\
This program is released under the GNU General Public License.\n\
See the COPYING file or <http://www.gnu.org/licenses/gpl.html>.\n\

Loading…
Cancel
Save