Add the gpg-util module

Move all code related to establishing connections with GnuPG
daemons (GnuPG Agent, Scdaemon) into a separated module.
develop
Damien Goutte-Gattat 8 years ago
parent dc817a13ab
commit da0037bad8
  1. 2
      src/Makefile.am
  2. 208
      src/gpg-util.c
  3. 38
      src/gpg-util.h
  4. 123
      src/scdrand.c

@ -1,6 +1,6 @@
bin_PROGRAMS = scdrand
scdrand_SOURCES = scdrand.c
scdrand_SOURCES = scdrand.c gpg-util.c gpg-util.h
AM_CPPFLAGS = -I$(top_srcdir)/lib
AM_LDFLAGS = -L$(top_builddir)/lib

@ -0,0 +1,208 @@
/*
* scdtools - Tools for Scdaemon and OpenPGP smartcards
* Copyright (C) 2014 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <pwd.h>
#include <gcrypt.h>
#include "gpg-util.h"
/*
* Wrap call to asprintf to make sure that when a memory allocation
* error occurs, errno is set to ENOMEM and the string pointer is
* set to NULL.
*/
static int
do_asprintf(char **strp, const char *fmt, ...)
{
int n;
va_list ap;
va_start(ap, fmt);
if ( (n = vasprintf(strp, fmt, ap)) == -1 ) {
errno = ENOMEM;
*strp = NULL;
}
va_end(ap);
return n;
}
/*
* Find the socket of GnuPG Agent. First look for the GPG_AGENT_INFO
* environment variable, and if unset look for the standard socket
* in GnuPG's home directory.
*
* The socket name is stored in a newly allocated buffer that should
* be freed by the caller.
*
* May return NULL upon memory allocation error, or if the user's
* home directory cannot be found.
*/
static char *
get_agent_socket_name(void)
{
char *env_info, *socket_name;
struct passwd *pwd;
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 and y is the protocol
* version (currently 1).
*/
if ( (colon_index = strchr(socket_name, ':')) )
*colon_index = '\0';
}
}
else if ( (env_info = getenv("GNUPGHOME")) )
do_asprintf(&socket_name, "%s/S.gpg-agent", env_info);
else if ( (env_info = getenv("HOME")) )
do_asprintf(&socket_name, "%s/.gnupg/S.gpg-agent", env_info);
else if ( (pwd = getpwuid(getuid())) )
do_asprintf(&socket_name, "%s/.gnupg/S.gpg-agent", pwd->pw_dir);
else
/* No way to get the user's home directory?
* Can that ever happen? */
socket_name = NULL;
return socket_name;
}
/*
* Establish a connection with a running GnuPG Agent.
*
* @param ctx A pointer to a assuan_context_t object that will be
* initialized by this function.
*
* @return 0 if the connection was established, or a gpg_error_t
* error code.
*/
gpg_error_t
connect_to_agent(assuan_context_t *ctx)
{
char *socket_name;
gpg_error_t e;
if ( ! (socket_name = get_agent_socket_name()) )
return gcry_error(GPG_ERR_NO_AGENT);
if ( ! (e = assuan_new(ctx)) ) {
if ( ! (e = assuan_socket_connect(*ctx, socket_name, ASSUAN_INVALID_PID, 0)) )
e = gcry_error(GPG_ERR_NO_ERROR);
else
assuan_release(*ctx);
}
free(socket_name);
return e;
}
/*
* 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;
if ( ! (*socket_name = strdup(line)) )
return gcry_error_from_errno(errno);
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.
*
* @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)
{
assuan_context_t ctx;
gpg_error_t e;
if ( ! (e = connect_to_agent(&ctx)) ) {
if ( ! (e = assuan_transact(ctx, "GETINFO scd_running",
NULL, NULL, NULL, NULL, NULL, NULL)) ) {
e = assuan_transact(ctx, "SCD GETINFO socket_name",
socket_name_cb, scd_socket_name, NULL, NULL, NULL, NULL);
}
assuan_release(ctx);
}
return e;
}
/*
* Establish a connection with a running Scdaemon.
*
* @param ctx A pointer to a assuan_context_t object that will be
* initialized by this function.
*
* @return 0 if the connection was established, or a gpg_error_t
* error code.
*/
gpg_error_t
connect_to_scdaemon(assuan_context_t *ctx)
{
char *socket_name;
gpg_error_t e;
if ( (e = get_scd_socket_name(&socket_name)) )
return e;
if ( ! (e = assuan_new(ctx)) ) {
if ( ! (e = assuan_socket_connect(*ctx, socket_name, ASSUAN_INVALID_PID, 0)) )
e = gcry_error(GPG_ERR_NO_ERROR);
else
assuan_release(*ctx);
}
return e;
}

@ -0,0 +1,38 @@
/*
* scdtools - Tools for Scdaemon and OpenPGP smartcards
* Copyright (C) 2014 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ICP20141218_GPGUTIL_H
#define ICP20141218_GPGUTIL_H
#include <assuan.h>
#ifdef __cpluscplus
extern "C" {
#endif
gpg_error_t
connect_to_agent(assuan_context_t *);
gpg_error_t
connect_to_scdaemon(assuan_context_t *);
#ifdef __cplusplus
}
#endif
#endif /* !ICP20141218_GPGUTIL_H */

