diff --git a/agent/ChangeLog b/agent/ChangeLog index 1fa81c4be..2c503a5fc 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,16 @@ +2004-07-27 Moritz Schulte + + * command-ssh.c: Use gcrypt memory allocators, use secure memory + where necessary. + +2004-07-26 Moritz Schulte + + * 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 * gpg-stream-config.h: New file (was missing before). diff --git a/agent/Makefile.am b/agent/Makefile.am index 406b8f33c..6eab1162c 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -30,7 +30,6 @@ AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS) gpg_agent_SOURCES = \ gpg-agent.c agent.h \ gpg-stream.c gpg-stream.h gpg-stream-config.h \ - buffer.c buffer.h \ command.c command-ssh.c \ query.c \ cache.c \ diff --git a/agent/command-ssh.c b/agent/command-ssh.c index c877c6ec7..8195b9038 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -26,7 +26,7 @@ #include #include #include -//#include +#include #include "agent.h" @@ -227,7 +227,7 @@ gpg_stream_write_uint32 (gpg_stream_t stream, uint32_t uint32) } 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) { gpg_err_code_t err = GPG_ERR_NO_ERROR; @@ -241,7 +241,10 @@ gpg_stream_read_string (gpg_stream_t stream, goto out; /* Allocate space. */ - buffer = malloc (length + 1); + if (secure) + buffer = gcry_malloc_secure (length + 1); + else + buffer = gcry_malloc (length + 1); if (! buffer) { err = gpg_err_code_from_errno (errno); @@ -268,7 +271,7 @@ gpg_stream_read_string (gpg_stream_t stream, } else if (buffer) - free (buffer); + gcry_free (buffer); return err; } @@ -303,8 +306,8 @@ gpg_stream_write_cstring (gpg_stream_t stream, char *string) } static gpg_err_code_t -gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint, - unsigned int mpi_type) +gpg_stream_read_mpint (gpg_stream_t stream, unsigned int secure, + mpint_t *mpint, unsigned int mpi_type) { gpg_err_code_t err = GPG_ERR_NO_ERROR; unsigned char *mpi_data = NULL; @@ -314,7 +317,8 @@ gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint, if (! mpi_type) 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) goto out; @@ -324,7 +328,7 @@ gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint, out: - free (mpi_data); + gcry_free (mpi_data); if (! err) *mpint = mpi; @@ -333,8 +337,8 @@ gpg_stream_read_mpint (gpg_stream_t stream, mpint_t *mpint, } static gpg_err_code_t -gpg_stream_write_mpint (gpg_stream_t stream, mpint_t mpint, - unsigned int mpi_type) +gpg_stream_write_mpint (gpg_stream_t stream, + mpint_t mpint, unsigned int mpi_type) { gpg_err_code_t err = GPG_ERR_NO_ERROR; unsigned char *mpi_buffer = NULL; @@ -376,7 +380,7 @@ gpg_stream_read_file (const char *filename, if (err) goto out; - buffer_new = malloc (buffer_new_n); + buffer_new = gcry_malloc (buffer_new_n); if (! buffer_new) { 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; 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) 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: { - 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) 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) 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) break; - err = gpg_stream_read_mpint (stream, &mpi_iqmp, 0); + err = gpg_stream_read_mpint (stream, 1, &mpi_iqmp, 0); if (err) 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) 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) break; @@ -483,7 +487,7 @@ ssh_receive_key_secret (gpg_stream_t stream, ssh_key_secret_t *key_secret) { /* 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, 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: - free (key_type); + gcry_free (key_type); gcry_mpi_release (mpi_iqmp); if (! err) *key_secret = key; else { - 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); + 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; + } } 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 }; 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) 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: { - 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) 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) break; break; @@ -589,7 +601,7 @@ ssh_receive_key_public (gpg_stream_t stream, ssh_key_public_t *key_public) out: - free (key_type); + gcry_free (key_type); if (! err) *key_public = key; @@ -670,7 +682,7 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size, if (err) goto out; - blob_new = malloc (blob_new_size); + blob_new = gcry_malloc (blob_new_size); if (! blob_new) { 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, public ? public->material.rsa.e - : secret->material.rsa.n); + : secret->material.rsa.e); break; case SSH_KEY_TYPE_NONE: @@ -867,7 +879,7 @@ ssh_handler_request_identities (ctrl_t ctrl, } key_directory_n = strlen (key_directory); - key_path = malloc (key_directory_n + 46); + key_path = gcry_malloc (key_directory_n + 46); if (! key_path) { err = gpg_err_code_from_errno (errno); @@ -903,7 +915,7 @@ ssh_handler_request_identities (ctrl_t ctrl, /* Convert it into a public key. */ err = ssh_key_public_from_stored_key (buffer, buffer_n, &key); - free (buffer); + gcry_free (buffer); buffer = NULL; if (err) goto out; @@ -915,7 +927,7 @@ ssh_handler_request_identities (ctrl_t ctrl, /* Add key blob to buffer stream. */ err = gpg_stream_write_string (key_blobs, key_blob, key_blob_n); - free (key_blob); + gcry_free (key_blob); key_blob = NULL; if (err) goto out; @@ -949,8 +961,8 @@ ssh_handler_request_identities (ctrl_t ctrl, gpg_stream_destroy (key_blobs); closedir (dir); free (key_directory); - free (key_path); - free (key_blob); + gcry_free (key_path); + gcry_free (key_blob); } static gpg_err_code_t @@ -984,7 +996,7 @@ data_sign (CTRL ctrl, unsigned char **sig, size_t *sig_n) unsigned int i = 0; 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); 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) goto out; - sig_blob = malloc (sig_blob_n); + sig_blob = gcry_malloc (sig_blob_n); if (! sig_blob) { 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); if ((! err) && (sig_blob_n != bytes_read)) - err = GPG_ERR_INTERNAL; /* violation */ + err = GPG_ERR_EOF; if (err) 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 (sublist); - free (sig_blob); + gcry_free (sig_blob); } return err; @@ -1095,7 +1107,7 @@ ssh_handler_sign_request (ctrl_t ctrl, /* 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) goto out; @@ -1105,7 +1117,7 @@ ssh_handler_sign_request (ctrl_t ctrl, /* 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) goto out; @@ -1161,9 +1173,9 @@ ssh_handler_sign_request (ctrl_t ctrl, else gpg_stream_write_byte (response, SSH_RESPONSE_FAILURE); - free (key_blob); - free (data); - free (sig); + gcry_free (key_blob); + gcry_free (data); + gcry_free (sig); } static gpg_err_code_t @@ -1194,7 +1206,7 @@ ssh_key_to_sexp_buffer (ssh_key_secret_t *key, const char *passphrase, goto out; 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) { 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: - if (key_sexp) - gcry_sexp_release (key_sexp); - if (buffer_new) - free (buffer_new); + gcry_sexp_release (key_sexp); + gcry_free (buffer_new); return err; } @@ -1254,24 +1264,35 @@ static gpg_err_code_t ssh_identity_register (ssh_key_secret_t *key, int ttl) { 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 int buffer_n = 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; if (DBG_COMMAND) 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) goto out; - ret = agent_key_available (key_grip); + ret = agent_key_available (key_grip_raw); if (! ret) 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) goto out; @@ -1279,11 +1300,11 @@ ssh_identity_register (ssh_key_secret_t *key, int ttl) if (err) 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) goto out; - err = agent_put_cache (key_grip, passphrase, ttl); + err = agent_put_cache (key_grip_raw, passphrase, ttl); if (err) goto out; @@ -1332,7 +1353,7 @@ ssh_handler_add_identity (ctrl_t ctrl, if (err) goto out; - err = gpg_stream_read_string (request, &comment, NULL); + err = gpg_stream_read_string (request, 0, &comment, NULL); if (err) goto out; @@ -1382,8 +1403,21 @@ ssh_handler_add_identity (ctrl_t ctrl, out: 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, err @@ -1405,7 +1439,7 @@ ssh_handler_remove_identity (ctrl_t ctrl, if (DBG_COMMAND) 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) goto out; @@ -1419,7 +1453,7 @@ ssh_handler_remove_identity (ctrl_t ctrl, out: - free (key_blob); + gcry_free (key_blob); err = gpg_stream_write_byte (response, err @@ -1567,9 +1601,25 @@ gpg_stream_eof_p (gpg_stream_t stream, unsigned int *eof) 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 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 }; gpg_err_code_t err = GPG_ERR_NO_ERROR; gpg_stream_t stream_sock = NULL; @@ -1598,24 +1648,28 @@ start_command_handler_ssh (int sock_client) if (err || eof) 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; - 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_functions_mem); if (err) break; 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_functions_mem); if (err) break; /* Retrieve request length. */ - free (request); + gcry_free (request); 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) break; @@ -1666,7 +1720,7 @@ start_command_handler_ssh (int sock_client) gpg_stream_destroy (stream_sock); gpg_stream_destroy (stream_request); gpg_stream_destroy (stream_response); - free (request); + gcry_free (request); if (DBG_COMMAND) log_debug ("[ssh-agent] Leaving ssh command handler: %s\n", gpg_strerror (err)); diff --git a/agent/gpg-stream.c b/agent/gpg-stream.c index 1139cbe3d..1fb3c4033 100644 --- a/agent/gpg-stream.c +++ b/agent/gpg-stream.c @@ -33,10 +33,430 @@ #include "gpg-stream.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. */ struct gpg_stream { @@ -66,6 +486,9 @@ struct gpg_stream /* 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 { char *memory; /* Data. */ @@ -73,6 +496,8 @@ typedef struct gpg_stream_handle_mem size_t data_size; /* Size of data in MEMORY. */ unsigned int grow: 1; /* MEMORY is allowed to grow. */ size_t offset; /* Current offset in MEMORY. */ + mem_func_realloc_t func_realloc; + mem_func_free_t func_free; } *gpg_stream_handle_mem_t; static gpg_error_t @@ -94,6 +519,10 @@ gpg_stream_func_mem_create (void **handle, mem_handle->data_size = 0; mem_handle->grow = mem_spec ? mem_spec->grow : 1; 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; } @@ -136,8 +565,9 @@ gpg_stream_func_mem_write (void *handle, while (bytes_to_write > mem_handle->memory_size - mem_handle->offset) { - memory_new = realloc (mem_handle->memory, - mem_handle->memory_size + BUFFER_BLOCK_SIZE); + memory_new = (*mem_handle->func_realloc) + (mem_handle->memory, + mem_handle->memory_size + BUFFER_BLOCK_SIZE); if (! memory_new) err = gpg_error_from_errno (errno); else @@ -216,7 +646,7 @@ gpg_stream_func_mem_destroy (void *handle) gpg_error_t err = GPG_ERR_NO_ERROR; if (mem_handle->memory) - free (mem_handle->memory); + (*mem_handle->func_free) (mem_handle->memory); free (mem_handle); return err; diff --git a/agent/gpg-stream.h b/agent/gpg-stream.h index c7db5f0b6..464d9ea9b 100644 --- a/agent/gpg-stream.h +++ b/agent/gpg-stream.h @@ -129,6 +129,8 @@ typedef struct gpg_stream_spec_mem char *memory; size_t memory_size; unsigned int grow: 1; + void *(*func_realloc) (void *mem, size_t size); + void (*func_free) (void *mem); } gpg_stream_spec_mem_t; extern gpg_stream_functions_t gpg_stream_functions_mem;