diff options
Diffstat (limited to 'g13')
-rw-r--r-- | g13/Makefile.am | 19 | ||||
-rw-r--r-- | g13/backend.c | 83 | ||||
-rw-r--r-- | g13/backend.h | 32 | ||||
-rw-r--r-- | g13/be-encfs.c | 58 | ||||
-rw-r--r-- | g13/be-encfs.h | 31 | ||||
-rw-r--r-- | g13/be-truecrypt.c | 39 | ||||
-rw-r--r-- | g13/be-truecrypt.h | 29 | ||||
-rw-r--r-- | g13/call-gpg.c | 466 | ||||
-rw-r--r-- | g13/call-gpg.h | 29 | ||||
-rw-r--r-- | g13/create.c | 306 | ||||
-rw-r--r-- | g13/g13.c | 103 | ||||
-rw-r--r-- | g13/g13.h | 17 | ||||
-rw-r--r-- | g13/keyblob.h | 126 | ||||
-rw-r--r-- | g13/utils.c | 51 | ||||
-rw-r--r-- | g13/utils.h | 32 |
15 files changed, 1385 insertions, 36 deletions
diff --git a/g13/Makefile.am b/g13/Makefile.am index dce2f0b04..44f546eb7 100644 --- a/g13/Makefile.am +++ b/g13/Makefile.am @@ -24,12 +24,19 @@ AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am -AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_PTH_CFLAGS) $(PTH_CFLAGS) g13_SOURCES = \ - g13.c g13.h - -g13_LDADD = $(libcommon) ../jnlib/libjnlib.a ../gl/libgnu.a \ - $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBINTL) + g13.c g13.h \ + keyblob.h \ + utils.c utils.h \ + create.c create.h \ + call-gpg.c call-gpg.h \ + backend.c backend.h \ + be-encfs.c be-encfs.h \ + be-truecrypt.c be-truecrypt.h + +g13_LDADD = $(libcommonpth) ../jnlib/libjnlib.a ../gl/libgnu.a \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_PTH_LIBS) $(PTH_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) diff --git a/g13/backend.c b/g13/backend.c new file mode 100644 index 000000000..a6f38719a --- /dev/null +++ b/g13/backend.c @@ -0,0 +1,83 @@ +/* backend.c - Dispatcher to the various backends. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "g13.h" +#include "i18n.h" +#include "keyblob.h" +#include "backend.h" +#include "be-encfs.h" +#include "be-truecrypt.h" + + +static gpg_error_t +no_such_backend (int conttype) +{ + log_error ("invalid backend %d given - this is most likely a bug\n", + conttype); + return gpg_error (GPG_ERR_INTERNAL); +} + + +/* If the backend requires a separate file or directory for the + container, return its name by computing it from FNAME which gives + the g13 filename. The new file name is allocated and stored at + R_NAME, if this is expected to be a directory true is stored at + R_ISDIR. If no detached name is expected or an error occurs NULL + is stored at R_NAME. The function returns 0 on success or an error + code. */ +gpg_error_t +be_get_detached_name (int conttype, const char *fname, + char **r_name, int *r_isdir) +{ + *r_name = NULL; + *r_isdir = 0; + switch (conttype) + { + case CONTTYPE_ENCFS: + return be_encfs_get_detached_name (fname, r_name, r_isdir); + + default: + return no_such_backend (conttype); + } +} + + +gpg_error_t +be_create_new_keys (int conttype, membuf_t *mb) +{ + switch (conttype) + { + case CONTTYPE_ENCFS: + return be_encfs_create_new_keys (mb); + + case CONTTYPE_TRUECRYPT: + return be_truecrypt_create_new_keys (mb); + + default: + return no_such_backend (conttype); + } +} + diff --git a/g13/backend.h b/g13/backend.h new file mode 100644 index 000000000..ffd03d3f5 --- /dev/null +++ b/g13/backend.h @@ -0,0 +1,32 @@ +/* backend.h - Defs for the dispatcher to the various backends. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 G13_BACKEND_H +#define G13_BACKEND_H + +#include "../common/membuf.h" + + +gpg_error_t be_get_detached_name (int conttype, const char *fname, + char **r_name, int *r_isdir); +gpg_error_t be_create_new_keys (int conttype, membuf_t *mb); + + +#endif /*G13_BACKEND_H*/ + diff --git a/g13/be-encfs.c b/g13/be-encfs.c new file mode 100644 index 000000000..18030b80e --- /dev/null +++ b/g13/be-encfs.c @@ -0,0 +1,58 @@ +/* be-encfs.c - The EncFS based backend + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "g13.h" +#include "i18n.h" +#include "keyblob.h" +#include "be-encfs.h" + +/* See be_get_detached_name for a description. Note that the + dispatcher code makes sure that NULL is stored at R_NAME before + calling us. */ +gpg_error_t +be_encfs_get_detached_name (const char *fname, char **r_name, int *r_isdir) +{ + char *result; + + if (!fname || !*fname) + return gpg_error (GPG_ERR_INV_ARG); + + result = strconcat (fname, ".d", NULL); + if (!result) + return gpg_error_from_syserror (); + *r_name = result; + *r_isdir = 1; + return 0; +} + + +gpg_error_t +be_encfs_create_new_keys (membuf_t *mb) +{ + return 0; +} + + diff --git a/g13/be-encfs.h b/g13/be-encfs.h new file mode 100644 index 000000000..061385345 --- /dev/null +++ b/g13/be-encfs.h @@ -0,0 +1,31 @@ +/* be-encfs.h - Public defs for the EncFS based backend + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 G13_BE_ENCFS_H +#define G13_BE_ENCFS_H + +#include "backend.h" + +gpg_error_t be_encfs_get_detached_name (const char *fname, + char **r_name, int *r_isdir); +gpg_error_t be_encfs_create_new_keys (membuf_t *mb); + + +#endif /*G13_BE_ENCFS_H*/ + diff --git a/g13/be-truecrypt.c b/g13/be-truecrypt.c new file mode 100644 index 000000000..6f51321f4 --- /dev/null +++ b/g13/be-truecrypt.c @@ -0,0 +1,39 @@ +/* be-truecrypt.c - The Truecrypt based backend + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "g13.h" +#include "i18n.h" +#include "be-truecrypt.h" + + +gpg_error_t +be_truecrypt_create_new_keys (membuf_t *mb) +{ + (void)mb; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + diff --git a/g13/be-truecrypt.h b/g13/be-truecrypt.h new file mode 100644 index 000000000..ef2c5675b --- /dev/null +++ b/g13/be-truecrypt.h @@ -0,0 +1,29 @@ +/* be-truecrypt.h - Public defs for the Truecrypt based backend + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 G13_BE_TRUECRYPT_H +#define G13_BE_TRUECRYPT_H + +#include "backend.h" + +gpg_error_t be_truecrypt_create_new_keys (membuf_t *mb); + + +#endif /*G13_BE_TRUECRYPT_H*/ + diff --git a/g13/call-gpg.c b/g13/call-gpg.c new file mode 100644 index 000000000..2399058b0 --- /dev/null +++ b/g13/call-gpg.c @@ -0,0 +1,466 @@ +/* call-gpg.c - Communication with the GPG + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <assert.h> +#include <pth.h> + +#include "g13.h" +#include <assuan.h> +#include "i18n.h" +#include "call-gpg.h" +#include "utils.h" +#include "../common/exechelp.h" + + + +/* Fire up a new GPG. Handle the server's initial greeting. Returns + 0 on success and stores the assuan context at R_CTX. */ +static gpg_error_t +start_gpg (ctrl_t ctrl, int input_fd, int output_fd, assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + const char *pgmname; + const char *argv[6]; + int no_close_list[5]; + int i; + char line[ASSUAN_LINELENGTH]; + + (void)ctrl; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (err)); + return err; + } + + /* The first time we are used, intialize the gpg_program variable. */ + if ( !opt.gpg_program || !*opt.gpg_program ) + opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG); + + if (opt.verbose) + log_info (_("no running gpg - starting `%s'\n"), opt.gpg_program); + + /* Compute argv[0]. */ + if ( !(pgmname = strrchr (opt.gpg_program, '/'))) + pgmname = opt.gpg_program; + else + pgmname++; + + if (fflush (NULL)) + { + err = gpg_error_from_syserror (); + log_error ("error flushing pending output: %s\n", gpg_strerror (err)); + return err; + } + + i = 0; + argv[i++] = pgmname; + argv[i++] = "--server"; + if ((opt.debug & 1024)) + argv[i++] = "--debug=1024"; + argv[i++] = "-z"; + argv[i++] = "0"; + argv[i++] = NULL; + + i = 0; + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + if (input_fd != -1) + no_close_list[i++] = input_fd; + if (output_fd != -1) + no_close_list[i++] = output_fd; + no_close_list[i] = -1; + + /* Connect to GPG and perform initial handshaking. */ + err = assuan_pipe_connect (ctx, opt.gpg_program, argv, no_close_list); + + /* if (!err) */ + /* err = assuan_transact (ctx, "OPTION audit-events=1", */ + /* NULL, NULL, NULL, NULL, NULL, NULL); */ + /* audit_log_ok (ctrl->audit, AUDIT_GPG_READY, err); */ + + if (err) + { + assuan_release (ctx); + log_error ("can't connect to GPG: %s\n", gpg_strerror (err)); + return gpg_error (GPG_ERR_NO_ENGINE); + } + + if (input_fd != -1) + { + snprintf (line, sizeof line, "INPUT FD=%d", input_fd); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + assuan_release (ctx); + log_error ("error sending INPUT command: %s\n", gpg_strerror (err)); + return err; + } + } + + if (output_fd != -1) + { + snprintf (line, sizeof line, "OUTPUT FD=%d", output_fd); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + assuan_release (ctx); + log_error ("error sending OUTPUT command: %s\n", gpg_strerror (err)); + return err; + } + } + + *r_ctx = ctx; + + if (DBG_ASSUAN) + log_debug ("connection to GPG established\n"); + return 0; +} + + +/* Release the assuan context created by start_gpg. */ +static void +release_gpg (assuan_context_t ctx) +{ + assuan_release (ctx); +} + + + +/* The data passed to the writer_thread. */ +struct writer_thread_parms +{ + int fd; + const void *data; + size_t datalen; + gpg_error_t *err_addr; +}; + + +/* The thread started by start_writer. */ +static void * +writer_thread (void *arg) +{ + struct writer_thread_parms *parm = arg; + const char *buffer = parm->data; + size_t length = parm->datalen; + + while (length) + { + ssize_t nwritten; + + nwritten = pth_write (parm->fd, buffer, length < 4096? length:4096); + if (nwritten < 0) + { + if (errno == EINTR) + continue; + *parm->err_addr = gpg_error_from_syserror (); + break; /* Write error. */ + } + length -= nwritten; + buffer += nwritten; + } + + if (close (parm->fd)) + log_error ("closing writer fd %d failed: %s\n", parm->fd, strerror (errno)); + xfree (parm); + return NULL; +} + + +/* Fire up a thread to send (DATA,DATALEN) to the file descriptor FD. + On success the thread receives the ownership over FD. The thread + ID is stored at R_TID. WRITER_ERR is the address of an gpg_error_t + variable to receive a possible write error after the thread has + finished. */ +static gpg_error_t +start_writer (int fd, const void *data, size_t datalen, + pth_t *r_tid, gpg_error_t *err_addr) +{ + gpg_error_t err; + struct writer_thread_parms *parm; + pth_attr_t tattr; + pth_t tid; + + *r_tid = NULL; + *err_addr = 0; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return gpg_error_from_syserror (); + parm->fd = fd; + parm->data = data; + parm->datalen = datalen; + parm->err_addr = err_addr; + + tattr = pth_attr_new (); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "fd-writer"); + + tid = pth_spawn (tattr, writer_thread, parm); + if (!tid) + { + err = gpg_error_from_syserror (); + log_error ("error spawning writer thread: %s\n", gpg_strerror (err)); + } + else + { + err = 0; + *r_tid = tid; + } + pth_attr_destroy (tattr); + + return err; +} + + + +/* The data passed to the reader_thread. */ +struct reader_thread_parms +{ + int fd; + membuf_t *mb; + gpg_error_t *err_addr; +}; + + +/* The thread started by start_reader. */ +static void * +reader_thread (void *arg) +{ + struct reader_thread_parms *parm = arg; + char buffer[4096]; + int nread; + + while ( (nread = pth_read (parm->fd, buffer, sizeof buffer)) ) + { + if (nread < 0) + { + if (errno == EINTR) + continue; + *parm->err_addr = gpg_error_from_syserror (); + break; /* Read error. */ + } + + put_membuf (parm->mb, buffer, nread); + } + + if (close (parm->fd)) + log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno)); + xfree (parm); + return NULL; +} + + +/* Fire up a thread to receive data from the file descriptor FD. On + success the thread receives the ownership over FD. The thread ID + is stored at R_TID. After the thread has finished an error from + the thread will be stored at ERR_ADDR. */ +static gpg_error_t +start_reader (int fd, membuf_t *mb, pth_t *r_tid, gpg_error_t *err_addr) +{ + gpg_error_t err; + struct reader_thread_parms *parm; + pth_attr_t tattr; + pth_t tid; + + *r_tid = NULL; + *err_addr = 0; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return gpg_error_from_syserror (); + parm->fd = fd; + parm->mb = mb; + parm->err_addr = err_addr; + + tattr = pth_attr_new (); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "fd-reader"); + + tid = pth_spawn (tattr, reader_thread, parm); + if (!tid) + { + err = gpg_error_from_syserror (); + log_error ("error spawning reader thread: %s\n", gpg_strerror (err)); + } + else + { + err = 0; + *r_tid = tid; + } + pth_attr_destroy (tattr); + + return err; +} + + + + +/* Call GPG to encrypt a block of data. + + + */ +gpg_error_t +gpg_encrypt_blob (ctrl_t ctrl, const void *plain, size_t plainlen, + void **r_ciph, size_t *r_ciphlen) +{ + gpg_error_t err; + assuan_context_t ctx; + int outbound_fds[2] = { -1, -1 }; + int inbound_fds[2] = { -1, -1 }; + pth_t writer_tid = NULL; + pth_t reader_tid = NULL; + gpg_error_t writer_err, reader_err; + membuf_t reader_mb; + + *r_ciph = NULL; + *r_ciphlen = 0; + + /* Init the memory buffer to receive the encrypted stuff. */ + init_membuf (&reader_mb, 4096); + + /* Create two pipes. */ + err = gnupg_create_outbound_pipe (outbound_fds); + if (!err) + err = gnupg_create_inbound_pipe (inbound_fds); + if (err) + { + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Start GPG and send the INPUT and OUTPUT commands. */ + err = start_gpg (ctrl, outbound_fds[0], inbound_fds[1], &ctx); + if (err) + goto leave; + close (outbound_fds[0]); outbound_fds[0] = -1; + close (inbound_fds[1]); inbound_fds[1] = -1; + + /* Start a writer thread to feed the INPUT command of the server. */ + err = start_writer (outbound_fds[1], plain, plainlen, + &writer_tid, &writer_err); + if (err) + return err; + outbound_fds[1] = -1; /* The thread owns the FD now. */ + + /* Start a reader thread to eat from the OUTPUT command of the + server. */ + err = start_reader (inbound_fds[0], &reader_mb, + &reader_tid, &reader_err); + if (err) + return err; + outbound_fds[0] = -1; /* The thread owns the FD now. */ + + /* Run the encryption. */ + err = assuan_transact (ctx, "RECIPIENT [email protected]", + NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's RECIPIENT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + + err = assuan_transact (ctx, "ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's ENCRYPT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + + /* Wait for reader and return the data. */ + if (!pth_join (reader_tid, NULL)) + { + err = gpg_error_from_syserror (); + log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + reader_tid = NULL; + if (reader_err) + { + err = reader_err; + log_error ("read error in reader thread: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Wait for the writer to catch a writer error. */ + if (!pth_join (writer_tid, NULL)) + { + err = gpg_error_from_syserror (); + log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + writer_tid = NULL; + if (writer_err) + { + err = writer_err; + log_error ("write error in writer thread: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Return the data. */ + *r_ciph = get_membuf (&reader_mb, r_ciphlen); + if (!*r_ciph) + { + err = gpg_error_from_syserror (); + log_error ("error while storing the data in the reader thread: %s\n", + gpg_strerror (err)); + goto leave; + } + + leave: + if (reader_tid) + { + pth_cancel (reader_tid); + pth_join (reader_tid, NULL); + } + if (writer_tid) + { + pth_cancel (writer_tid); + pth_join (writer_tid, NULL); + } + if (outbound_fds[0] != -1) + close (outbound_fds[0]); + if (outbound_fds[1] != -1) + close (outbound_fds[1]); + if (inbound_fds[0] != -1) + close (inbound_fds[0]); + if (inbound_fds[1] != -1) + close (inbound_fds[1]); + release_gpg (ctx); + xfree (get_membuf (&reader_mb, NULL)); + return err; +} + + diff --git a/g13/call-gpg.h b/g13/call-gpg.h new file mode 100644 index 000000000..3e801be3b --- /dev/null +++ b/g13/call-gpg.h @@ -0,0 +1,29 @@ +/* call-gpg.h - Defs for the communication with GPG + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 G13_CALL_GPG_H +#define G13_CALL_GPG_H + +gpg_error_t gpg_encrypt_blob (ctrl_t ctrl, + const void *plain, size_t plainlen, + void **r_ciph, size_t *r_ciphlen); + + + +#endif /*G13_CALL_GPG_H*/ diff --git a/g13/create.c b/g13/create.c new file mode 100644 index 000000000..0c6735b80 --- /dev/null +++ b/g13/create.c @@ -0,0 +1,306 @@ +/* create.c - Create a new crypto container + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <assert.h> + +#include "g13.h" +#include "i18n.h" +#include "create.h" + +#include "keyblob.h" +#include "backend.h" +#include "utils.h" +#include "call-gpg.h" +#include "estream.h" + +/* Create a new blob with all the session keys and other meta + information which are to be stored encrypted in the crypto + container header. On success the malloced blob is stored at R_BLOB + and its length at R_BLOBLEN. On error en error ocde is returned + and (R_BLOB,R_BLOBLEN) are set to (NULL,0). + + The format of this blob is a sequence of tag-length-value tuples. + All tuples have this format: + + 2 byte TAG Big endian unsigned integer (0..65535) + described by the KEYBLOB_TAG_ constants. + 2 byte LENGTH Big endian unsigned integer (0..65535) + giving the length of the value. + length bytes VALUE The value described by the tag. + + The first tag in a keyblob must be a BLOBVERSION. The other tags + depend on the type of the container as described by the CONTTYPE + tag. See keyblob.h for details. */ +static gpg_error_t +create_new_keyblob (ctrl_t ctrl, int is_detached, + void **r_blob, size_t *r_bloblen) +{ + gpg_error_t err; + unsigned char twobyte[2]; + membuf_t mb; + + *r_blob = NULL; + *r_bloblen = 0; + + init_membuf_secure (&mb, 512); + + append_tuple (&mb, KEYBLOB_TAG_BLOBVERSION, "\x01", 1); + + twobyte[0] = (ctrl->conttype >> 8); + twobyte[1] = (ctrl->conttype); + append_tuple (&mb, KEYBLOB_TAG_CONTTYPE, twobyte, 2); + if (is_detached) + append_tuple (&mb, KEYBLOB_TAG_DETACHED, NULL, 0); + + err = be_create_new_keys (ctrl->conttype, &mb); + if (err) + goto leave; + + append_tuple (&mb, KEYBLOB_TAG_FILLER, "filler", 6); + + + *r_blob = get_membuf (&mb, r_bloblen); + if (!*r_blob) + { + err = gpg_error_from_syserror (); + *r_bloblen = 0; + } + else + log_debug ("used keyblob size is %zu\n", *r_bloblen); + + leave: + xfree (get_membuf (&mb, NULL)); + return err; +} + + + +/* Encrypt the keyblob (KEYBLOB,KEYBLOBLEN) and store the result at + (R_ENCBLOB, R_ENCBLOBLEN). Returns 0 on success or an error code. + On error R_EKYBLOB is set to NULL. Depending on the keys set in + CTRL the result is a single OpenPGP binary message, a single + special OpenPGP packet encapsulating a CMS message or a + concatenation of both with the CMS packet being the last. */ +static gpg_error_t +encrypt_keyblob (ctrl_t ctrl, void *keyblob, size_t keybloblen, + void **r_encblob, size_t *r_encbloblen) +{ + gpg_error_t err; + + /* FIXME: For now we only implement OpenPGP. */ + err = gpg_encrypt_blob (ctrl, keyblob, keybloblen, + r_encblob, r_encbloblen); + + return err; +} + + +/* Write a new file under the name FILENAME with the keyblob and an + appropriate header. This fucntion is called with a lock file in + place and after checking that the filename does not exists. */ +static gpg_error_t +write_keyblob (ctrl_t ctrl, const char *filename, + const void *keyblob, size_t keybloblen) +{ + gpg_error_t err; + estream_t fp; + unsigned char packet[32]; + size_t headerlen, paddinglen; + + fp = es_fopen (filename, "wbx"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("error creating new container `%s': %s\n", + filename, gpg_strerror (err)); + return err; + } + + /* Allow for an least 8 times larger keyblob to accommodate for + future key changes. Round it up to 4096 byte. */ + headerlen = ((32 + 8 * keybloblen + 16) + 4095) / 4096 * 4096; + paddinglen = headerlen - 32 - keybloblen; + assert (paddinglen >= 16); + + packet[0] = (0xc0|61); /* CTB for the private packet type 0x61. */ + packet[1] = 0xff; /* 5 byte length packet, value 20. */ + packet[2] = 0; + packet[3] = 0; + packet[4] = 0; + packet[5] = 26; + memcpy (packet+6, "GnuPG/G13", 10); /* Packet subtype. */ + packet[16] = 1; /* G13 packet format. */ + packet[17] = 0; /* Reserved. */ + packet[18] = 0; /* Reserved. */ + packet[19] = 0; /* OS Flag. */ + packet[20] = (headerlen >> 24); /* Total length of header. */ + packet[21] = (headerlen >> 16); + packet[22] = (headerlen >> 8); + packet[23] = (headerlen); + packet[24] = 1; /* Number of header copies. */ + packet[25] = 0; /* Number of header copies at the end. */ + packet[26] = 0; /* Reserved. */ + packet[27] = 0; /* Reserved. */ + packet[28] = 0; /* Reserved. */ + packet[29] = 0; /* Reserved. */ + packet[30] = 0; /* Reserved. */ + packet[31] = 0; /* Reserved. */ + + if (es_fwrite (packet, 32, 1, fp) != 1) + goto writeerr; + + if (es_fwrite (keyblob, keybloblen, 1, fp) != 1) + goto writeerr; + + /* Write the padding. */ + packet[0] = (0xc0|61); /* CTB for Private packet type 0x61. */ + packet[1] = 0xff; /* 5 byte length packet, value 20. */ + packet[2] = (paddinglen-6) >> 24; + packet[3] = (paddinglen-6) >> 16; + packet[4] = (paddinglen-6) >> 8; + packet[5] = (paddinglen-6); + memcpy (packet+6, "GnuPG/PAD", 10); /* Packet subtype. */ + if (es_fwrite (packet, 16, 1, fp) != 1) + goto writeerr; + memset (packet, 0, 32); + for (paddinglen-=16; paddinglen >= 32; paddinglen -= 32) + if (es_fwrite (packet, 32, 1, fp) != 1) + goto writeerr; + if (paddinglen) + if (es_fwrite (packet, paddinglen, 1, fp) != 1) + goto writeerr; + + if (es_fclose (fp)) + { + err = gpg_error_from_syserror (); + log_error ("error closing `%s': %s\n", + filename, gpg_strerror (err)); + remove (filename); + return err; + } + + return err; + + + writeerr: + err = gpg_error_from_syserror (); + log_error ("error writing header to `%s': %s\n", + filename, gpg_strerror (err)); + es_fclose (fp); + remove (filename); + return err; +} + + + +/* Create a new container under the name FILENAME and intialize it + using the current settings. If the file already exists an error is + returned. */ +gpg_error_t +create_new_container (ctrl_t ctrl, const char *filename) +{ + gpg_error_t err; + dotlock_t lock; + void *keyblob = NULL; + size_t keybloblen; + void *enckeyblob = NULL; + size_t enckeybloblen; + char *detachedname = NULL; + int detachedisdir; + + /* A quick check to see that no container with that name already + exists. */ + if (!access (filename, F_OK)) + return gpg_error (GPG_ERR_EEXIST); + + /* Take a lock and proceed with the creation. If there is a lock we + immediately return an error because for creation it does not make + sense to wait. */ + lock = create_dotlock (filename); + if (!lock) + return gpg_error_from_syserror (); + if (make_dotlock (lock, 0)) + { + err = gpg_error_from_syserror (); + goto leave; + } + else + err = 0; + + /* Check again that the file does not exist. */ + { + struct stat sb; + + if (!stat (filename, &sb)) + { + err = gpg_error (GPG_ERR_EEXIST); + goto leave; + } + } + /* And a possible detached file or directory may not exist either. */ + err = be_get_detached_name (ctrl->conttype, filename, + &detachedname, &detachedisdir); + if (err) + goto leave; + if (detachedname) + { + struct stat sb; + + if (!stat (detachedname, &sb)) + { + err = gpg_error (GPG_ERR_EEXIST); + goto leave; + } + } + + /* Create a new keyblob. */ + err = create_new_keyblob (ctrl, !!detachedname, &keyblob, &keybloblen); + if (err) + goto leave; + + /* Encrypt that keyblob. */ + err = encrypt_keyblob (ctrl, keyblob, keybloblen, + &enckeyblob, &enckeybloblen); + if (err) + goto leave; + + /* Write out the header, the encrypted keyblob and some padding. */ + err = write_keyblob (ctrl, filename, enckeyblob, enckeybloblen); + if (err) + goto leave; + + /* Create and append the container. */ + + + + leave: + xfree (detachedname); + xfree (enckeyblob); + xfree (keyblob); + destroy_dotlock (lock); + + return err; +} @@ -25,14 +25,18 @@ #include <ctype.h> #include <unistd.h> #include <fcntl.h> +#include <pth.h> #include "g13.h" #include <gcrypt.h> +#include <assuan.h> #include "i18n.h" #include "sysutils.h" #include "gc-opt-flags.h" +#include "create.h" +#include "keyblob.h" enum cmd_and_opt_values { @@ -60,6 +64,8 @@ enum cmd_and_opt_values { oOutput, oAgentProgram, + oGpgProgram, + oDisplay, oTTYname, oTTYtype, @@ -141,6 +147,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoOptions, "no-options", "@"), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), + ARGPARSE_s_s (oGpgProgram, "gpg-program", "@"), ARGPARSE_s_s (oDisplay, "display", "@"), ARGPARSE_s_s (oTTYname, "ttyname", "@"), ARGPARSE_s_s (oTTYtype, "ttytype", "@"), @@ -172,6 +179,14 @@ static void set_cmd (enum cmd_and_opt_values *ret_cmd, static void emergency_cleanup (void); +/* Begin Pth wrapper functions. */ +GCRY_THREAD_OPTION_PTH_IMPL; +static int fixed_gcry_pth_init (void) +{ + return pth_self ()? 0 : (pth_init () == FALSE) ? errno : 0; +} +/* End Pth wrapper functions. */ + static const char * my_strusage( int level ) @@ -299,6 +314,7 @@ main ( int argc, char **argv) ARGPARSE_ARGS pargs; int orig_argc; char **orig_argv; + gpg_error_t err; const char *fname; int may_coredump; FILE *configfp = NULL; @@ -324,14 +340,23 @@ main ( int argc, char **argv) gnupg_reopen_std ("g13"); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); - gcry_control (GCRYCTL_DISABLE_INTERNAL_LOCKING); log_set_prefix ("g13", 1); /* Make sure that our subsystems are ready. */ - i18n_init(); + i18n_init (); init_common_subsystems (); + /* Libgcrypt requires us to register the threading model first. + Note that this will also do the pth_init. */ + gcry_threads_pth.init = fixed_gcry_pth_init; + err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth); + if (err) + { + log_fatal ("can't register GNU Pth with Libgcrypt: %s\n", + gpg_strerror (err)); + } + /* Check that the Libgcrypt is suitable. */ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt", @@ -378,6 +403,20 @@ main ( int argc, char **argv) Now we are now working under our real uid */ + /* Setup malloc hooks. */ + { + struct assuan_malloc_hooks malloc_hooks; + + malloc_hooks.malloc = gcry_malloc; + malloc_hooks.realloc = gcry_realloc; + malloc_hooks.free = gcry_free; + assuan_set_malloc_hooks (&malloc_hooks); + } + + /* Prepare libassuan. */ + assuan_set_assuan_log_prefix (log_get_prefix (NULL)); + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + /* Setup a default control structure for command line mode. */ memset (&ctrl, 0, sizeof ctrl); @@ -394,29 +433,31 @@ main ( int argc, char **argv) pargs.flags = 1; /* Do not remove the args. */ next_pass: - if (configname) { - configlineno = 0; - configfp = fopen (configname, "r"); - if (!configfp) - { - if (default_config) - { - if (parse_debug) - log_info (_("NOTE: no default option file `%s'\n"), configname); - } - else - { - log_error (_("option file `%s': %s\n"), configname, strerror(errno)); - g13_exit(2); - } - xfree (configname); - configname = NULL; - } - if (parse_debug && configname) - log_info (_("reading options from `%s'\n"), configname); - default_config = 0; - } - + if (configname) + { + configlineno = 0; + configfp = fopen (configname, "r"); + if (!configfp) + { + if (default_config) + { + if (parse_debug) + log_info (_("NOTE: no default option file `%s'\n"), configname); + } + else + { + log_error (_("option file `%s': %s\n"), + configname, strerror(errno)); + g13_exit(2); + } + xfree (configname); + configname = NULL; + } + if (parse_debug && configname) + log_info (_("reading options from `%s'\n"), configname); + default_config = 0; + } + while (!no_more_options && optfile_parse (configfp, configname, &configlineno, &pargs, opts)) { @@ -484,6 +525,7 @@ main ( int argc, char **argv) case oHomedir: opt.homedir = pargs.r.ret_str; break; case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; + case oGpgProgram: opt.gpg_program = pargs.r.ret_str; break; case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break; case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break; case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break; @@ -635,7 +677,10 @@ main ( int argc, char **argv) { if (argc != 1) wrong_args ("--create filename"); - + err = create_new_container (&ctrl, argv[0]); + if (err) + log_error ("error creating a new container: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); } break; @@ -647,8 +692,8 @@ main ( int argc, char **argv) /* Print the audit result if needed. */ if (auditlog && auditfp) { - audit_print_result (ctrl.audit, auditfp, 0); - audit_release (ctrl.audit); + /* audit_print_result (ctrl.audit, auditfp, 0); */ + /* audit_release (ctrl.audit); */ ctrl.audit = NULL; es_fclose (auditfp); } @@ -686,7 +731,7 @@ g13_exit (int rc) void g13_init_default_ctrl (struct server_control_s *ctrl) { - (void)ctrl; + ctrl->conttype = CONTTYPE_ENCFS; } @@ -41,7 +41,16 @@ struct const char *homedir; /* Configuration directory name. */ const char *config_filename; /* Name of the used config file. */ + + /* Filename of the AGENT program. */ const char *agent_program; + + /* Filename of the GPG program. Unless set via an program option it + is initialzed at the first engine startup to the standard gpg + filename. */ + const char *gpg_program; + + /* Environment variables passed along to the engine. */ char *display; char *ttyname; char *ttytype; @@ -50,7 +59,9 @@ struct char *xauthority; char *pinentry_user_data; - char *outfile; /* Name of the output file. */ + /* Name of the output file - FIXME: what is this? */ + const char *outfile; + } opt; @@ -83,6 +94,10 @@ struct server_control_s accessed. */ int with_colons; /* Use column delimited output format */ + + /* Type of the current container. See the CONTTYPE_ constants. */ + int conttype; + }; diff --git a/g13/keyblob.h b/g13/keyblob.h new file mode 100644 index 000000000..b52919e0c --- /dev/null +++ b/g13/keyblob.h @@ -0,0 +1,126 @@ +/* keyblob.h - Defs to describe a keyblob + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 G13_KEYBLOB_H +#define G13_KEYBLOB_H + +/* The header block is the actual core of G13. Here is the format: + + u8 Packet type. Value is 61 (0x3d). + u8 Constant value 255 (0xff). + u32 Length of the following structure + b10 Value: "GnuPG/G13\x00". + u8 Version. Value is 1. + u8 reserved + u8 reserved + u8 OS Flag: reserved, should be 0. + u32 Length of the entire header. This includes all bytes + starting at the packet type and ending with the last + padding byte of the header. + u8 Number of copies of this header (1..255). + u8 Number of copies of this header at the end of the + container (usually 0). + b6 reserved + n bytes: OpenPGP encrypted and optionally signed message. + n bytes: CMS encrypted and optionally signed packet. Such a CMS + packet will be enclosed in a a private flagged OpenPGP + packet. Either the OpenPGP encrypted packet as described + above, the CMS encrypted or both packets must exist. The + encapsulation packet has this structure: + u8 Packet type. Value is 61 (0x3d). + u8 Constant value 255 (0xff). + u32 Length of the following structure + b10 Value: "GnuPG/CMS\x00". + b(n) Regular CMS structure. + n bytes: Padding. The structure resembles an OpenPGP packet. + u8 Packet type. Value is 61 (0x3d). + u8 Constant value 255 (0xff). + u32 Length of the following structure + b10 Value: "GnuPG/PAD\x00". + b(n) Padding stuff. + Given this structure the minimum padding is 16 bytes. + + n bytes: File system container. + (optionally followed by copies on the header). +*/ + + +#define KEYBLOB_TAG_BLOBVERSION 0 +/* This tag is used to describe the version of the keyblob. It must + be the first tag in a keyblob. Its value is a single byte giving + the blob version. The current version is 1. */ + +#define KEYBLOB_TAG_CONTTYPE 1 +/* This tag gives the type of the container. The value is a two byte + big endian integer giving the type of the container as described by + the CONTTYPE_ constants. */ + +#define KEYBLOB_TAG_DETACHED 2 +/* Indicates that the actual storage is not in the same file as the + keyblob. If a value is given it is expected to be the GUID of the + partition. */ + +#define KEYBLOB_TAG_KEYNO 16 +/* This tag indicates a new key. The value is a 4 byte big endian + integer giving the key number. If the container type does only + need one key this key number should be 0. */ + +#define KEYBLOB_TAG_ENCALGO 17 +/* Describes the algorithm of the key. It must follow a KEYNO tag. + The value is a 2 byte big endian algorithm number. The algorithm + numbers used are those from Libgcrypt (e.g. AES 128 is described by + the value 7). This tag is optional. */ + +#define KEYBLOB_TAG_ENCKEY 18 +/* This tag gives the actual encryption key. It must follow a KEYNO + tag. The value is the plain key. */ + +#define KEYBLOB_TAG_MACALGO 19 +/* Describes the MAC algorithm. It must follow a KEYNO tag. The + value is a 2 byte big endian algorithm number describing the MAC + algorithm with a value of 1 indicating HMAC. It is followed by + data specific to the MAC algorithm. In case of HMAC this data is a + 2 byte big endian integer with the Libgcrypt algorithm id of the + hash algorithm. */ + +#define KEYBLOB_TAG_MACKEY 20 +/* This tag gives the actual MACing key. It must follow a KEYNO tag. + The value is the key used for MACing. */ + + +#define KEYBLOB_TAG_FILLER 0xffff +/* This tag may be used for alignment and padding porposes. The value + has no meaning. */ + + + +#define CONTTYPE_ENCFS 1 +/* A EncFS based backend. This requires a whole directory which + includes the encrypted files. Metadata is not encrypted. */ + + +#define CONTTYPE_TRUECRYPT 21571 +/* A Truecrypt (www.truecrypt.org) based container. Due to the design + of truecrypt this requires a second datafile because it is not + possible to to prepend a truecrypt container with our keyblob. */ + + + + +#endif /*G13_KEYBLOB_H*/ diff --git a/g13/utils.c b/g13/utils.c new file mode 100644 index 000000000..15b4426ef --- /dev/null +++ b/g13/utils.c @@ -0,0 +1,51 @@ +/* utils.c - Utility functions + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "g13.h" +#include "utils.h" + + +/* Append the TAG and the VALUE to the MEMBUF. There is no error + checking here; this is instead done while getting the value back + from the membuf. */ +void +append_tuple (membuf_t *membuf, int tag, const void *value, size_t length) +{ + unsigned char buf[2]; + + assert (tag >= 0 && tag <= 0xffff); + assert (length <= 0xffff); + + buf[0] = tag >> 8; + buf[1] = tag; + put_membuf (membuf, buf, 2); + buf[0] = length >> 8; + buf[1] = length; + put_membuf (membuf, buf, 2); + if (length) + put_membuf (membuf, value, length); +} + diff --git a/g13/utils.h b/g13/utils.h new file mode 100644 index 000000000..c1104f759 --- /dev/null +++ b/g13/utils.h @@ -0,0 +1,32 @@ +/* utils.h - Defs for utility fucthe dispatcher to the various backends.ntions + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG 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 G13_UTILS_H +#define G13_UTILS_H + +#include "../common/membuf.h" + + +void append_tuple (membuf_t *membuf, + int tag, const void *value, size_t length); + + + +#endif /*G13_UTILS_H*/ + |