@ -32,13 +32,11 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pwd.h>
#include <linux/random.h>
#include <assuan.h>
#include <xmem.h>
#include "xmem.h"
#include "gpg-util.h"
#define MAX_RANDOM_BYTES 256
#define DEFAULT_RANDOM_BYTES 8
@ -90,91 +88,6 @@ See the COPYING file or <http://www.gnu.org/licenses/gpl.html>.\n\
exit(EXIT_SUCCESS);
}
/*
* Obtain the pathname of a running GPG Agent's listening socket
* from the environment. Terminate the program if the socket
* cannnot be found.
*
* @return
* A newly allocated string containing the socket's pathname,
* to be freed by the caller.
*/
static char *
get_gpg_agent_socket_name(void)
{
char *env_info, *socket_name, *colon_index;
struct passwd *pwd;
if ( (env_info = getenv("GPG_AGENT_INFO")) ) {
socket_name = xstrdup(env_info);
if ( (colon_index = strchr(socket_name, ':')) )
*colon_index = '\0';
}
else if ( (env_info = getenv("GNUPGHOME")) )
asprintf(&socket_name, "%s/S.gpg-agent", env_info);
else if ( (env_info = getenv("HOME")) )
asprintf(&socket_name, "%s/.gnupg/S.gpg-agent", env_info);
else if ( (pwd = getpwuid(getuid())) )
asprintf(&socket_name, "%s/.gnupg/S.gpg-agent", pwd->pw_dir);
else
errx(EXIT_FAILURE, "Cannot locate GPG Agent socket");
return socket_name;
}
/*
* Data callback for the below function.
*/
gpg_error_t
scd_running_data_cb(void *arg, const void *line, size_t len)
{
*((char **)arg) = xstrdup(line);
return 0;
}
/*
* Obtain the pathname of a running scdaemon's listening socket.
* Terminate the program if the socket cannot be found.
*
* @return
* A newly allocated string containing the socket's pathname,
* to be freed by the caller.
*/
static char *
get_scd_socket_name(void)
{
char *gpg_agent_socket_name, *scd_socket_name;
assuan_context_t ctx;
gpg_error_t ge;
gpg_agent_socket_name = get_gpg_agent_socket_name();
if ( (ge = assuan_new(&ctx)) )
errx(EXIT_FAILURE, "Cannot create Assuan context: %s",
gpg_strerror(ge));
if ( (ge = assuan_socket_connect(ctx, gpg_agent_socket_name,
ASSUAN_INVALID_PID, 0)) )
errx(EXIT_FAILURE, "Cannot connect to GPG Agent: %s",
gpg_strerror(ge));
if ( (ge = assuan_transact(ctx, "GETINFO scd_running",
NULL, NULL, NULL, NULL, NULL, NULL)) )
errx(EXIT_FAILURE, "No running scdaemon found");
if ( (ge = assuan_transact(ctx, "SCD GETINFO socket_name",
scd_running_data_cb, &scd_socket_name,
NULL, NULL, NULL, NULL)) )
errx(EXIT_FAILURE, "Cannot obtain scdaemon's socket name: %s",
gpg_strerror(ge));
assuan_release(ctx);
free(gpg_agent_socket_name);
return scd_socket_name;
}
/*
* Represents a response to a GET CHALLENGE command.
@ -234,33 +147,6 @@ get_challenge(assuan_context_t ctx, unsigned char *buffer, size_t len)
return c.len;
}
/*
* Connect to a running scdaemon.
*
* @return
* An Assuan context connected to the scdaemon.
*/
static assuan_context_t
connect_to_scdaemon(void)
{
char *socket_name;
assuan_context_t ctx;
gpg_error_t ge;
socket_name = get_scd_socket_name();
if ( (ge = assuan_new(&ctx)) )
errx(EXIT_FAILURE, "Cannot create Assuan context: %s",
gpg_strerror(ge));
if ( (ge = assuan_socket_connect(ctx, socket_name, ASSUAN_INVALID_PID, 0)) )
errx(EXIT_FAILURE, "Cannot connect to scdaemon: %s", gpg_strerror(ge));
free(socket_name);
return ctx;
}
/*
* Wait until the available entropy falls below the specified threshold.
* Available entropy is checked regularly at the specified interval.
@ -372,6 +258,7 @@ main(int argc, char **argv)
int c, fd, n, loop;
unsigned nbytes, interval, threshold;
assuan_context_t ctx;
gpg_error_t e;
unsigned char random_buffer[MAX_RANDOM_BYTES];
struct option options[] = {
@ -439,7 +326,9 @@ main(int argc, char **argv)
#endif
assuan_set_gpg_err_source(GPG_ERR_SOURCE_USER_1);
ctx = connect_to_scdaemon();
if ( (e = connect_to_scdaemon(&ctx)) )
err(EXIT_FAILURE, "Cannot connect to Scdaemon: %s", gcry_strerror(e));
while ( loop == -1 || loop-- > 0 ) {
if ( threshold != 0 )

Loading…
Cancel
Save