diff --git a/common/Makefile.am b/common/Makefile.am index 678e1a269..c02c60e04 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,211 +1,212 @@ # Makefile for common gnupg modules # Copyright (C) 2001, 2003, 2007, 2010 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 . ## Process this file with automake to produce Makefile.in EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ audit-events.h status-codes.h ChangeLog.jnlib \ ChangeLog-2011.include w32info-rc.h.in gnupg.ico noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a if !HAVE_W32CE_SYSTEM noinst_LIBRARIES += libsimple-pwquery.a endif noinst_PROGRAMS = $(module_tests) $(module_maint_tests) TESTS = $(module_tests) BUILT_SOURCES = audit-events.h status-codes.h MAINTAINERCLEANFILES = audit-events.h status-codes.h AM_CPPFLAGS = AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) include $(top_srcdir)/am/cmacros.am common_sources = \ common-defs.h \ util.h i18n.c i18n.h \ types.h host2net.h dynload.h w32help.h \ mapstrings.c stringhelp.c stringhelp.h \ strlist.c strlist.h \ utf8conv.c utf8conv.h \ argparse.c argparse.h \ logging.c logging.h \ dotlock.c dotlock.h \ mischelp.c mischelp.h \ status.c status.h\ shareddefs.h \ openpgpdefs.h \ gc-opt-flags.h \ keyserver.h \ sexp-parse.h \ tlv.c tlv.h \ init.c init.h \ sexputil.c \ sysutils.c sysutils.h \ homedir.c \ gettime.c gettime.h \ yesno.c \ b64enc.c b64dec.c zb32.c zb32.h \ convert.c \ percent.c \ mbox-util.c mbox-util.h \ miscellaneous.c \ xasprintf.c \ xreadline.c \ membuf.c membuf.h \ iobuf.c iobuf.h \ ttyio.c ttyio.h \ asshelp.c asshelp2.c asshelp.h \ exechelp.h \ signal.c \ audit.c audit.h \ localename.c \ session-env.c session-env.h \ userids.c userids.h \ openpgp-oid.c \ ssh-utils.c ssh-utils.h \ agent-opt.c \ helpfile.c \ mkdir_p.c mkdir_p.h \ - strlist.c strlist.h + strlist.c strlist.h \ + call-gpg.c call-gpg.h if HAVE_W32_SYSTEM common_sources += w32-reg.c w32-afunix.c w32-afunix.h endif # To make the code easier to read we have split home some code into # separate source files. if HAVE_W32_SYSTEM if HAVE_W32CE_SYSTEM common_sources += exechelp-w32ce.c else common_sources += exechelp-w32.c endif else common_sources += exechelp-posix.c endif # Sources only useful without NPTH. without_npth_sources = \ get-passphrase.c get-passphrase.h libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 libcommonpth_a_SOURCES = $(common_sources) libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) if !HAVE_W32CE_SYSTEM libsimple_pwquery_a_SOURCES = \ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) endif libgpgrl_a_SOURCES = \ gpgrlhelp.c if MAINTAINER_MODE # Note: Due to the dependency on Makefile, the file will always be # rebuilt, so we allow this only in maintainer mode. # Create the audit-events.h include file from audit.h # Note: We create the target file in the source directory because it # is a distributed built source. If we would not do that we may end # up with two files and then it is not clear which version of the # files will be picked up. audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ -v namespace=eventstr_ > $(srcdir)/audit-events.h # Create the status-codes.h include file from status.h status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ -v namespace=statusstr_ > $(srcdir)/status-codes.h endif # # Module tests # module_tests = t-stringhelp t-timestuff \ t-convert t-percent t-gettime t-sysutils t-sexputil \ t-session-env t-openpgp-oid t-ssh-utils \ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist if !HAVE_W32CE_SYSTEM module_tests += t-exechelp endif if HAVE_W32_SYSTEM module_tests += t-w32-reg endif if MAINTAINER_MODE module_maint_tests = t-helpfile t-b64 else module_maint_tests = endif t_extra_src = t-support.h t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) t_common_ldadd = libcommon.a \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(LIBICONV) # Common tests t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) t_stringhelp_LDADD = $(t_common_ldadd) t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) t_timestuff_LDADD = $(t_common_ldadd) t_convert_LDADD = $(t_common_ldadd) t_percent_LDADD = $(t_common_ldadd) t_gettime_LDADD = $(t_common_ldadd) t_sysutils_LDADD = $(t_common_ldadd) t_helpfile_LDADD = $(t_common_ldadd) t_sexputil_LDADD = $(t_common_ldadd) t_b64_LDADD = $(t_common_ldadd) t_exechelp_LDADD = $(t_common_ldadd) t_session_env_LDADD = $(t_common_ldadd) t_openpgp_oid_LDADD = $(t_common_ldadd) t_ssh_utils_LDADD = $(t_common_ldadd) t_mapstrings_LDADD = $(t_common_ldadd) t_zb32_SOURCES = t-zb32.c $(t_extra_src) t_zb32_LDADD = $(t_common_ldadd) t_mbox_util_LDADD = $(t_common_ldadd) t_iobuf_LDADD = $(t_common_ldadd) t_strlist_LDADD = $(t_common_ldadd) # System specific test if HAVE_W32_SYSTEM t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) t_w32_reg_LDADD = $(t_common_ldadd) endif # All programs should depend on the created libs. $(PROGRAMS) : libcommon.a libcommonpth.a diff --git a/g13/call-gpg.c b/common/call-gpg.c similarity index 93% rename from g13/call-gpg.c rename to common/call-gpg.c index 0bd935c1f..bcad1d61a 100644 --- a/g13/call-gpg.c +++ b/common/call-gpg.c @@ -1,592 +1,589 @@ /* 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 . */ #include -#include + +#include +#include +#include +#include #include +#include #include -#include #include -#include -#include -#include "g13.h" -#include -#include "i18n.h" #include "call-gpg.h" -#include "utils.h" -#include "../common/exechelp.h" - +#include "exechelp.h" +#include "i18n.h" +#include "logging.h" +#include "membuf.h" +#include "util.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) +start_gpg (ctrl_t ctrl, const char *gpg_program, + 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[10]; 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); + if ( !gpg_program || !*gpg_program ) + gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG); /* Compute argv[0]. */ - if ( !(pgmname = strrchr (opt.gpg_program, '/'))) - pgmname = opt.gpg_program; + if ( !(pgmname = strrchr (gpg_program, '/'))) + pgmname = 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++] = "--trust-model"; argv[i++] = "always"; argv[i++] = NULL; i = 0; if (log_get_fd () != -1) no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr)); if (input_fd != -1) no_close_list[i++] = assuan_fd_from_posix_fd (input_fd); if (output_fd != -1) no_close_list[i++] = assuan_fd_from_posix_fd (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, + err = assuan_pipe_connect (ctx, gpg_program, argv, no_close_list, NULL, NULL, 0); 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_IPC) - 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_main (void *arg) { struct writer_thread_parms *parm = arg; const char *buffer = parm->data; size_t length = parm->datalen; while (length) { ssize_t nwritten; nwritten = npth_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, npth_t *r_thread, gpg_error_t *err_addr) { gpg_error_t err; struct writer_thread_parms *parm; npth_attr_t tattr; npth_t thread; int ret; memset (r_thread, '\0', sizeof (*r_thread)); *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; npth_attr_init (&tattr); npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); ret = npth_create (&thread, &tattr, writer_thread_main, parm); if (ret) { err = gpg_error_from_errno (ret); log_error ("error spawning writer thread: %s\n", gpg_strerror (err)); } else { npth_setname_np (thread, "fd-writer"); err = 0; *r_thread = thread; } npth_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_main (void *arg) { struct reader_thread_parms *parm = arg; char buffer[4096]; int nread; while ( (nread = npth_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, npth_t *r_thread, gpg_error_t *err_addr) { gpg_error_t err; struct reader_thread_parms *parm; npth_attr_t tattr; npth_t thread; int ret; memset (r_thread, '\0', sizeof (*r_thread)); *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; npth_attr_init (&tattr); npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); ret = npth_create (&thread, &tattr, reader_thread_main, parm); if (ret) { err = gpg_error_from_errno (ret); log_error ("error spawning reader thread: %s\n", gpg_strerror (err)); } else { npth_setname_np (thread, "fd-reader"); err = 0; *r_thread = thread; } npth_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, - strlist_t keys, void **r_ciph, size_t *r_ciphlen) +gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program, + const void *plain, size_t plainlen, + strlist_t keys, + void **r_ciph, size_t *r_ciphlen) { gpg_error_t err; assuan_context_t ctx = NULL; int outbound_fds[2] = { -1, -1 }; int inbound_fds[2] = { -1, -1 }; npth_t writer_thread = (npth_t)0; npth_t reader_thread = (npth_t)0; gpg_error_t writer_err, reader_err; membuf_t reader_mb; char line[ASSUAN_LINELENGTH]; strlist_t sl; int ret; *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); + err = start_gpg (ctrl, gpg_program, 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_thread, &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_thread, &reader_err); if (err) return err; outbound_fds[0] = -1; /* The thread owns the FD now. */ /* Run the encryption. */ for (sl = keys; sl; sl = sl->next) { snprintf (line, sizeof line, "RECIPIENT -- %s", sl->d); err = assuan_transact (ctx, line, 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. */ ret = npth_join (reader_thread, NULL); if (ret) { err = gpg_error_from_errno (ret); log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); goto leave; } /* FIXME: Not really valid, as npth_t is an opaque type. */ memset (&reader_thread, '\0', sizeof (reader_thread)); 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. */ ret = npth_join (writer_thread, NULL); if (ret) { err = gpg_error_from_errno (ret); log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); goto leave; } memset (&writer_thread, '\0', sizeof (writer_thread)); 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: /* FIXME: Not valid, as npth_t is an opaque type. */ if (reader_thread) npth_detach (reader_thread); if (writer_thread) npth_detach (writer_thread); 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; } /* Call GPG to decrypt a block of data. */ gpg_error_t -gpg_decrypt_blob (ctrl_t ctrl, const void *ciph, size_t ciphlen, +gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program, + const void *ciph, size_t ciphlen, void **r_plain, size_t *r_plainlen) { gpg_error_t err; assuan_context_t ctx = NULL; int outbound_fds[2] = { -1, -1 }; int inbound_fds[2] = { -1, -1 }; npth_t writer_thread = (npth_t)0; npth_t reader_thread = (npth_t)0; gpg_error_t writer_err, reader_err; membuf_t reader_mb; int ret; *r_plain = NULL; *r_plainlen = 0; /* Init the memory buffer to receive the encrypted stuff. */ init_membuf_secure (&reader_mb, 1024); /* 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); + err = start_gpg (ctrl, gpg_program, 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], ciph, ciphlen, &writer_thread, &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_thread, &reader_err); if (err) return err; outbound_fds[0] = -1; /* The thread owns the FD now. */ /* Run the decryption. */ err = assuan_transact (ctx, "DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL); if (err) { log_error ("the engine's DECRYPT command failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); goto leave; } /* Wait for reader and return the data. */ ret = npth_join (reader_thread, NULL); if (ret) { err = gpg_error_from_errno (ret); log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); goto leave; } memset (&reader_thread, '\0', sizeof (reader_thread)); 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. */ ret = npth_join (writer_thread, NULL); if (ret) { err = gpg_error_from_errno (ret); log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); goto leave; } memset (&writer_thread, '\0', sizeof (writer_thread)); if (writer_err) { err = writer_err; log_error ("write error in writer thread: %s\n", gpg_strerror (err)); goto leave; } /* Return the data. */ *r_plain = get_membuf (&reader_mb, r_plainlen); if (!*r_plain) { 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_thread) npth_detach (reader_thread); if (writer_thread) npth_detach (writer_thread); 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/common/call-gpg.h similarity index 82% rename from g13/call-gpg.h rename to common/call-gpg.h index 339544d50..606473d88 100644 --- a/g13/call-gpg.h +++ b/common/call-gpg.h @@ -1,32 +1,37 @@ /* 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 . */ #ifndef G13_CALL_GPG_H #define G13_CALL_GPG_H -gpg_error_t gpg_encrypt_blob (ctrl_t ctrl, +#include "strlist.h" + +typedef struct server_control_s *ctrl_t; + +gpg_error_t gpg_encrypt_blob (ctrl_t ctrl, const char *gpg_program, const void *plain, size_t plainlen, strlist_t keys, void **r_ciph, size_t *r_ciphlen); -gpg_error_t gpg_decrypt_blob (ctrl_t ctrl, const void *ciph, size_t ciphlen, +gpg_error_t gpg_decrypt_blob (ctrl_t ctrl, const char *gpg_program, + const void *ciph, size_t ciphlen, void **r_plain, size_t *r_plainlen); #endif /*G13_CALL_GPG_H*/ diff --git a/g13/Makefile.am b/g13/Makefile.am index 152cf367e..e17d099c3 100644 --- a/g13/Makefile.am +++ b/g13/Makefile.am @@ -1,48 +1,47 @@ # g13/Makefile.am # 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 . ## Process this file with automake to produce Makefile.in EXTRA_DIST = ChangeLog-2011 bin_PROGRAMS = g13 AM_CPPFLAGS = -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) g13_SOURCES = \ g13.c g13.h \ g13-common.c g13-common.h \ keyblob.h \ utils.c utils.h \ server.c server.h \ create.c create.h \ mount.c mount.h \ mountinfo.c mountinfo.h \ - call-gpg.c call-gpg.h \ runner.c runner.h \ backend.c backend.h \ be-encfs.c be-encfs.h \ be-truecrypt.c be-truecrypt.h g13_LDADD = $(libcommonpth) \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) diff --git a/g13/create.c b/g13/create.c index 6c09c2e81..c4e94b830 100644 --- a/g13/create.c +++ b/g13/create.c @@ -1,324 +1,324 @@ /* 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 . */ #include #include #include #include #include #include #include #include #include "g13.h" #include "i18n.h" #include "create.h" #include "keyblob.h" #include "backend.h" #include "utils.h" -#include "call-gpg.h" +#include "../common/call-gpg.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 an error code 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; /* Just for testing. */ 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, strlist_t keys, 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, keys, + err = gpg_encrypt_blob (ctrl, opt.gpg_program, keyblob, keybloblen, keys, r_encblob, r_encbloblen); return err; } /* Write a new file under the name FILENAME with the keyblob and an appropriate header. This function is called with a lock file in place and after checking that the filename does not exists. */ static gpg_error_t write_keyblob (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 version. */ 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 0; 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. KEYS is a list of public keys to which the container will be encrypted. If the file already exists an error is returned. */ gpg_error_t g13_create_container (ctrl_t ctrl, const char *filename, strlist_t keys) { gpg_error_t err; dotlock_t lock; void *keyblob = NULL; size_t keybloblen; void *enckeyblob = NULL; size_t enckeybloblen; char *detachedname = NULL; int detachedisdir; tupledesc_t tuples = NULL; unsigned int dummy_rid; if (!keys) return gpg_error (GPG_ERR_NO_PUBKEY); /* 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 = dotlock_create (filename, 0); if (!lock) return gpg_error_from_syserror (); if (dotlock_take (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, keys, &enckeyblob, &enckeybloblen); if (err) goto leave; /* Put a copy of the keyblob into a tuple structure. */ err = create_tupledesc (&tuples, keyblob, keybloblen); if (err) goto leave; keyblob = NULL; /* if (opt.verbose) */ /* dump_keyblob (tuples); */ /* Write out the header, the encrypted keyblob and some padding. */ err = write_keyblob (filename, enckeyblob, enckeybloblen); if (err) goto leave; /* Create and append the container. FIXME: We should pass the estream object in addition to the filename, so that the backend can append the container to the g13 file. */ err = be_create_container (ctrl, ctrl->conttype, filename, -1, tuples, &dummy_rid); leave: destroy_tupledesc (tuples); xfree (detachedname); xfree (enckeyblob); xfree (keyblob); dotlock_destroy (lock); return err; } diff --git a/g13/mount.c b/g13/mount.c index 8d1c0150f..1f7fbcc4c 100644 --- a/g13/mount.c +++ b/g13/mount.c @@ -1,416 +1,416 @@ /* mount.c - Mount a 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 . */ #include #include #include #include #include #include #include #include #include "g13.h" #include "i18n.h" #include "mount.h" #include "keyblob.h" #include "backend.h" #include "utils.h" #include "../common/sysutils.h" -#include "call-gpg.h" +#include "../common/call-gpg.h" #include "mountinfo.h" #include "runner.h" #include "host2net.h" /* Parse the header prefix and return the length of the entire header. */ static gpg_error_t parse_header (const char *filename, const unsigned char *packet, size_t packetlen, size_t *r_headerlen) { unsigned int len; if (packetlen != 32) return gpg_error (GPG_ERR_BUG); len = buf32_to_uint (packet+2); if (packet[0] != (0xc0|61) || len < 26 || memcmp (packet+6, "GnuPG/G13", 10)) { log_error ("file '%s' is not valid container\n", filename); return gpg_error (GPG_ERR_INV_OBJ); } if (packet[16] != 1) { log_error ("unknown version %u of container '%s'\n", (unsigned int)packet[16], filename); return gpg_error (GPG_ERR_INV_OBJ); } if (packet[17] || packet[18] || packet[26] || packet[27] || packet[28] || packet[29] || packet[30] || packet[31]) log_info ("WARNING: unknown meta information in '%s'\n", filename); if (packet[19]) log_info ("WARNING: OS flag is not supported in '%s'\n", filename); if (packet[24] != 1 || packet[25] != 0) { log_error ("meta data copies in '%s' are not supported\n", filename); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } len = buf32_to_uint (packet+20); /* Do a basic sanity check on the length. */ if (len < 32 || len > 1024*1024) { log_error ("bad length given in container '%s'\n", filename); return gpg_error (GPG_ERR_INV_OBJ); } *r_headerlen = len; return 0; } /* Read the prefix of the keyblob and do some basic parsing. On success returns an open estream file at R_FP and the length of the header at R_HEADERLEN. */ static gpg_error_t read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen) { gpg_error_t err; estream_t fp; unsigned char packet[32]; *r_fp = NULL; fp = es_fopen (filename, "rb"); if (!fp) { err = gpg_error_from_syserror (); log_error ("error reading '%s': %s\n", filename, gpg_strerror (err)); return err; } /* Read the header. It is defined as 32 bytes thus we read it in one go. */ if (es_fread (packet, 32, 1, fp) != 1) { err = gpg_error_from_syserror (); log_error ("error reading the header of '%s': %s\n", filename, gpg_strerror (err)); es_fclose (fp); return err; } err = parse_header (filename, packet, 32, r_headerlen); if (err) es_fclose (fp); else *r_fp = fp; return err; } /* Read the keyblob at FILENAME. The caller should have acquired a lockfile and checked that the file exists. */ static gpg_error_t read_keyblob (const char *filename, void **r_enckeyblob, size_t *r_enckeybloblen) { gpg_error_t err; estream_t fp = NULL; size_t headerlen = 0; size_t msglen; void *msg = NULL; *r_enckeyblob = NULL; *r_enckeybloblen = 0; err = read_keyblob_prefix (filename, &fp, &headerlen); if (err) goto leave; if (opt.verbose) log_info ("header length of '%s' is %zu\n", filename, headerlen); /* Read everything including the padding. We should eventually do a regular OpenPGP parsing to detect the padding packet and pass only the actual used OpenPGP data to the engine. This is in particular required when supporting CMS which will be encapsulated in an OpenPGP packet. */ assert (headerlen >= 32); msglen = headerlen - 32; if (!msglen) { err = gpg_error (GPG_ERR_NO_DATA); goto leave; } msg = xtrymalloc (msglen); if (!msglen) { err = gpg_error_from_syserror (); goto leave; } if (es_fread (msg, msglen, 1, fp) != 1) { err = gpg_error_from_syserror (); log_error ("error reading keyblob of '%s': %s\n", filename, gpg_strerror (err)); goto leave; } *r_enckeyblob = msg; msg = NULL; *r_enckeybloblen = msglen; leave: xfree (msg); es_fclose (fp); return err; } /* Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result at (R_KEYBLOB, R_KEYBLOBLEN). Returns 0 on success or an error code. On error R_KEYBLOB is set to NULL. */ static gpg_error_t decrypt_keyblob (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen, void **r_keyblob, size_t *r_keybloblen) { gpg_error_t err; /* FIXME: For now we only implement OpenPGP. */ - err = gpg_decrypt_blob (ctrl, enckeyblob, enckeybloblen, + err = gpg_decrypt_blob (ctrl, opt.gpg_program, enckeyblob, enckeybloblen, r_keyblob, r_keybloblen); return err; } static void dump_keyblob (tupledesc_t tuples) { size_t n; unsigned int tag; const void *value; log_info ("keyblob dump:\n"); tag = KEYBLOB_TAG_BLOBVERSION; value = find_tuple (tuples, tag, &n); while (value) { log_info (" tag: %-5u len: %-2u value: ", tag, (unsigned int)n); if (tag == KEYBLOB_TAG_ENCKEY || tag == KEYBLOB_TAG_MACKEY) log_printf ("[confidential]\n"); else if (!n) log_printf ("[none]\n"); else log_printhex ("", value, n); value = next_tuple (tuples, &tag, &n); } } /* Mount the container with name FILENAME at MOUNTPOINT. */ gpg_error_t g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) { gpg_error_t err; dotlock_t lock; void *enckeyblob = NULL; size_t enckeybloblen; void *keyblob = NULL; size_t keybloblen; tupledesc_t tuples = NULL; size_t n; const unsigned char *value; int conttype; unsigned int rid; char *mountpoint_buffer = NULL; /* A quick check to see whether the container exists. */ if (access (filename, R_OK)) return gpg_error_from_syserror (); if (!mountpoint) { mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX"); if (!mountpoint_buffer) return gpg_error_from_syserror (); if (!gnupg_mkdtemp (mountpoint_buffer)) { err = gpg_error_from_syserror (); log_error (_("can't create directory '%s': %s\n"), "/tmp/g13-XXXXXX", gpg_strerror (err)); xfree (mountpoint_buffer); return err; } mountpoint = mountpoint_buffer; } /* Try to take a lock. */ lock = dotlock_create (filename, 0); if (!lock) { xfree (mountpoint_buffer); return gpg_error_from_syserror (); } if (dotlock_take (lock, 0)) { err = gpg_error_from_syserror (); goto leave; } else err = 0; /* Check again that the file exists. */ { struct stat sb; if (stat (filename, &sb)) { err = gpg_error_from_syserror (); goto leave; } } /* Read the encrypted keyblob. */ err = read_keyblob (filename, &enckeyblob, &enckeybloblen); if (err) goto leave; /* Decrypt that keyblob and store it in a tuple descriptor. */ err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen, &keyblob, &keybloblen); if (err) goto leave; xfree (enckeyblob); enckeyblob = NULL; err = create_tupledesc (&tuples, keyblob, keybloblen); if (!err) keyblob = NULL; else { if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) log_error ("unknown keyblob version\n"); goto leave; } if (opt.verbose) dump_keyblob (tuples); value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n); if (!value || n != 2) conttype = 0; else conttype = (value[0] << 8 | value[1]); if (!be_is_supported_conttype (conttype)) { log_error ("content type %d is not supported\n", conttype); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid); if (!err) { err = mountinfo_add_mount (filename, mountpoint, conttype, rid, !!mountpoint_buffer); /* Fixme: What shall we do if this fails? Add a provisional mountinfo entry first and remove it on error? */ if (!err) { char *tmp = percent_plus_escape (mountpoint); if (!tmp) err = gpg_error_from_syserror (); else { g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL); xfree (tmp); } } } leave: destroy_tupledesc (tuples); xfree (keyblob); xfree (enckeyblob); dotlock_destroy (lock); xfree (mountpoint_buffer); return err; } /* Unmount the container with name FILENAME or the one mounted at MOUNTPOINT. If both are given the FILENAME takes precedence. */ gpg_error_t g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) { gpg_error_t err; unsigned int rid; runner_t runner; (void)ctrl; if (!filename && !mountpoint) return gpg_error (GPG_ERR_ENOENT); err = mountinfo_find_mount (filename, mountpoint, &rid); if (err) return err; runner = runner_find_by_rid (rid); if (!runner) { log_error ("runner %u not found\n", rid); return gpg_error (GPG_ERR_NOT_FOUND); } runner_cancel (runner); runner_release (runner); return 0; } /* Test whether the container with name FILENAME is a suitable G13 container. This function may even be called on a mounted container. */ gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename) { gpg_error_t err; estream_t fp = NULL; size_t dummy; (void)ctrl; /* Read just the prefix of the header. */ err = read_keyblob_prefix (filename, &fp, &dummy); if (!err) es_fclose (fp); return err; }