1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-27 15:47:05 +01:00

2004-07-27 Moritz Schulte <moritz@g10code.com>

* command-ssh.c: Use gcrypt memory allocators, use secure memory
	where necessary.

2004-07-26  Moritz Schulte  <moritz@g10code.com>

	* command-ssh.c (data_sign): Do not forget to unsigned char when
	constructing human-readable key grip.

	* Makefile.am (gpg_agent_SOURCES): Removed: buffer.c, buffer.h;
	updated Libgpg-stream.
This commit is contained in:
Moritz Schulte 2004-07-26 23:01:19 +00:00
parent da2899b712
commit 809b77941f
5 changed files with 571 additions and 73 deletions

View File

@ -1,3 +1,16 @@
2004-07-27 Moritz Schulte <moritz@g10code.com>
* command-ssh.c: Use gcrypt memory allocators, use secure memory
where necessary.
2004-07-26 Moritz Schulte <moritz@g10code.com>
* command-ssh.c (data_sign): Do not forget to unsigned char when
constructing human-readable key grip.
* Makefile.am (gpg_agent_SOURCES): Removed: buffer.c, buffer.h;
updated Libgpg-stream.
2004-07-24 Moritz Schulte <moritz@g10code.com> 2004-07-24 Moritz Schulte <moritz@g10code.com>
* gpg-stream-config.h: New file (was missing before). * gpg-stream-config.h: New file (was missing before).

View File

@ -30,7 +30,6 @@ AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
gpg_agent_SOURCES = \ gpg_agent_SOURCES = \
gpg-agent.c agent.h \ gpg-agent.c agent.h \
gpg-stream.c gpg-stream.h gpg-stream-config.h \ gpg-stream.c gpg-stream.h gpg-stream-config.h \
buffer.c buffer.h \
command.c command-ssh.c \ command.c command-ssh.c \
query.c \ query.c \
cache.c \ cache.c \

View File

@ -26,7 +26,7 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
//#include <stdio.h> #include <stdio.h>
#include "agent.h" #include "agent.h"
@ -227,7 +227,7 @@ gpg_stream_write_uint32 (gpg_stream_t stream, uint32_t uint32)
} }
static gpg_err_code_t static gpg_err_code_t
gpg_stream_read_string (gpg_stream_t stream, gpg_stream_read_string (gpg_stream_t stream, unsigned int secure,
unsigned char **string, uint32_t *string_size) unsigned char **string, uint32_t *string_size)
{ {
gpg_err_code_t err = GPG_ERR_NO_ERROR; gpg_err_code_t err = GPG_ERR_NO_ERROR;
@ -241,7 +241,10 @@ gpg_stream_read_string (gpg_stream_t stream,
goto out; goto out;
/* Allocate space. */ /* Allocate space. */
buffer = malloc (length + 1); if (secure)
buffer = gcry_malloc_secure (length + 1);
else
buffer = gcry_malloc (length + 1);
if (! buffer) if (! buffer)
{ {
err = gpg_err_code_from_errno (errno); err = gpg_err_code_from_errno (errno);
@ -268,7 +271,7 @@ gpg_stream_read_string (gpg_stream_t stream,
} }
else else
if (buffer) if (buffer)
free (buffer); gcry_free (buffer);
return err; return err;
} }
@ -303,8 +306,8 @@ gpg_stream_write_cstring (gpg_stream_t stream, char *string)
} }
static gpg_err_code_t static gpg_err_code_t
gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint, gpg_stream_read_mpint (gpg_stream_t stream, unsigned int secure,
unsigned int mpi_type) mpint_t *mpint, unsigned int mpi_type)
{ {
gpg_err_code_t err = GPG_ERR_NO_ERROR; gpg_err_code_t err = GPG_ERR_NO_ERROR;
unsigned char *mpi_data = NULL; unsigned char *mpi_data = NULL;
@ -314,7 +317,8 @@ gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint,
if (! mpi_type) if (! mpi_type)
mpi_type = GCRYMPI_FMT_STD; mpi_type = GCRYMPI_FMT_STD;
err = gpg_stream_read_string (stream, &mpi_data, &mpi_data_size); err = gpg_stream_read_string (stream, secure,
&mpi_data, &mpi_data_size);
if (err) if (err)
goto out; goto out;
@ -324,7 +328,7 @@ gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint,
out: out:
free (mpi_data); gcry_free (mpi_data);
if (! err) if (! err)
*mpint = mpi; *mpint = mpi;
@ -333,8 +337,8 @@ gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint,
} }
static gpg_err_code_t static gpg_err_code_t
gpg_stream_write_mpint (gpg_stream_t stream, mpint_t mpint, gpg_stream_write_mpint (gpg_stream_t stream,
unsigned int mpi_type) mpint_t mpint, unsigned int mpi_type)
{ {
gpg_err_code_t err = GPG_ERR_NO_ERROR; gpg_err_code_t err = GPG_ERR_NO_ERROR;
unsigned char *mpi_buffer = NULL; unsigned char *mpi_buffer = NULL;
@ -376,7 +380,7 @@ gpg_stream_read_file (const char *filename,
if (err) if (err)
goto out; goto out;
buffer_new = malloc (buffer_new_n); buffer_new = gcry_malloc (buffer_new_n);
if (! buffer_new) if (! buffer_new)
{ {
err = gpg_err_code_from_errno (errno); err = gpg_err_code_from_errno (errno);
@ -436,7 +440,7 @@ ssh_receive_key_secret (gpg_stream_t stream, ssh_key_secret_t *key_secret)
unsigned char *key_type = NULL; unsigned char *key_type = NULL;
gcry_mpi_t mpi_iqmp = NULL; gcry_mpi_t mpi_iqmp = NULL;
err = gpg_stream_read_string (stream, &key_type, NULL); err = gpg_stream_read_string (stream, 0, &key_type, NULL);
if (err) if (err)
goto out; goto out;
@ -448,22 +452,22 @@ ssh_receive_key_secret (gpg_stream_t stream, ssh_key_secret_t *key_secret)
{ {
case SSH_KEY_TYPE_RSA: case SSH_KEY_TYPE_RSA:
{ {
err = gpg_stream_read_mpint (stream, &key.material.rsa.n, 0); err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.n, 0);
if (err) if (err)
break; break;
err = gpg_stream_read_mpint (stream, &key.material.rsa.e, 0); err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.e, 0);
if (err) if (err)
break; break;
err = gpg_stream_read_mpint (stream, &key.material.rsa.d, 0); err = gpg_stream_read_mpint (stream, 1, &key.material.rsa.d, 0);
if (err) if (err)
break; break;
err = gpg_stream_read_mpint (stream, &mpi_iqmp, 0); err = gpg_stream_read_mpint (stream, 1, &mpi_iqmp, 0);
if (err) if (err)
break; break;
err = gpg_stream_read_mpint (stream, &key.material.rsa.p, 0); err = gpg_stream_read_mpint (stream, 1, &key.material.rsa.p, 0);
if (err) if (err)
break; break;
err = gpg_stream_read_mpint (stream, &key.material.rsa.q, 0); err = gpg_stream_read_mpint (stream, 1, &key.material.rsa.q, 0);
if (err) if (err)
break; break;
@ -483,7 +487,7 @@ ssh_receive_key_secret (gpg_stream_t stream, ssh_key_secret_t *key_secret)
{ {
/* u has to be recomputed. */ /* u has to be recomputed. */
key.material.rsa.u = gcry_mpi_new (0); key.material.rsa.u = gcry_mpi_snew (0);
gcry_mpi_invm (key.material.rsa.u, gcry_mpi_invm (key.material.rsa.u,
key.material.rsa.p, key.material.rsa.q); key.material.rsa.p, key.material.rsa.q);
} }
@ -501,19 +505,27 @@ ssh_receive_key_secret (gpg_stream_t stream, ssh_key_secret_t *key_secret)
out: out:
free (key_type); gcry_free (key_type);
gcry_mpi_release (mpi_iqmp); gcry_mpi_release (mpi_iqmp);
if (! err) if (! err)
*key_secret = key; *key_secret = key;
else else
{ {
switch (key.type)
{
case SSH_KEY_TYPE_RSA:
gcry_mpi_release (key.material.rsa.n); gcry_mpi_release (key.material.rsa.n);
gcry_mpi_release (key.material.rsa.e); gcry_mpi_release (key.material.rsa.e);
gcry_mpi_release (key.material.rsa.d); gcry_mpi_release (key.material.rsa.d);
gcry_mpi_release (key.material.rsa.p); gcry_mpi_release (key.material.rsa.p);
gcry_mpi_release (key.material.rsa.q); gcry_mpi_release (key.material.rsa.q);
gcry_mpi_release (key.material.rsa.u); gcry_mpi_release (key.material.rsa.u);
break;
case SSH_KEY_TYPE_NONE:
break;
}
} }
return err; return err;
@ -558,7 +570,7 @@ ssh_receive_key_public (gpg_stream_t stream, ssh_key_public_t *key_public)
ssh_key_public_t key = { 0 }; ssh_key_public_t key = { 0 };
unsigned char *key_type = NULL; unsigned char *key_type = NULL;
err = gpg_stream_read_string (stream, &key_type, NULL); err = gpg_stream_read_string (stream, 0, &key_type, NULL);
if (err) if (err)
goto out; goto out;
@ -570,10 +582,10 @@ ssh_receive_key_public (gpg_stream_t stream, ssh_key_public_t *key_public)
{ {
case SSH_KEY_TYPE_RSA: case SSH_KEY_TYPE_RSA:
{ {
err = gpg_stream_read_mpint (stream, &key.material.rsa.e, 0); err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.e, 0);
if (err) if (err)
break; break;
err = gpg_stream_read_mpint (stream, &key.material.rsa.n, 0); err = gpg_stream_read_mpint (stream, 0, &key.material.rsa.n, 0);
if (err) if (err)
break; break;
break; break;
@ -589,7 +601,7 @@ ssh_receive_key_public (gpg_stream_t stream, ssh_key_public_t *key_public)
out: out:
free (key_type); gcry_free (key_type);
if (! err) if (! err)
*key_public = key; *key_public = key;
@ -670,7 +682,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
if (err) if (err)
goto out; goto out;
blob_new = malloc (blob_new_size); blob_new = gcry_malloc (blob_new_size);
if (! blob_new) if (! blob_new)
{ {
err = gpg_err_code_from_errno (errno); err = gpg_err_code_from_errno (errno);
@ -721,7 +733,7 @@ ssh_key_grip (ssh_key_public_t *public, ssh_key_secret_t *secret,
: secret->material.rsa.n, : secret->material.rsa.n,
public public
? public->material.rsa.e ? public->material.rsa.e
: secret->material.rsa.n); : secret->material.rsa.e);
break; break;
case SSH_KEY_TYPE_NONE: case SSH_KEY_TYPE_NONE:
@ -867,7 +879,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
} }
key_directory_n = strlen (key_directory); key_directory_n = strlen (key_directory);
key_path = malloc (key_directory_n + 46); key_path = gcry_malloc (key_directory_n + 46);
if (! key_path) if (! key_path)
{ {
err = gpg_err_code_from_errno (errno); err = gpg_err_code_from_errno (errno);
@ -903,7 +915,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
/* Convert it into a public key. */ /* Convert it into a public key. */
err = ssh_key_public_from_stored_key (buffer, buffer_n, &key); err = ssh_key_public_from_stored_key (buffer, buffer_n, &key);
free (buffer); gcry_free (buffer);
buffer = NULL; buffer = NULL;
if (err) if (err)
goto out; goto out;
@ -915,7 +927,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
/* Add key blob to buffer stream. */ /* Add key blob to buffer stream. */
err = gpg_stream_write_string (key_blobs, key_blob, key_blob_n); err = gpg_stream_write_string (key_blobs, key_blob, key_blob_n);
free (key_blob); gcry_free (key_blob);
key_blob = NULL; key_blob = NULL;
if (err) if (err)
goto out; goto out;
@ -949,8 +961,8 @@ ssh_handler_request_identities (ctrl_t ctrl,
gpg_stream_destroy (key_blobs); gpg_stream_destroy (key_blobs);
closedir (dir); closedir (dir);
free (key_directory); free (key_directory);
free (key_path); gcry_free (key_path);
free (key_blob); gcry_free (key_blob);
} }
static gpg_err_code_t static gpg_err_code_t
@ -984,7 +996,7 @@ data_sign (CTRL ctrl, unsigned char **sig, size_t *sig_n)
unsigned int i = 0; unsigned int i = 0;
for (i = 0; i < 20; i++) for (i = 0; i < 20; i++)
sprintf (&key_grip[i * 2], "%02X", ctrl->keygrip[i]); sprintf (&key_grip[i * 2], "%02X", (unsigned char) ctrl->keygrip[i]);
strncpy (strchr (description, '0'), key_grip, 40); strncpy (strchr (description, '0'), key_grip, 40);
err = agent_pksign_do (ctrl, description, &signature_sexp, 0); err = agent_pksign_do (ctrl, description, &signature_sexp, 0);
@ -1039,7 +1051,7 @@ data_sign (CTRL ctrl, unsigned char **sig, size_t *sig_n)
if (err) if (err)
goto out; goto out;
sig_blob = malloc (sig_blob_n); sig_blob = gcry_malloc (sig_blob_n);
if (! sig_blob) if (! sig_blob)
{ {
err = gpg_err_code_from_errno (errno); err = gpg_err_code_from_errno (errno);
@ -1048,7 +1060,7 @@ data_sign (CTRL ctrl, unsigned char **sig, size_t *sig_n)
err = gpg_stream_read (stream, sig_blob, sig_blob_n, &bytes_read); err = gpg_stream_read (stream, sig_blob, sig_blob_n, &bytes_read);
if ((! err) && (sig_blob_n != bytes_read)) if ((! err) && (sig_blob_n != bytes_read))
err = GPG_ERR_INTERNAL; /* violation */ err = GPG_ERR_EOF;
if (err) if (err)
goto out; goto out;
@ -1067,7 +1079,7 @@ data_sign (CTRL ctrl, unsigned char **sig, size_t *sig_n)
{ {
gcry_sexp_release (signature_sexp); gcry_sexp_release (signature_sexp);
gcry_sexp_release (sublist); gcry_sexp_release (sublist);
free (sig_blob); gcry_free (sig_blob);
} }
return err; return err;
@ -1095,7 +1107,7 @@ ssh_handler_sign_request (ctrl_t ctrl,
/* Receive key. */ /* Receive key. */
err = gpg_stream_read_string (request, &key_blob, &key_blob_size); err = gpg_stream_read_string (request, 0, &key_blob, &key_blob_size);
if (err) if (err)
goto out; goto out;
@ -1105,7 +1117,7 @@ ssh_handler_sign_request (ctrl_t ctrl,
/* Receive data to sign. */ /* Receive data to sign. */
err = gpg_stream_read_string (request, &data, &data_size); err = gpg_stream_read_string (request, 0, &data, &data_size);
if (err) if (err)
goto out; goto out;
@ -1161,9 +1173,9 @@ ssh_handler_sign_request (ctrl_t ctrl,
else else
gpg_stream_write_byte (response, SSH_RESPONSE_FAILURE); gpg_stream_write_byte (response, SSH_RESPONSE_FAILURE);
free (key_blob); gcry_free (key_blob);
free (data); gcry_free (data);
free (sig); gcry_free (sig);
} }
static gpg_err_code_t static gpg_err_code_t
@ -1194,7 +1206,7 @@ ssh_key_to_sexp_buffer (ssh_key_secret_t *key, const char *passphrase,
goto out; goto out;
buffer_new_n = gcry_sexp_sprint (key_sexp, GCRYSEXP_FMT_CANON, NULL, 0); buffer_new_n = gcry_sexp_sprint (key_sexp, GCRYSEXP_FMT_CANON, NULL, 0);
buffer_new = malloc (buffer_new_n); buffer_new = gcry_malloc (buffer_new_n);
if (! buffer_new) if (! buffer_new)
{ {
err = gpg_err_code_from_errno (errno); err = gpg_err_code_from_errno (errno);
@ -1209,10 +1221,8 @@ ssh_key_to_sexp_buffer (ssh_key_secret_t *key, const char *passphrase,
out: out:
if (key_sexp)
gcry_sexp_release (key_sexp); gcry_sexp_release (key_sexp);
if (buffer_new) gcry_free (buffer_new);
free (buffer_new);
return err; return err;
} }
@ -1254,24 +1264,35 @@ static gpg_err_code_t
ssh_identity_register (ssh_key_secret_t *key, int ttl) ssh_identity_register (ssh_key_secret_t *key, int ttl)
{ {
gpg_err_code_t err = GPG_ERR_NO_ERROR; gpg_err_code_t err = GPG_ERR_NO_ERROR;
unsigned char key_grip[21] = { 0 }; unsigned char key_grip_raw[21] = { 0 };
unsigned char *buffer = NULL; unsigned char *buffer = NULL;
unsigned int buffer_n = 0; unsigned int buffer_n = 0;
char passphrase[100] = { 0 }; char passphrase[100] = { 0 };
char description[] =
"Please provide the passphrase, which should "
"be used for protecting the received secret key "
"`0123456789012345678901234567890123456789':";
unsigned int i = 0;
char key_grip[41];
int ret = 0; int ret = 0;
if (DBG_COMMAND) if (DBG_COMMAND)
log_debug ("[ssh-agent] registering identity `%s'\n", key_grip); log_debug ("[ssh-agent] registering identity `%s'\n", key_grip);
err = ssh_key_grip (NULL, key, key_grip); err = ssh_key_grip (NULL, key, key_grip_raw);
if (err) if (err)
goto out; goto out;
ret = agent_key_available (key_grip); ret = agent_key_available (key_grip_raw);
if (! ret) if (! ret)
goto out; goto out;
err = get_passphrase ("foo", sizeof (passphrase), passphrase); for (i = 0; i < 20; i++)
sprintf (&key_grip[i * 2], "%02X", key_grip_raw[i]);
strncpy (strchr (description, '0'), key_grip, 40);
err = get_passphrase (description,
sizeof (passphrase), passphrase);
if (err) if (err)
goto out; goto out;
@ -1279,11 +1300,11 @@ ssh_identity_register (ssh_key_secret_t *key, int ttl)
if (err) if (err)
goto out; goto out;
err = agent_write_private_key (key_grip, buffer, buffer_n, 0); err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0);
if (err) if (err)
goto out; goto out;
err = agent_put_cache (key_grip, passphrase, ttl); err = agent_put_cache (key_grip_raw, passphrase, ttl);
if (err) if (err)
goto out; goto out;
@ -1332,7 +1353,7 @@ ssh_handler_add_identity (ctrl_t ctrl,
if (err) if (err)
goto out; goto out;
err = gpg_stream_read_string (request, &comment, NULL); err = gpg_stream_read_string (request, 0, &comment, NULL);
if (err) if (err)
goto out; goto out;
@ -1383,7 +1404,20 @@ ssh_handler_add_identity (ctrl_t ctrl,
free (comment); free (comment);
//ssh_key_destroy (key); FIXME switch (key.type)
{
case SSH_KEY_TYPE_RSA:
gcry_mpi_release (key.material.rsa.n);
gcry_mpi_release (key.material.rsa.e);
gcry_mpi_release (key.material.rsa.d);
gcry_mpi_release (key.material.rsa.p);
gcry_mpi_release (key.material.rsa.q);
gcry_mpi_release (key.material.rsa.u);
break;
case SSH_KEY_TYPE_NONE:
break;
}
gpg_stream_write_byte (response, gpg_stream_write_byte (response,
err err
@ -1405,7 +1439,7 @@ ssh_handler_remove_identity (ctrl_t ctrl,
if (DBG_COMMAND) if (DBG_COMMAND)
log_debug ("[ssh-agent] remove identity\n"); log_debug ("[ssh-agent] remove identity\n");
err = gpg_stream_read_string (request, &key_blob, NULL); err = gpg_stream_read_string (request, 0, &key_blob, NULL);
if (err) if (err)
goto out; goto out;
@ -1419,7 +1453,7 @@ ssh_handler_remove_identity (ctrl_t ctrl,
out: out:
free (key_blob); gcry_free (key_blob);
err = gpg_stream_write_byte (response, err = gpg_stream_write_byte (response,
err err
@ -1567,9 +1601,25 @@ gpg_stream_eof_p (gpg_stream_t stream, unsigned int *eof)
return err; return err;
} }
/* FIXME: Libgcrypt should provide this function. */
static void *
gcry_realloc_secure (void *mem, size_t size)
{
if (! mem)
return gcry_malloc_secure (size);
else
return gcry_realloc (mem, size);
}
void void
start_command_handler_ssh (int sock_client) start_command_handler_ssh (int sock_client)
{ {
gpg_stream_spec_mem_t stream_spec_secure = { NULL, 0, 1,
gcry_realloc_secure,
gcry_free };
gpg_stream_spec_mem_t stream_spec = { NULL, 0, 1,
gcry_realloc,
gcry_free };
struct server_control_s ctrl = { NULL }; struct server_control_s ctrl = { NULL };
gpg_err_code_t err = GPG_ERR_NO_ERROR; gpg_err_code_t err = GPG_ERR_NO_ERROR;
gpg_stream_t stream_sock = NULL; gpg_stream_t stream_sock = NULL;
@ -1598,24 +1648,28 @@ start_command_handler_ssh (int sock_client)
if (err || eof) if (err || eof)
break; break;
/* Create memory streams for request/response data. */ /* Create memory streams for request/response data. The entire
request will be stored in secure memory, since it might
contain secret key material. The response does not have to
be stored in secure memory, since we never give out secret
keys. */
stream_request = NULL; stream_request = NULL;
err = gpg_stream_create (&stream_request, NULL, err = gpg_stream_create (&stream_request, &stream_spec_secure,
GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE, GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
gpg_stream_functions_mem); gpg_stream_functions_mem);
if (err) if (err)
break; break;
stream_response = NULL; stream_response = NULL;
err = gpg_stream_create (&stream_response, NULL, err = gpg_stream_create (&stream_response, &stream_spec,
GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE, GPG_STREAM_FLAG_READ | GPG_STREAM_FLAG_WRITE,
gpg_stream_functions_mem); gpg_stream_functions_mem);
if (err) if (err)
break; break;
/* Retrieve request length. */ /* Retrieve request length. */
free (request); gcry_free (request);
request = NULL; request = NULL;
err = gpg_stream_read_string (stream_sock, &request, &request_size); err = gpg_stream_read_string (stream_sock, 1, &request, &request_size);
if (err) if (err)
break; break;
@ -1666,7 +1720,7 @@ start_command_handler_ssh (int sock_client)
gpg_stream_destroy (stream_sock); gpg_stream_destroy (stream_sock);
gpg_stream_destroy (stream_request); gpg_stream_destroy (stream_request);
gpg_stream_destroy (stream_response); gpg_stream_destroy (stream_response);
free (request); gcry_free (request);
if (DBG_COMMAND) if (DBG_COMMAND)
log_debug ("[ssh-agent] Leaving ssh command handler: %s\n", gpg_strerror (err)); log_debug ("[ssh-agent] Leaving ssh command handler: %s\n", gpg_strerror (err));

View File

@ -33,10 +33,430 @@
#include "gpg-stream.h" #include "gpg-stream.h"
#include "gpg-stream-config.h" #include "gpg-stream-config.h"
#include "buffer.h"
/* Buffer management layer. */
#define BUFFER_BLOCK_SIZE 1024
typedef struct buffer *buffer_t;
/* Callbacks, necessary for filling/flushing/seeking. */
typedef gpg_error_t (*buffer_func_read_t) (void *handle,
char *buffer,
size_t bytes_to_read,
size_t *bytes_read);
typedef gpg_error_t (*buffer_func_write_t) (void *handle,
const char *buffer,
size_t bytes_to_write,
size_t *bytes_written);
typedef gpg_error_t (*buffer_func_seek_t) (void *handle,
off_t offset,
int whence);
typedef gpg_error_t (*buffer_func_stat_t) (void *handle,
size_t *size);
typedef struct buffer_functions
{
buffer_func_read_t func_read; /* Read callback. */
buffer_func_write_t func_write; /* Write callback. */
buffer_func_seek_t func_seek; /* Seek callback. */
buffer_func_stat_t func_stat; /* Stat callback. */
} buffer_functions_t;
/* Buffer context. */
struct buffer
{
void *handle; /* Handle, passed to callbacks. */
buffer_functions_t functions; /* Callback functions. */
unsigned int flags; /* General flags. */
struct buffer_in
{
char *container; /* Container holding data. */
size_t container_size; /* Size of CONTAINER. */
size_t data_size; /* Size of data in CONTAINER. */
off_t data_offset; /* Offset inside of CONTAINER. */
} buffer_in;
struct buffer_out
{
char *container; /* Container holding data. */
size_t container_size; /* Size of CONTAINER. */
size_t data_size; /* Size of data in CONTAINER. */
off_t data_offset; /* Offset inside of CONTAINER. */
size_t data_flushed; /* Amount of data already flushed. */
} buffer_out;
};
/* Buffer contains unflushed data. */
#define BUFFER_FLAG_DIRTY (1 << 0)
/* Fill buffer. */
static gpg_error_t
buffer_fill_do (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t bytes_read = 0;
if (! buffer->functions.func_read)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else
{
buffer_func_read_t func_read = buffer->functions.func_read;
err = (*func_read) (buffer->handle,
buffer->buffer_in.container,
buffer->buffer_in.container_size,
&bytes_read);
}
buffer->buffer_in.data_offset = 0;
buffer->buffer_in.data_size = bytes_read;
return err;
}
/* Empty buffer input. */
static gpg_error_t
buffer_empty (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
buffer->buffer_in.data_size = 0;
buffer->buffer_in.data_offset = 0;
return err;
}
/* Flush data contained in buffer. */
static gpg_error_t
buffer_flush_do (buffer_t buffer)
{
buffer_func_write_t func_write = buffer->functions.func_write;
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t bytes_written = 0;
if (! func_write)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else if (buffer->flags & BUFFER_FLAG_DIRTY)
while ((buffer->buffer_out.data_size
- buffer->buffer_out.data_flushed) && (! err))
{
err = (*func_write) (buffer->handle,
buffer->buffer_out.container
+ buffer->buffer_out.data_flushed,
buffer->buffer_out.data_size
- buffer->buffer_out.data_flushed,
&bytes_written);
if (! err)
{
buffer->buffer_out.data_size = 0;
buffer->buffer_out.data_offset = 0;
buffer->buffer_out.data_flushed = 0;
buffer->flags &= ~BUFFER_FLAG_DIRTY;
}
else
buffer->buffer_out.data_flushed += bytes_written;
}
return err;
}
static gpg_error_t
buffer_stat_do (buffer_t buffer,
size_t *size)
{
buffer_func_stat_t func_stat = buffer->functions.func_stat;
gpg_error_t err = GPG_ERR_NO_ERROR;
err = (*func_stat) (buffer->handle, size);
return err;
}
/* Read from a buffer. */
gpg_error_t
buffer_read (buffer_t buffer,
char *data,
size_t bytes_to_read,
size_t *bytes_read)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t data_read = 0;
size_t data_to_copy = 0;
if (! (buffer->flags & BUFFER_FLAG_DIRTY))
err = buffer_flush_do (buffer);
while ((bytes_to_read - data_read) && (! err))
{
if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size)
{
/* Nothing more to read in current container, try to
fill container with new data. */
err = buffer_fill_do (buffer);
if (! err)
if (! buffer->buffer_in.data_size)
/* Filling did not result in any data read. */
break;
}
if (! err)
{
if ((bytes_to_read
- data_read) > (buffer->buffer_in.data_size
- buffer->buffer_in.data_offset))
data_to_copy = (buffer->buffer_in.data_size
- buffer->buffer_in.data_offset);
else
data_to_copy = bytes_to_read - data_read;
memcpy (data + data_read,
buffer->buffer_in.container + buffer->buffer_in.data_offset,
data_to_copy);
buffer->buffer_in.data_offset += data_to_copy;
data_read += data_to_copy;
}
}
if (bytes_read)
*bytes_read = data_read;
return err;
}
/* Write to a buffer. */
gpg_error_t
buffer_write (buffer_t buffer,
const char *data,
size_t bytes_to_write,
size_t *bytes_written)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
size_t data_written = 0;
size_t data_to_copy = 0;
while ((bytes_to_write - data_written) && (! err))
{
if (buffer->buffer_out.data_offset == buffer->buffer_out.container_size)
/* Container full, flush buffer. */
err = buffer_flush_do (buffer);
if (! err)
{
if ((bytes_to_write
- data_written) > (buffer->buffer_out.container_size
- buffer->buffer_out.data_offset))
data_to_copy = (buffer->buffer_out.container_size
- buffer->buffer_out.data_offset);
else
data_to_copy = bytes_to_write - data_written;
memcpy (buffer->buffer_out.container
+ buffer->buffer_out.data_offset,
data + data_written,
data_to_copy);
if ((buffer->buffer_out.data_offset
+ data_to_copy) > buffer->buffer_out.data_size)
buffer->buffer_out.data_size = (buffer->buffer_out.data_offset
+ data_to_copy);
buffer->buffer_out.data_offset += data_to_copy;
data_written += data_to_copy;
if (data_written)
if (! (buffer->flags & BUFFER_FLAG_DIRTY))
buffer->flags |= BUFFER_FLAG_DIRTY;
}
}
if (bytes_written)
*bytes_written = data_written;
return err;
}
/* Seek in a buffer. */
gpg_error_t
buffer_seek (buffer_t buffer,
off_t offset,
int whence)
{
buffer_func_seek_t func_seek = buffer->functions.func_seek;
gpg_error_t err = GPG_ERR_NO_ERROR;
if (! func_seek)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else
{
if (buffer->flags & BUFFER_FLAG_DIRTY)
/* Flush data first in order to prevent flushing it to the
wrong offset. */
err = buffer_flush_do (buffer);
if (! err)
err = (*func_seek) (buffer->handle, offset, whence);
if (! err)
err = buffer_empty (buffer);
}
return err;
}
/* Return the unread data contained in a buffer. */
gpg_error_t
buffer_peek (buffer_t buffer,
char **data,
size_t *data_size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size)
/* Refill container. */
err = buffer_fill_do (buffer);
if (! err)
{
if (data)
*data = buffer->buffer_in.container + buffer->buffer_in.data_offset;
if (data_size)
*data_size = buffer->buffer_in.data_size - buffer->buffer_in.data_offset;
}
return err;
}
/* Skip SIZE bytes of input data contained in buffer. */
gpg_error_t
buffer_skip (buffer_t buffer,
size_t size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (buffer->buffer_in.data_offset + size > buffer->buffer_in.data_size)
err = gpg_error (GPG_ERR_INV_ARG);
else
buffer->buffer_in.data_offset += size;
return err;
}
/* Create a new buffer. */
gpg_error_t
buffer_create (buffer_t *buffer,
void *handle,
buffer_functions_t functions)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
/* Allocate memory, initialize. */
buffer_t buffer_new = NULL;
char *container_in_new = NULL;
char *container_out_new = NULL;
buffer_new = malloc (sizeof (*buffer_new));
if (! buffer_new)
err = gpg_error_from_errno (errno);
if (! err)
{
container_in_new = malloc (BUFFER_BLOCK_SIZE);
if (! container_in_new)
err = gpg_error_from_errno (errno);
}
if (! err)
{
container_out_new = malloc (BUFFER_BLOCK_SIZE);
if (! container_out_new)
err = gpg_error_from_errno (errno);
}
if (! err)
{
buffer_new->handle = handle;
buffer_new->flags = 0;
buffer_new->functions = functions;
buffer_new->buffer_in.container = container_in_new;
buffer_new->buffer_in.container_size = BUFFER_BLOCK_SIZE;
buffer_new->buffer_in.data_size = 0;
buffer_new->buffer_in.data_offset = 0;
buffer_new->buffer_out.container = container_out_new;
buffer_new->buffer_out.container_size = BUFFER_BLOCK_SIZE;
buffer_new->buffer_out.data_size = 0;
buffer_new->buffer_out.data_offset = 0;
buffer_new->buffer_out.data_flushed = 0;
*buffer = buffer_new;
}
else
{
if (container_in_new)
free (container_in_new);
if (container_out_new)
free (container_out_new);
if (buffer_new)
free (buffer_new);
}
return err;
}
/* Destroy a buffer. */
gpg_error_t
buffer_destroy (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
if (buffer)
{
err = buffer_flush_do (buffer);
free (buffer->buffer_in.container);
free (buffer->buffer_out.container);
free (buffer);
}
return err;
}
/* Write out unwritten data contained in buffer. */
gpg_error_t
buffer_flush (buffer_t buffer)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_flush_do (buffer);
return err;
}
/* Stat buffer. */
gpg_error_t
buffer_stat (buffer_t buffer,
size_t *size)
{
gpg_error_t err = GPG_ERR_NO_ERROR;
err = buffer_stat_do (buffer, size);
return err;
}
/* Stream layer. */
/* A Stream Context. */ /* A Stream Context. */
struct gpg_stream struct gpg_stream
{ {
@ -66,6 +486,9 @@ struct gpg_stream
/* Implementation of Memory I/O. */ /* Implementation of Memory I/O. */
typedef void *(*mem_func_realloc_t) (void *mem, size_t size);
typedef void (*mem_func_free_t) (void *mem);
typedef struct gpg_stream_handle_mem typedef struct gpg_stream_handle_mem
{ {
char *memory; /* Data. */ char *memory; /* Data. */
@ -73,6 +496,8 @@ typedef struct gpg_stream_handle_mem
size_t data_size; /* Size of data in MEMORY. */ size_t data_size; /* Size of data in MEMORY. */
unsigned int grow: 1; /* MEMORY is allowed to grow. */ unsigned int grow: 1; /* MEMORY is allowed to grow. */
size_t offset; /* Current offset in MEMORY. */ size_t offset; /* Current offset in MEMORY. */
mem_func_realloc_t func_realloc;
mem_func_free_t func_free;
} *gpg_stream_handle_mem_t; } *gpg_stream_handle_mem_t;
static gpg_error_t static gpg_error_t
@ -94,6 +519,10 @@ gpg_stream_func_mem_create (void **handle,
mem_handle->data_size = 0; mem_handle->data_size = 0;
mem_handle->grow = mem_spec ? mem_spec->grow : 1; mem_handle->grow = mem_spec ? mem_spec->grow : 1;
mem_handle->offset = 0; mem_handle->offset = 0;
mem_handle->func_realloc = ((mem_spec && mem_spec->func_realloc)
? mem_spec->func_realloc : realloc);
mem_handle->func_free = ((mem_spec && mem_spec->func_free)
? mem_spec->func_free : free);
*handle = mem_handle; *handle = mem_handle;
} }
@ -136,7 +565,8 @@ gpg_stream_func_mem_write (void *handle,
while (bytes_to_write > mem_handle->memory_size - mem_handle->offset) while (bytes_to_write > mem_handle->memory_size - mem_handle->offset)
{ {
memory_new = realloc (mem_handle->memory, memory_new = (*mem_handle->func_realloc)
(mem_handle->memory,
mem_handle->memory_size + BUFFER_BLOCK_SIZE); mem_handle->memory_size + BUFFER_BLOCK_SIZE);
if (! memory_new) if (! memory_new)
err = gpg_error_from_errno (errno); err = gpg_error_from_errno (errno);
@ -216,7 +646,7 @@ gpg_stream_func_mem_destroy (void *handle)
gpg_error_t err = GPG_ERR_NO_ERROR; gpg_error_t err = GPG_ERR_NO_ERROR;
if (mem_handle->memory) if (mem_handle->memory)
free (mem_handle->memory); (*mem_handle->func_free) (mem_handle->memory);
free (mem_handle); free (mem_handle);
return err; return err;

View File

@ -129,6 +129,8 @@ typedef struct gpg_stream_spec_mem
char *memory; char *memory;
size_t memory_size; size_t memory_size;
unsigned int grow: 1; unsigned int grow: 1;
void *(*func_realloc) (void *mem, size_t size);
void (*func_free) (void *mem);
} gpg_stream_spec_mem_t; } gpg_stream_spec_mem_t;
extern gpg_stream_functions_t gpg_stream_functions_mem; extern gpg_stream_functions_t gpg_stream_functions_mem;