diff --git a/common/iobuf.c b/common/iobuf.c index 00d1b8d26..f5bbfb2d2 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -1,2705 +1,2705 @@ /* iobuf.c - File Handling for OpenPGP. * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008, * 2009, 2010, 2011 Free Software Foundation, Inc. * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file 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 #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #endif #ifdef __riscos__ # include # include #endif /* __riscos__ */ #include #include "util.h" #include "sysutils.h" #include "iobuf.h" /*-- Begin configurable part. --*/ /* The size of the internal buffers. NOTE: If you change this value you MUST also adjust the regression test "armored_key_8192" in armor.test! */ #define IOBUF_BUFFER_SIZE 8192 /* To avoid a potential DoS with compression packets we better limit the number of filters in a chain. */ #define MAX_NESTING_FILTER 64 /*-- End configurable part. --*/ #ifdef HAVE_W32_SYSTEM # ifdef HAVE_W32CE_SYSTEM # define FD_FOR_STDIN (es_fileno (es_stdin)) # define FD_FOR_STDOUT (es_fileno (es_stdout)) # else # define FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE)) # define FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE)) # endif #else /*!HAVE_W32_SYSTEM*/ # define FD_FOR_STDIN (0) # define FD_FOR_STDOUT (1) #endif /*!HAVE_W32_SYSTEM*/ /* The context used by the file filter. */ typedef struct { gnupg_fd_t fp; /* Open file pointer or handle. */ int keep_open; int no_cache; int eof_seen; int print_only_name; /* Flags indicating that fname is not a real file. */ char fname[1]; /* Name of the file. */ } file_filter_ctx_t; /* The context used by the estream filter. */ typedef struct { estream_t fp; /* Open estream handle. */ int keep_open; int no_cache; int eof_seen; int print_only_name; /* Flags indicating that fname is not a real file. */ char fname[1]; /* Name of the file. */ } file_es_filter_ctx_t; /* Object to control the "close cache". */ struct close_cache_s { struct close_cache_s *next; gnupg_fd_t fp; char fname[1]; }; typedef struct close_cache_s *close_cache_t; static close_cache_t close_cache; #ifdef HAVE_W32_SYSTEM typedef struct { int sock; int keep_open; int no_cache; int eof_seen; int print_only_name; /* Flag indicating that fname is not a real file. */ char fname[1]; /* Name of the file */ } sock_filter_ctx_t; #endif /*HAVE_W32_SYSTEM*/ /* The first partial length header block must be of size 512 to make * it easier (and more efficient) we use a min. block size of 512 for * all chunks (but the last one) */ #define OP_MIN_PARTIAL_CHUNK 512 #define OP_MIN_PARTIAL_CHUNK_2POW 9 /* The context we use for the block filter (used to handle OpenPGP length information header). */ typedef struct { int use; size_t size; size_t count; int partial; /* 1 = partial header, 2 in last partial packet. */ char *buffer; /* Used for partial header. */ size_t buflen; /* Used size of buffer. */ int first_c; /* First character of a partial header (which is > 0). */ int eof; } block_filter_ctx_t; /* Global flag to tell whether special file names are enabled. See gpg.c for an explanation of these file names. FIXME: This does not belong in the iobuf subsystem. */ static int special_names_enabled; /* Local prototypes. */ static int underflow (iobuf_t a, int clear_pending_eof); static int translate_file_handle (int fd, int for_write); /* Sends any pending data to the filter's FILTER function. Note: this works on the filter and not on the whole pipeline. That is, iobuf_flush doesn't necessarily cause data to be written to any underlying file; it just causes any data buffered at the filter A to be sent to A's filter function. If A is a IOBUF_OUTPUT_TEMP filter, then this also enlarges the buffer by IOBUF_BUFFER_SIZE. May only be called on an IOBUF_OUTPUT or IOBUF_OUTPUT_TEMP filters. */ static int filter_flush (iobuf_t a); /* This is a replacement for strcmp. Under W32 it does not distinguish between backslash and slash. */ static int fd_cache_strcmp (const char *a, const char *b) { #ifdef HAVE_DOSISH_SYSTEM for (; *a && *b; a++, b++) { if (*a != *b && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')) ) break; } return *(const unsigned char *)a - *(const unsigned char *)b; #else return strcmp (a, b); #endif } /* * Invalidate (i.e. close) a cached iobuf */ static int fd_cache_invalidate (const char *fname) { close_cache_t cc; int rc = 0; assert (fname); if (DBG_IOBUF) log_debug ("fd_cache_invalidate (%s)\n", fname); for (cc = close_cache; cc; cc = cc->next) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { if (DBG_IOBUF) log_debug (" did (%s)\n", cc->fname); #ifdef HAVE_W32_SYSTEM if (!CloseHandle (cc->fp)) rc = -1; #else rc = close (cc->fp); #endif cc->fp = GNUPG_INVALID_FD; } } return rc; } /* Try to sync changes to the disk. This is to avoid data loss during a system crash in write/close/rename cycle on some file systems. */ static int fd_cache_synchronize (const char *fname) { int err = 0; #ifdef HAVE_FSYNC close_cache_t cc; if (DBG_IOBUF) log_debug ("fd_cache_synchronize (%s)\n", fname); for (cc=close_cache; cc; cc = cc->next ) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { if (DBG_IOBUF) log_debug (" did (%s)\n", cc->fname); err = fsync (cc->fp); } } #else (void)fname; #endif /*HAVE_FSYNC*/ return err; } static gnupg_fd_t direct_open (const char *fname, const char *mode, int mode700) { #ifdef HAVE_W32_SYSTEM unsigned long da, cd, sm; HANDLE hfile; (void)mode700; /* Note, that we do not handle all mode combinations */ /* According to the ReactOS source it seems that open() of the * standard MSW32 crt does open the file in shared mode which is * something new for MS applications ;-) */ if (strchr (mode, '+')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; da = GENERIC_READ | GENERIC_WRITE; cd = OPEN_EXISTING; sm = FILE_SHARE_READ | FILE_SHARE_WRITE; } else if (strchr (mode, 'w')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; da = GENERIC_WRITE; cd = CREATE_ALWAYS; sm = FILE_SHARE_WRITE; } else { da = GENERIC_READ; cd = OPEN_EXISTING; sm = FILE_SHARE_READ; } #ifdef HAVE_W32CE_SYSTEM { wchar_t *wfname = utf8_to_wchar (fname); if (wfname) { hfile = CreateFile (wfname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); xfree (wfname); } else hfile = INVALID_HANDLE_VALUE; } #else hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL); #endif return hfile; #else /*!HAVE_W32_SYSTEM*/ int oflag; int cflag = S_IRUSR | S_IWUSR; if (!mode700) cflag |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* Note, that we do not handle all mode combinations */ if (strchr (mode, '+')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; oflag = O_RDWR; } else if (strchr (mode, 'w')) { if (fd_cache_invalidate (fname)) return GNUPG_INVALID_FD; oflag = O_WRONLY | O_CREAT | O_TRUNC; } else { oflag = O_RDONLY; } #ifdef O_BINARY if (strchr (mode, 'b')) oflag |= O_BINARY; #endif #ifdef __riscos__ { struct stat buf; /* Don't allow iobufs on directories */ if (!stat (fname, &buf) && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode)) return __set_errno (EISDIR); } #endif return open (fname, oflag, cflag); #endif /*!HAVE_W32_SYSTEM*/ } /* * Instead of closing an FD we keep it open and cache it for later reuse * Note that this caching strategy only works if the process does not chdir. */ static void fd_cache_close (const char *fname, gnupg_fd_t fp) { close_cache_t cc; assert (fp); if (!fname || !*fname) { #ifdef HAVE_W32_SYSTEM CloseHandle (fp); #else close (fp); #endif if (DBG_IOBUF) log_debug ("fd_cache_close (%d) real\n", (int)fp); return; } /* try to reuse a slot */ for (cc = close_cache; cc; cc = cc->next) { if (cc->fp == GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { cc->fp = fp; if (DBG_IOBUF) log_debug ("fd_cache_close (%s) used existing slot\n", fname); return; } } /* add a new one */ if (DBG_IOBUF) log_debug ("fd_cache_close (%s) new slot created\n", fname); cc = xcalloc (1, sizeof *cc + strlen (fname)); strcpy (cc->fname, fname); cc->fp = fp; cc->next = close_cache; close_cache = cc; } /* * Do a direct_open on FNAME but first try to reuse one from the fd_cache */ static gnupg_fd_t fd_cache_open (const char *fname, const char *mode) { close_cache_t cc; assert (fname); for (cc = close_cache; cc; cc = cc->next) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) { gnupg_fd_t fp = cc->fp; cc->fp = GNUPG_INVALID_FD; if (DBG_IOBUF) log_debug ("fd_cache_open (%s) using cached fp\n", fname); #ifdef HAVE_W32_SYSTEM if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff) { log_error ("rewind file failed on handle %p: ec=%d\n", fp, (int) GetLastError ()); fp = GNUPG_INVALID_FD; } #else if (lseek (fp, 0, SEEK_SET) == (off_t) - 1) { log_error ("can't rewind fd %d: %s\n", fp, strerror (errno)); fp = GNUPG_INVALID_FD; } #endif return fp; } } if (DBG_IOBUF) log_debug ("fd_cache_open (%s) not cached\n", fname); return direct_open (fname, mode, 0); } static int file_filter (void *opaque, int control, iobuf_t chain, byte * buf, size_t * ret_len) { file_filter_ctx_t *a = opaque; gnupg_fd_t f = a->fp; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; (void)chain; /* Not used. */ if (control == IOBUFCTRL_UNDERFLOW) { assert (size); /* We need a buffer. */ if (a->eof_seen) { rc = -1; *ret_len = 0; } else { #ifdef HAVE_W32_SYSTEM unsigned long nread; nbytes = 0; if (!ReadFile (f, buf, size, &nread, NULL)) { int ec = (int) GetLastError (); if (ec != ERROR_BROKEN_PIPE) { rc = gpg_error_from_errno (ec); log_error ("%s: read error: ec=%d\n", a->fname, ec); } } else if (!nread) { a->eof_seen = 1; rc = -1; } else { nbytes = nread; } #else int n; nbytes = 0; do { n = read (f, buf, size); } while (n == -1 && errno == EINTR); if (n == -1) { /* error */ if (errno != EPIPE) { rc = gpg_error_from_syserror (); log_error ("%s: read error: %s\n", a->fname, strerror (errno)); } } else if (!n) { /* eof */ a->eof_seen = 1; rc = -1; } else { nbytes = n; } #endif *ret_len = nbytes; } } else if (control == IOBUFCTRL_FLUSH) { if (size) { #ifdef HAVE_W32_SYSTEM byte *p = buf; unsigned long n; nbytes = size; do { if (size && !WriteFile (f, p, nbytes, &n, NULL)) { int ec = (int) GetLastError (); rc = gpg_error_from_errno (ec); log_error ("%s: write error: ec=%d\n", a->fname, ec); break; } p += n; nbytes -= n; } while (nbytes); nbytes = p - buf; #else byte *p = buf; int n; nbytes = size; do { do { n = write (f, p, nbytes); } while (n == -1 && errno == EINTR); if (n > 0) { p += n; nbytes -= n; } } while (n != -1 && nbytes); if (n == -1) { rc = gpg_error_from_syserror (); log_error ("%s: write error: %s\n", a->fname, strerror (errno)); } nbytes = p - buf; #endif } *ret_len = nbytes; } else if (control == IOBUFCTRL_INIT) { a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; } else if (control == IOBUFCTRL_DESC) { mem2str (buf, "file_filter(fd)", *ret_len); } else if (control == IOBUFCTRL_FREE) { if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT) { if (DBG_IOBUF) log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f)); if (!a->keep_open) fd_cache_close (a->no_cache ? NULL : a->fname, f); } xfree (a); /* We can free our context now. */ } return rc; } /* Similar to file_filter but using the estream system. */ static int file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf, size_t * ret_len) { file_es_filter_ctx_t *a = opaque; estream_t f = a->fp; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; (void)chain; /* Not used. */ if (control == IOBUFCTRL_UNDERFLOW) { assert (size); /* We need a buffer. */ if (a->eof_seen) { rc = -1; *ret_len = 0; } else { nbytes = 0; rc = es_read (f, buf, size, &nbytes); if (rc == -1) { /* error */ rc = gpg_error_from_syserror (); log_error ("%s: read error: %s\n", a->fname, strerror (errno)); } else if (!nbytes) { /* eof */ a->eof_seen = 1; rc = -1; } *ret_len = nbytes; } } else if (control == IOBUFCTRL_FLUSH) { if (size) { byte *p = buf; size_t nwritten; nbytes = size; do { nwritten = 0; if (es_write (f, p, nbytes, &nwritten)) { rc = gpg_error_from_syserror (); log_error ("%s: write error: %s\n", a->fname, strerror (errno)); break; } p += nwritten; nbytes -= nwritten; } while (nbytes); nbytes = p - buf; } *ret_len = nbytes; } else if (control == IOBUFCTRL_INIT) { a->eof_seen = 0; a->no_cache = 0; } else if (control == IOBUFCTRL_DESC) { mem2str (buf, "estream_filter", *ret_len); } else if (control == IOBUFCTRL_FREE) { if (f != es_stdin && f != es_stdout) { if (DBG_IOBUF) log_debug ("%s: es_fclose %p\n", a->fname, f); if (!a->keep_open) es_fclose (f); } f = NULL; xfree (a); /* We can free our context now. */ } return rc; } #ifdef HAVE_W32_SYSTEM /* Because network sockets are special objects under Lose32 we have to use a dedicated filter for them. */ static int sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, size_t * ret_len) { sock_filter_ctx_t *a = opaque; size_t size = *ret_len; size_t nbytes = 0; int rc = 0; (void)chain; if (control == IOBUFCTRL_UNDERFLOW) { assert (size); /* need a buffer */ if (a->eof_seen) { rc = -1; *ret_len = 0; } else { int nread; nread = recv (a->sock, buf, size, 0); if (nread == SOCKET_ERROR) { int ec = (int) WSAGetLastError (); rc = gpg_error_from_errno (ec); log_error ("socket read error: ec=%d\n", ec); } else if (!nread) { a->eof_seen = 1; rc = -1; } else { nbytes = nread; } *ret_len = nbytes; } } else if (control == IOBUFCTRL_FLUSH) { if (size) { byte *p = buf; int n; nbytes = size; do { n = send (a->sock, p, nbytes, 0); if (n == SOCKET_ERROR) { int ec = (int) WSAGetLastError (); rc = gpg_error_from_errno (ec); log_error ("socket write error: ec=%d\n", ec); break; } p += n; nbytes -= n; } while (nbytes); nbytes = p - buf; } *ret_len = nbytes; } else if (control == IOBUFCTRL_INIT) { a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; } else if (control == IOBUFCTRL_DESC) { mem2str (buf, "sock_filter", *ret_len); } else if (control == IOBUFCTRL_FREE) { if (!a->keep_open) closesocket (a->sock); xfree (a); /* we can free our context now */ } return rc; } #endif /*HAVE_W32_SYSTEM*/ /**************** * This is used to implement the block write mode. * Block reading is done on a byte by byte basis in readbyte(), * without a filter */ static int block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, size_t * ret_len) { block_filter_ctx_t *a = opaque; char *buf = (char *)buffer; size_t size = *ret_len; int c, needed, rc = 0; char *p; if (control == IOBUFCTRL_UNDERFLOW) { size_t n = 0; p = buf; assert (size); /* need a buffer */ if (a->eof) /* don't read any further */ rc = -1; while (!rc && size) { if (!a->size) { /* get the length bytes */ if (a->partial == 2) { a->eof = 1; if (!n) rc = -1; break; } else if (a->partial) { /* These OpenPGP introduced huffman like encoded length * bytes are really a mess :-( */ if (a->first_c) { c = a->first_c; a->first_c = 0; } else if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: 1st length byte missing\n"); rc = GPG_ERR_BAD_DATA; break; } if (c < 192) { a->size = c; a->partial = 2; if (!a->size) { a->eof = 1; if (!n) rc = -1; break; } } else if (c < 224) { a->size = (c - 192) * 256; if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: 2nd length byte missing\n"); rc = GPG_ERR_BAD_DATA; break; } a->size += c + 192; a->partial = 2; if (!a->size) { a->eof = 1; if (!n) rc = -1; break; } } else if (c == 255) { a->size = (size_t)iobuf_get (chain) << 24; a->size |= iobuf_get (chain) << 16; a->size |= iobuf_get (chain) << 8; if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: invalid 4 byte length\n"); rc = GPG_ERR_BAD_DATA; break; } a->size |= c; a->partial = 2; if (!a->size) { a->eof = 1; if (!n) rc = -1; break; } } else { /* Next partial body length. */ a->size = 1 << (c & 0x1f); } /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */ } else BUG (); } while (!rc && size && a->size) { needed = size < a->size ? size : a->size; c = iobuf_read (chain, p, needed); if (c < needed) { if (c == -1) c = 0; log_error ("block_filter %p: read error (size=%lu,a->size=%lu)\n", a, (ulong) size + c, (ulong) a->size + c); rc = GPG_ERR_BAD_DATA; } else { size -= c; a->size -= c; p += c; n += c; } } } *ret_len = n; } else if (control == IOBUFCTRL_FLUSH) { if (a->partial) { /* the complicated openpgp scheme */ size_t blen, n, nbytes = size + a->buflen; assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); if (nbytes < OP_MIN_PARTIAL_CHUNK) { /* not enough to write a partial block out; so we store it */ if (!a->buffer) a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); memcpy (a->buffer + a->buflen, buf, size); a->buflen += size; } else { /* okay, we can write out something */ /* do this in a loop to use the most efficient block lengths */ p = buf; do { /* find the best matching block length - this is limited * by the size of the internal buffering */ for (blen = OP_MIN_PARTIAL_CHUNK * 2, c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes; blen *= 2, c++) ; blen /= 2; c--; /* write the partial length header */ assert (c <= 0x1f); /*;-) */ c |= 0xe0; iobuf_put (chain, c); if ((n = a->buflen)) { /* write stuff from the buffer */ assert (n == OP_MIN_PARTIAL_CHUNK); if (iobuf_write (chain, a->buffer, n)) rc = gpg_error_from_syserror (); a->buflen = 0; nbytes -= n; } if ((n = nbytes) > blen) n = blen; if (n && iobuf_write (chain, p, n)) rc = gpg_error_from_syserror (); p += n; nbytes -= n; } while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK); /* store the rest in the buffer */ if (!rc && nbytes) { assert (!a->buflen); assert (nbytes < OP_MIN_PARTIAL_CHUNK); if (!a->buffer) a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); memcpy (a->buffer, p, nbytes); a->buflen = nbytes; } } } else BUG (); } else if (control == IOBUFCTRL_INIT) { if (DBG_IOBUF) log_debug ("init block_filter %p\n", a); if (a->partial) a->count = 0; else if (a->use == IOBUF_INPUT) a->count = a->size = 0; else a->count = a->size; /* force first length bytes */ a->eof = 0; a->buffer = NULL; a->buflen = 0; } else if (control == IOBUFCTRL_DESC) { mem2str (buf, "block_filter", *ret_len); } else if (control == IOBUFCTRL_FREE) { if (a->use == IOBUF_OUTPUT) { /* write the end markers */ if (a->partial) { u32 len; /* write out the remaining bytes without a partial header * the length of this header may be 0 - but if it is * the first block we are not allowed to use a partial header * and frankly we can't do so, because this length must be * a power of 2. This is _really_ complicated because we * have to check the possible length of a packet prior * to it's creation: a chain of filters becomes complicated * and we need a lot of code to handle compressed packets etc. * :-((((((( */ /* construct header */ len = a->buflen; /*log_debug("partial: remaining length=%u\n", len ); */ if (len < 192) rc = iobuf_put (chain, len); else if (len < 8384) { if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192))) rc = iobuf_put (chain, ((len - 192) % 256)); } else { /* use a 4 byte header */ if (!(rc = iobuf_put (chain, 0xff))) if (!(rc = iobuf_put (chain, (len >> 24) & 0xff))) if (!(rc = iobuf_put (chain, (len >> 16) & 0xff))) if (!(rc = iobuf_put (chain, (len >> 8) & 0xff))) rc = iobuf_put (chain, len & 0xff); } if (!rc && len) rc = iobuf_write (chain, a->buffer, len); if (rc) { log_error ("block_filter: write error: %s\n", strerror (errno)); rc = gpg_error_from_syserror (); } xfree (a->buffer); a->buffer = NULL; a->buflen = 0; } else BUG (); } else if (a->size) { log_error ("block_filter: pending bytes!\n"); } if (DBG_IOBUF) log_debug ("free block_filter %p\n", a); xfree (a); /* we can free our context now */ } return rc; } #define MAX_IOBUF_DESC 32 /* * Fill the buffer by the description of iobuf A. * The buffer size should be MAX_IOBUF_DESC (or larger). * Returns BUF as (const char *). */ static const char * iobuf_desc (iobuf_t a, byte *buf) { size_t len = MAX_IOBUF_DESC; if (! a || ! a->filter) memcpy (buf, "?", 2); else a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, buf, &len); return buf; } static void print_chain (iobuf_t a) { if (!DBG_IOBUF) return; for (; a; a = a->chain) { byte desc[MAX_IOBUF_DESC]; log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n", a->no, a->subno, iobuf_desc (a, desc), a->filter_eof, (int) a->d.start, (int) a->d.len); } } int iobuf_print_chain (iobuf_t a) { print_chain (a); return 0; } iobuf_t iobuf_alloc (int use, size_t bufsize) { iobuf_t a; static int number = 0; assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP); if (bufsize == 0) { log_bug ("iobuf_alloc() passed a bufsize of 0!\n"); bufsize = IOBUF_BUFFER_SIZE; } a = xcalloc (1, sizeof *a); a->use = use; a->d.buf = xmalloc (bufsize); a->d.size = bufsize; a->no = ++number; a->subno = 0; a->real_fname = NULL; return a; } int iobuf_close (iobuf_t a) { iobuf_t a_chain; size_t dummy_len = 0; int rc = 0; for (; a; a = a_chain) { byte desc[MAX_IOBUF_DESC]; int rc2 = 0; a_chain = a->chain; if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a))) log_error ("filter_flush failed on close: %s\n", gpg_strerror (rc)); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: close '%s'\n", a->no, a->subno, iobuf_desc (a, desc)); if (a->filter && (rc2 = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, NULL, &dummy_len))) log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc)); if (! rc && rc2) /* Whoops! An error occurred. Save it in RC if we haven't already recorded an error. */ rc = rc2; xfree (a->real_fname); if (a->d.buf) { memset (a->d.buf, 0, a->d.size); /* erase the buffer */ xfree (a->d.buf); } xfree (a); } return rc; } int iobuf_cancel (iobuf_t a) { const char *s; iobuf_t a2; int rc; #if defined(HAVE_W32_SYSTEM) || defined(__riscos__) char *remove_name = NULL; #endif if (a && a->use == IOBUF_OUTPUT) { s = iobuf_get_real_fname (a); if (s && *s) { #if defined(HAVE_W32_SYSTEM) || defined(__riscos__) remove_name = xstrdup (s); #else remove (s); #endif } } /* send a cancel message to all filters */ for (a2 = a; a2; a2 = a2->chain) { size_t dummy; if (a2->filter) a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy); } rc = iobuf_close (a); #if defined(HAVE_W32_SYSTEM) || defined(__riscos__) if (remove_name) { /* Argg, MSDOS does not allow to remove open files. So * we have to do it here */ #ifdef HAVE_W32CE_SYSTEM wchar_t *wtmp = utf8_to_wchar (remove_name); if (wtmp) DeleteFile (wtmp); xfree (wtmp); #else remove (remove_name); #endif xfree (remove_name); } #endif return rc; } iobuf_t iobuf_temp (void) { return iobuf_alloc (IOBUF_OUTPUT_TEMP, IOBUF_BUFFER_SIZE); } iobuf_t iobuf_temp_with_content (const char *buffer, size_t length) { iobuf_t a; int i; a = iobuf_alloc (IOBUF_INPUT_TEMP, length); assert (length == a->d.size); /* memcpy (a->d.buf, buffer, length); */ for (i=0; i < length; i++) a->d.buf[i] = buffer[i]; a->d.len = length; return a; } void iobuf_enable_special_filenames (int yes) { special_names_enabled = yes; } /* See whether the filename has the form "-&nnnn", where n is a non-zero number. Returns this number or -1 if it is not the case. */ static int check_special_filename (const char *fname) { if (special_names_enabled && fname && *fname == '-' && fname[1] == '&') { int i; fname += 2; for (i = 0; digitp (fname+i); i++) ; if (!fname[i]) return atoi (fname); } return -1; } int iobuf_is_pipe_filename (const char *fname) { if (!fname || (*fname=='-' && !fname[1]) ) return 1; return check_special_filename (fname) != -1; } static iobuf_t do_open (const char *fname, int special_filenames, int use, const char *opentype, int mode700) { iobuf_t a; gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len = 0; int print_only = 0; int fd; byte desc[MAX_IOBUF_DESC]; assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT); if (special_filenames /* NULL or '-'. */ && (!fname || (*fname == '-' && !fname[1]))) { if (use == IOBUF_INPUT) { fp = FD_FOR_STDIN; fname = "[stdin]"; } else { fp = FD_FOR_STDOUT; fname = "[stdout]"; } print_only = 1; } else if (!fname) return NULL; else if (special_filenames && (fd = check_special_filename (fname)) != -1) return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1), opentype); else { if (use == IOBUF_INPUT) fp = fd_cache_open (fname, opentype); else fp = direct_open (fname, opentype, mode700); if (fp == GNUPG_INVALID_FD) return NULL; } a = iobuf_alloc (use, IOBUF_BUFFER_SIZE); fcx = xmalloc (sizeof *fcx + strlen (fname)); fcx->fp = fp; fcx->print_only_name = print_only; strcpy (fcx->fname, fname); if (!print_only) a->real_fname = xstrdup (fname); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n", a->no, a->subno, fname, iobuf_desc (a, desc), FD2INT (fcx->fp)); return a; } iobuf_t iobuf_open (const char *fname) { return do_open (fname, 1, IOBUF_INPUT, "rb", 0); } iobuf_t iobuf_create (const char *fname, int mode700) { return do_open (fname, 1, IOBUF_OUTPUT, "wb", mode700); } iobuf_t iobuf_openrw (const char *fname) { return do_open (fname, 0, IOBUF_OUTPUT, "r+b", 0); } static iobuf_t do_iobuf_fdopen (int fd, const char *mode, int keep_open) { iobuf_t a; gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len; fp = INT2FD (fd); a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, IOBUF_BUFFER_SIZE); fcx = xmalloc (sizeof *fcx + 20); fcx->fp = fp; fcx->print_only_name = 1; fcx->keep_open = keep_open; sprintf (fcx->fname, "[fd %d]", fd); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: fdopen%s '%s'\n", a->no, a->subno, keep_open? "_nc":"", fcx->fname); iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); return a; } iobuf_t iobuf_fdopen (int fd, const char *mode) { return do_iobuf_fdopen (fd, mode, 0); } iobuf_t iobuf_fdopen_nc (int fd, const char *mode) { return do_iobuf_fdopen (fd, mode, 1); } iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open) { iobuf_t a; file_es_filter_ctx_t *fcx; size_t len = 0; a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, IOBUF_BUFFER_SIZE); fcx = xtrymalloc (sizeof *fcx + 30); fcx->fp = estream; fcx->print_only_name = 1; fcx->keep_open = keep_open; sprintf (fcx->fname, "[fd %p]", estream); a->filter = file_es_filter; a->filter_ov = fcx; file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: esopen%s '%s'\n", a->no, a->subno, keep_open? "_nc":"", fcx->fname); return a; } iobuf_t iobuf_sockopen (int fd, const char *mode) { iobuf_t a; #ifdef HAVE_W32_SYSTEM sock_filter_ctx_t *scx; size_t len; a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, IOBUF_BUFFER_SIZE); scx = xmalloc (sizeof *scx + 25); scx->sock = fd; scx->print_only_name = 1; sprintf (scx->fname, "[sock %d]", fd); a->filter = sock_filter; a->filter_ov = scx; sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname); iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); #else a = iobuf_fdopen (fd, mode); #endif return a; } int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval) { byte desc[MAX_IOBUF_DESC]; if (cmd == IOBUF_IOCTL_KEEP_OPEN) { /* Keep system filepointer/descriptor open. This was used in the past by http.c; this ioctl is not directly used anymore. */ if (DBG_IOBUF) log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n", a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc), intval); for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; b->keep_open = intval; return 0; } #ifdef HAVE_W32_SYSTEM else if (!a->chain && a->filter == sock_filter) { sock_filter_ctx_t *b = a->filter_ov; b->keep_open = intval; return 0; } #endif } else if (cmd == IOBUF_IOCTL_INVALIDATE_CACHE) { if (DBG_IOBUF) log_debug ("iobuf-*.*: ioctl '%s' invalidate\n", ptrval ? (char *) ptrval : "?"); if (!a && !intval && ptrval) { if (fd_cache_invalidate (ptrval)) return -1; return 0; } } else if (cmd == IOBUF_IOCTL_NO_CACHE) { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n", a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc), intval); for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; b->no_cache = intval; return 0; } #ifdef HAVE_W32_SYSTEM else if (!a->chain && a->filter == sock_filter) { sock_filter_ctx_t *b = a->filter_ov; b->no_cache = intval; return 0; } #endif } else if (cmd == IOBUF_IOCTL_FSYNC) { /* Do a fsync on the open fd and return any errors to the caller of iobuf_ioctl. Note that we work on a file name here. */ if (DBG_IOBUF) log_debug ("iobuf-*.*: ioctl '%s' fsync\n", ptrval? (const char*)ptrval:""); - if (!a && !intval && ptrval) - { - return fd_cache_synchronize (ptrval); - } - } + if (!a && !intval && ptrval) + { + return fd_cache_synchronize (ptrval); + } + } return -1; } /**************** * Register an i/o filter. */ int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control, iobuf_t chain, byte * buf, size_t * len), void *ov) { return iobuf_push_filter2 (a, f, ov, 0); } int iobuf_push_filter2 (iobuf_t a, int (*f) (void *opaque, int control, iobuf_t chain, byte * buf, size_t * len), void *ov, int rel_ov) { iobuf_t b; size_t dummy_len = 0; int rc = 0; if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a))) return rc; if (a->subno >= MAX_NESTING_FILTER) { log_error ("i/o filter too deeply nested - corrupted data?\n"); return GPG_ERR_BAD_DATA; } /* We want to create a new filter and put it in front of A. A simple implementation would do: b = iobuf_alloc (...); b->chain = a; return a; This is a bit problematic: A is the head of the pipeline and there are potentially many pointers to it. Requiring the caller to update all of these pointers is a burden. An alternative implementation would add a level of indirection. For instance, we could use a pipeline object, which contains a pointer to the first filter in the pipeline. This is not what we do either. Instead, we allocate a new buffer (B) and copy the first filter's state into that and use the initial buffer (A) for the new filter. One limitation of this approach is that it is not practical to maintain a pointer to a specific filter's state. Before: A | v 0x100 0x200 +----------+ +----------+ | filter x |--------->| filter y |---->.... +----------+ +----------+ After: B | v 0x300 +----------+ A | filter x | | +----------+ v 0x100 ^ v 0x200 +----------+ +----------+ | filter w | | filter y |---->.... +----------+ +----------+ Note: filter x's address changed from 0x100 to 0x300, but A still points to the head of the pipeline. */ b = xmalloc (sizeof *b); memcpy (b, a, sizeof *b); /* fixme: it is stupid to keep a copy of the name at every level * but we need the name somewhere because the name known by file_filter * may have been released when we need the name of the file */ b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL; /* remove the filter stuff from the new stream */ a->filter = NULL; a->filter_ov = NULL; a->filter_ov_owner = 0; a->filter_eof = 0; if (a->use == IOBUF_OUTPUT_TEMP) /* A TEMP filter buffers any data sent to it; it does not forward any data down the pipeline. If we add a new filter to the pipeline, it shouldn't also buffer data. It should send it downstream to be buffered. Thus, the correct type for a filter added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not IOBUF_OUTPUT_TEMP. */ { a->use = IOBUF_OUTPUT; /* When pipeline is written to, the temp buffer's size is increased accordingly. We don't need to allocate a 10 MB buffer for a non-terminal filter. Just use the default size. */ a->d.size = IOBUF_BUFFER_SIZE; } else if (a->use == IOBUF_INPUT_TEMP) /* Same idea as above. */ { a->use = IOBUF_INPUT; a->d.size = IOBUF_BUFFER_SIZE; } /* The new filter (A) gets a new buffer. If the pipeline is an output or temp pipeline, then giving the buffer to the new filter means that data that was written before the filter was pushed gets sent to the filter. That's clearly wrong. If the pipeline is an input pipeline, then giving the buffer to the new filter (A) means that data that has read from (B), but not yet read from the pipeline won't be processed by the new filter (A)! That's certainly not what we want. */ a->d.buf = xmalloc (a->d.size); a->d.len = 0; a->d.start = 0; /* disable nlimit for the new stream */ a->ntotal = b->ntotal + b->nbytes; a->nlimit = a->nbytes = 0; a->nofast = 0; /* make a link from the new stream to the original stream */ a->chain = b; /* setup the function on the new stream */ a->filter = f; a->filter_ov = ov; a->filter_ov_owner = rel_ov; a->subno = b->subno + 1; if (DBG_IOBUF) { byte desc[MAX_IOBUF_DESC]; log_debug ("iobuf-%d.%d: push '%s'\n", a->no, a->subno, iobuf_desc (a, desc)); print_chain (a); } /* now we can initialize the new function if we have one */ if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain, NULL, &dummy_len))) log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc)); return rc; } /**************** * Remove an i/o filter. */ static int pop_filter (iobuf_t a, int (*f) (void *opaque, int control, iobuf_t chain, byte * buf, size_t * len), void *ov) { iobuf_t b; size_t dummy_len = 0; int rc = 0; byte desc[MAX_IOBUF_DESC]; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: pop '%s'\n", a->no, a->subno, iobuf_desc (a, desc)); if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP) { /* This should be the last filter in the pipeline. */ assert (! a->chain); return 0; } if (!a->filter) { /* this is simple */ b = a->chain; assert (b); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); return 0; } for (b = a; b; b = b->chain) if (b->filter == f && (!ov || b->filter_ov == ov)) break; if (!b) log_bug ("pop_filter(): filter function not found\n"); /* flush this stream if it is an output stream */ if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b))) { log_error ("filter_flush failed in pop_filter: %s\n", gpg_strerror (rc)); return rc; } /* and tell the filter to free it self */ if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain, NULL, &dummy_len))) { log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); return rc; } if (b->filter_ov && b->filter_ov_owner) { xfree (b->filter_ov); b->filter_ov = NULL; } /* and see how to remove it */ if (a == b && !b->chain) log_bug ("can't remove the last filter from the chain\n"); else if (a == b) { /* remove the first iobuf from the chain */ /* everything from b is copied to a. This is save because * a flush has been done on the to be removed entry */ b = a->chain; xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno); } else if (!b->chain) { /* remove the last iobuf from the chain */ log_bug ("Ohh jeee, trying to remove a head filter\n"); } else { /* remove an intermediate iobuf from the chain */ log_bug ("Ohh jeee, trying to remove an intermediate filter\n"); } return rc; } /**************** * read underflow: read more bytes into the buffer and return * the first byte or -1 on EOF. */ static int underflow (iobuf_t a, int clear_pending_eof) { size_t len; int rc; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: buffer size: %d; still buffered: %d => space for %d bytes\n", a->no, a->subno, (int) a->d.size, (int) (a->d.len - a->d.start), (int) (a->d.size - (a->d.len - a->d.start))); if (a->use == IOBUF_INPUT_TEMP) /* By definition, there isn't more data to read into the buffer. */ return -1; assert (a->use == IOBUF_INPUT); /* If there is still some buffered data, then move it to the start of the buffer and try to fill the end of the buffer. (This is useful if we are called from iobuf_peek().) */ assert (a->d.start <= a->d.len); a->d.len -= a->d.start; memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len); a->d.start = 0; if (a->d.len == 0 && a->filter_eof) /* The last time we tried to read from this filter, we got an EOF. We couldn't return the EOF, because there was buffered data. Since there is no longer any buffered data, return the error. */ { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n", a->no, a->subno); if (! clear_pending_eof) return -1; if (a->chain) /* A filter follows this one. Free this filter. */ { iobuf_t b = a->chain; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n", a->no, a->subno); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); print_chain (a); } else a->filter_eof = 0; /* for the top level filter */ return -1; /* return one(!) EOF */ } if (a->d.len == 0 && a->error) /* The last time we tried to read from this filter, we got an error. We couldn't return the error, because there was buffered data. Since there is no longer any buffered data, return the error. */ { if (DBG_IOBUF) log_debug ("iobuf-%d.%d: pending error (%s) returned\n", a->no, a->subno, gpg_strerror (a->error)); return -1; } if (a->filter && ! a->filter_eof && ! a->error) /* We have a filter function and the last time we tried to read we didn't get an EOF or an error. Try to fill the buffer. */ { /* Be careful to account for any buffered data. */ len = a->d.size - a->d.len; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n", a->no, a->subno, (ulong) len); if (len == 0) /* There is no space for more data. Don't bother calling A->FILTER. */ rc = 0; else rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, &a->d.buf[a->d.len], &len); a->d.len += len; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes\n", a->no, a->subno, rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc), (ulong) len); /* if( a->no == 1 ) */ /* log_hexdump (" data:", a->d.buf, len); */ if (rc == -1) /* EOF. */ { size_t dummy_len = 0; /* Tell the filter to free itself */ if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, NULL, &dummy_len))) log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); /* Free everything except for the internal buffer. */ if (a->filter_ov && a->filter_ov_owner) xfree (a->filter_ov); a->filter_ov = NULL; a->filter = NULL; a->filter_eof = 1; if (clear_pending_eof && a->d.len == 0 && a->chain) /* We don't need to keep this filter around at all: - we got an EOF - we have no buffered data - a filter follows this one. Unlink this filter. */ { iobuf_t b = a->chain; if (DBG_IOBUF) log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n", a->no, a->subno); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); xfree (b); print_chain (a); return -1; } else if (a->d.len == 0) /* We can't unlink this filter (it is the only one in the pipeline), but we can immediately return EOF. */ return -1; } else if (rc) /* Record the error. */ { a->error = rc; if (a->d.len == 0) /* There is no buffered data. Immediately return EOF. */ return -1; } } assert (a->d.start <= a->d.len); if (a->d.start < a->d.len) return a->d.buf[a->d.start++]; /* EOF. */ return -1; } static int filter_flush (iobuf_t a) { size_t len; int rc; if (a->use == IOBUF_OUTPUT_TEMP) { /* increase the temp buffer */ size_t newsize = a->d.size + IOBUF_BUFFER_SIZE; if (DBG_IOBUF) log_debug ("increasing temp iobuf from %lu to %lu\n", (ulong) a->d.size, (ulong) newsize); a->d.buf = xrealloc (a->d.buf, newsize); a->d.size = newsize; return 0; } else if (a->use != IOBUF_OUTPUT) log_bug ("flush on non-output iobuf\n"); else if (!a->filter) log_bug ("filter_flush: no filter\n"); len = a->d.len; rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len); if (!rc && len != a->d.len) { log_info ("filter_flush did not write all!\n"); rc = GPG_ERR_INTERNAL; } else if (rc) a->error = rc; a->d.len = 0; return rc; } int iobuf_readbyte (iobuf_t a) { int c; if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP) { log_bug ("iobuf_readbyte called on a non-INPUT pipeline!\n"); return -1; } assert (a->d.start <= a->d.len); if (a->nlimit && a->nbytes >= a->nlimit) return -1; /* forced EOF */ if (a->d.start < a->d.len) { c = a->d.buf[a->d.start++]; } else if ((c = underflow (a, 1)) == -1) return -1; /* EOF */ assert (a->d.start <= a->d.len); /* Note: if underflow doesn't return EOF, then it returns the first byte that was read and advances a->d.start appropriately. */ a->nbytes++; return c; } int iobuf_read (iobuf_t a, void *buffer, unsigned int buflen) { unsigned char *buf = (unsigned char *)buffer; int c, n; if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP) { log_bug ("iobuf_read called on a non-INPUT pipeline!\n"); return -1; } if (a->nlimit) { /* Handle special cases. */ for (n = 0; n < buflen; n++) { if ((c = iobuf_readbyte (a)) == -1) { if (!n) return -1; /* eof */ break; } if (buf) { *buf = c; buf++; } } return n; } n = 0; do { if (n < buflen && a->d.start < a->d.len) /* Drain the buffer. */ { unsigned size = a->d.len - a->d.start; if (size > buflen - n) size = buflen - n; if (buf) memcpy (buf, a->d.buf + a->d.start, size); n += size; a->d.start += size; if (buf) buf += size; } if (n < buflen) /* Draining the internal buffer didn't fill BUFFER. Call underflow to read more data into the filter's internal buffer. */ { if ((c = underflow (a, 1)) == -1) /* EOF. If we managed to read something, don't return EOF now. */ { a->nbytes += n; return n ? n : -1 /*EOF*/; } if (buf) *buf++ = c; n++; } } while (n < buflen); a->nbytes += n; return n; } int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) { int n = 0; assert (buflen > 0); assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP); if (buflen > a->d.size) /* We can't peek more than we can buffer. */ buflen = a->d.size; /* Try to fill the internal buffer with enough data to satisfy the request. */ while (buflen > a->d.len - a->d.start) { if (underflow (a, 0) == -1) /* EOF. We can't read any more. */ break; /* Underflow consumes the first character (it's the return value). unget() it by resetting the "file position". */ assert (a->d.start == 1); a->d.start = 0; } n = a->d.len - a->d.start; if (n > buflen) n = buflen; if (n == 0) /* EOF. */ return -1; memcpy (buf, &a->d.buf[a->d.start], n); return n; } int iobuf_writebyte (iobuf_t a, unsigned int c) { int rc; if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) { log_bug ("iobuf_writebyte called on an input pipeline!\n"); return -1; } if (a->d.len == a->d.size) if ((rc=filter_flush (a))) return rc; assert (a->d.len < a->d.size); a->d.buf[a->d.len++] = c; return 0; } int iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen) { const unsigned char *buf = (const unsigned char *)buffer; int rc; if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) { log_bug ("iobuf_write called on an input pipeline!\n"); return -1; } do { if (buflen && a->d.len < a->d.size) { unsigned size = a->d.size - a->d.len; if (size > buflen) size = buflen; memcpy (a->d.buf + a->d.len, buf, size); buflen -= size; buf += size; a->d.len += size; } if (buflen) { rc = filter_flush (a); if (rc) return rc; } } while (buflen); return 0; } int iobuf_writestr (iobuf_t a, const char *buf) { if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) { log_bug ("iobuf_writestr called on an input pipeline!\n"); return -1; } return iobuf_write (a, buf, strlen (buf)); } int iobuf_write_temp (iobuf_t dest, iobuf_t source) { assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP); iobuf_flush_temp (source); return iobuf_write (dest, source->d.buf, source->d.len); } size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen) { byte desc[MAX_IOBUF_DESC]; size_t n; while (1) { int rc = filter_flush (a); if (rc) log_bug ("Flushing iobuf %d.%d (%s) from iobuf_temp_to_buffer failed. Ignoring.\n", a->no, a->subno, iobuf_desc (a, desc)); if (! a->chain) break; a = a->chain; } n = a->d.len; if (n > buflen) n = buflen; memcpy (buffer, a->d.buf, n); return n; } /* Copies the data from the input iobuf SOURCE to the output iobuf DEST until either an error is encountered or EOF is reached. Returns the number of bytes copies. */ size_t iobuf_copy (iobuf_t dest, iobuf_t source) { char *temp; /* Use a 1 MB buffer. */ const size_t temp_size = 1024 * 1024; size_t nread; size_t nwrote = 0; int err; assert (source->use == IOBUF_INPUT || source->use == IOBUF_INPUT_TEMP); assert (dest->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); temp = xmalloc (temp_size); while (1) { nread = iobuf_read (source, temp, temp_size); if (nread == -1) /* EOF. */ break; err = iobuf_write (dest, temp, nread); if (err) break; nwrote += nread; } xfree (temp); return nwrote; } void iobuf_flush_temp (iobuf_t temp) { if (temp->use == IOBUF_INPUT || temp->use == IOBUF_INPUT_TEMP) log_bug ("iobuf_flush_temp called on an input pipeline!\n"); while (temp->chain) pop_filter (temp, temp->filter, NULL); } void iobuf_set_limit (iobuf_t a, off_t nlimit) { if (nlimit) a->nofast = 1; else a->nofast = 0; a->nlimit = nlimit; a->ntotal += a->nbytes; a->nbytes = 0; } off_t iobuf_get_filelength (iobuf_t a, int *overflow) { if (overflow) *overflow = 0; /* Hmmm: file_filter may have already been removed */ for ( ; a->chain; a = a->chain ) ; if (a->filter != file_filter) return 0; { file_filter_ctx_t *b = a->filter_ov; gnupg_fd_t fp = b->fp; #if defined(HAVE_W32_SYSTEM) ulong size; static int (* __stdcall get_file_size_ex) (void *handle, LARGE_INTEGER *r_size); static int get_file_size_ex_initialized; if (!get_file_size_ex_initialized) { void *handle; handle = dlopen ("kernel32.dll", RTLD_LAZY); if (handle) { get_file_size_ex = dlsym (handle, "GetFileSizeEx"); if (!get_file_size_ex) dlclose (handle); } get_file_size_ex_initialized = 1; } if (get_file_size_ex) { /* This is a newer system with GetFileSizeEx; we use this then because it seem that GetFileSize won't return a proper error in case a file is larger than 4GB. */ LARGE_INTEGER exsize; if (get_file_size_ex (fp, &exsize)) { if (!exsize.u.HighPart) return exsize.u.LowPart; if (overflow) *overflow = 1; return 0; } } else { if ((size=GetFileSize (fp, NULL)) != 0xffffffff) return size; } log_error ("GetFileSize for handle %p failed: %s\n", fp, w32_strerror (0)); #else /*!HAVE_W32_SYSTEM*/ { struct stat st; if ( !fstat (FD2INT (fp), &st) ) return st.st_size; log_error("fstat() failed: %s\n", strerror(errno) ); } #endif /*!HAVE_W32_SYSTEM*/ } return 0; } int iobuf_get_fd (iobuf_t a) { for (; a->chain; a = a->chain) ; if (a->filter != file_filter) return -1; { file_filter_ctx_t *b = a->filter_ov; gnupg_fd_t fp = b->fp; return FD2INT (fp); } } off_t iobuf_tell (iobuf_t a) { return a->ntotal + a->nbytes; } #if !defined(HAVE_FSEEKO) && !defined(fseeko) #ifdef HAVE_LIMITS_H # include #endif #ifndef LONG_MAX # define LONG_MAX ((long) ((unsigned long) -1 >> 1)) #endif #ifndef LONG_MIN # define LONG_MIN (-1 - LONG_MAX) #endif /**************** * A substitute for fseeko, for hosts that don't have it. */ static int fseeko (FILE * stream, off_t newpos, int whence) { while (newpos != (long) newpos) { long pos = newpos < 0 ? LONG_MIN : LONG_MAX; if (fseek (stream, pos, whence) != 0) return -1; newpos -= pos; whence = SEEK_CUR; } return fseek (stream, (long) newpos, whence); } #endif int iobuf_seek (iobuf_t a, off_t newpos) { file_filter_ctx_t *b = NULL; if (a->use == IOBUF_OUTPUT || a->use == IOBUF_INPUT) { /* Find the last filter in the pipeline. */ for (; a->chain; a = a->chain) ; if (a->filter != file_filter) return -1; b = a->filter_ov; #ifdef HAVE_W32_SYSTEM if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff) { log_error ("SetFilePointer failed on handle %p: ec=%d\n", b->fp, (int) GetLastError ()); return -1; } #else if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1) { log_error ("can't lseek: %s\n", strerror (errno)); return -1; } #endif /* Discard the buffer it is not a temp stream. */ a->d.len = 0; } a->d.start = 0; a->nbytes = 0; a->nlimit = 0; a->nofast = 0; a->ntotal = newpos; a->error = 0; /* It is impossible for A->CHAIN to be non-NULL. If A is an INPUT or OUTPUT buffer, then we find the last filter, which is defined as A->CHAIN being NULL. If A is a TEMP filter, then A must be the only filter in the pipe: when iobuf_push_filter adds a filter to the front of a pipeline, it sets the new filter to be an OUTPUT filter if the pipeline is an OUTPUT or TEMP pipeline and to be an INPUT filter if the pipeline is an INPUT pipeline. Thus, only the last filter in a TEMP pipeline can be a */ /* remove filters, but the last */ if (a->chain) log_debug ("pop_filter called in iobuf_seek - please report\n"); while (a->chain) pop_filter (a, a->filter, NULL); return 0; } const char * iobuf_get_real_fname (iobuf_t a) { if (a->real_fname) return a->real_fname; /* the old solution */ for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; return b->print_only_name ? NULL : b->fname; } return NULL; } const char * iobuf_get_fname (iobuf_t a) { for (; a; a = a->chain) if (!a->chain && a->filter == file_filter) { file_filter_ctx_t *b = a->filter_ov; return b->fname; } return NULL; } const char * iobuf_get_fname_nonnull (iobuf_t a) { const char *fname; fname = iobuf_get_fname (a); return fname? fname : "[?]"; } /**************** * enable partial block mode as described in the OpenPGP draft. * LEN is the first length byte on read, but ignored on writes. */ void iobuf_set_partial_block_mode (iobuf_t a, size_t len) { block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx); ctx->use = a->use; if (!len) { if (a->use == IOBUF_INPUT) log_debug ("pop_filter called in set_partial_block_mode" " - please report\n"); /* XXX: This pop_filter doesn't make sense. Since we haven't actually added the filter to the pipeline yet, why are we popping anything? Moreover, since we don't report an error, the caller won't directly see an error. I think that it would be better to push the filter and set a->error to GPG_ERR_BAD_DATA, but Werner thinks it's impossible for len to be 0 (but he doesn't want to remove the check just in case). */ pop_filter (a, block_filter, NULL); } else { ctx->partial = 1; ctx->size = 0; ctx->first_c = len; iobuf_push_filter (a, block_filter, ctx); } } unsigned int iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, unsigned *length_of_buffer, unsigned *max_length) { int c; char *buffer = (char *)*addr_of_buffer; unsigned length = *length_of_buffer; unsigned nbytes = 0; unsigned maxlen = *max_length; char *p; /* The code assumes that we have space for at least a newline and a NUL character in the buffer. This requires at least 2 bytes. We don't complicate the code by handling the stupid corner case, but simply assert that it can't happen. */ assert (length >= 2 || maxlen >= 2); if (!buffer || length <= 1) /* must allocate a new buffer */ { length = 256 <= maxlen ? 256 : maxlen; buffer = xrealloc (buffer, length); *addr_of_buffer = (unsigned char *)buffer; *length_of_buffer = length; } p = buffer; while ((c = iobuf_get (a)) != -1) { *p++ = c; nbytes++; if (c == '\n') break; if (nbytes == length - 1) /* We don't have enough space to add a \n and a \0. Increase the buffer size. */ { if (length == maxlen) /* We reached the buffer's size limit! */ { /* Skip the rest of the line. */ while (c != '\n' && (c = iobuf_get (a)) != -1) ; /* p is pointing at the last byte in the buffer. We always terminate the line with "\n\0" so overwrite the previous byte with a \n. */ assert (p > buffer); p[-1] = '\n'; /* Indicate truncation. */ *max_length = 0; break; } length += length < 1024 ? 256 : 1024; if (length > maxlen) length = maxlen; buffer = xrealloc (buffer, length); *addr_of_buffer = (unsigned char *)buffer; *length_of_buffer = length; p = buffer + nbytes; } } /* Add the terminating NUL. */ *p = 0; /* Return the number of characters written to the buffer including the newline, but not including the terminating NUL. */ return nbytes; } static int translate_file_handle (int fd, int for_write) { #if defined(HAVE_W32CE_SYSTEM) /* This is called only with one of the special filenames. Under W32CE the FD here is not a file descriptor but a rendezvous id, thus we need to finish the pipe first. */ fd = _assuan_w32ce_finish_pipe (fd, for_write); #elif defined(HAVE_W32_SYSTEM) { int x; (void)for_write; if (fd == 0) x = (int) GetStdHandle (STD_INPUT_HANDLE); else if (fd == 1) x = (int) GetStdHandle (STD_OUTPUT_HANDLE); else if (fd == 2) x = (int) GetStdHandle (STD_ERROR_HANDLE); else x = fd; if (x == -1) log_debug ("GetStdHandle(%d) failed: ec=%d\n", fd, (int) GetLastError ()); fd = x; } #else (void)for_write; #endif return fd; } void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial) { if ( partial ) { for (;;) { if (a->nofast || a->d.start >= a->d.len) { if (iobuf_readbyte (a) == -1) { break; } } else { unsigned long count = a->d.len - a->d.start; a->nbytes += count; a->d.start = a->d.len; } } } else { unsigned long remaining = n; while (remaining > 0) { if (a->nofast || a->d.start >= a->d.len) { if (iobuf_readbyte (a) == -1) { break; } --remaining; } else { unsigned long count = a->d.len - a->d.start; if (count > remaining) { count = remaining; } a->nbytes += count; a->d.start += count; remaining -= count; } } } } diff --git a/g10/encrypt.c b/g10/encrypt.c index 46b0be0cc..49ec65b4a 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -1,976 +1,976 @@ /* encrypt.c - Main encryption driver * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 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 "gpg.h" #include "options.h" #include "packet.h" #include "status.h" #include "iobuf.h" #include "keydb.h" #include "util.h" #include "main.h" #include "filter.h" #include "trustdb.h" #include "i18n.h" #include "status.h" #include "pkglue.h" static int encrypt_simple( const char *filename, int mode, int use_seskey ); static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out ); /**************** * Encrypt FILENAME with only the symmetric cipher. Take input from * stdin if FILENAME is NULL. */ int encrypt_symmetric (const char *filename) { return encrypt_simple( filename, 1, 0 ); } /**************** * Encrypt FILENAME as a literal data packet only. Take input from * stdin if FILENAME is NULL. */ int encrypt_store (const char *filename) { return encrypt_simple( filename, 0, 0 ); } static void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) { gcry_cipher_hd_t hd; byte buf[33]; assert ( dek->keylen <= 32 ); if (!*seskey) { *seskey=xmalloc_clear(sizeof(DEK)); (*seskey)->algo=dek->algo; make_session_key(*seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } /* The encrypted session key is prefixed with a one-octet algorithm id. */ buf[0] = (*seskey)->algo; memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); /* We only pass already checked values to the following function, thus we consider any failure as fatal. */ if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) BUG (); if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) BUG (); gcry_cipher_setiv (hd, NULL, 0); gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); gcry_cipher_close (hd); memcpy( enckey, buf, (*seskey)->keylen + 1 ); wipememory( buf, sizeof buf ); /* burn key */ } /* We try very hard to use a MDC */ int use_mdc (pk_list_t pk_list,int algo) { /* RFC-2440 don't has MDC */ if (RFC2440) return 0; /* --force-mdc overrides --disable-mdc */ if(opt.force_mdc) return 1; if(opt.disable_mdc) return 0; /* Do the keys really support MDC? */ if(select_mdc_from_pklist(pk_list)) return 1; /* The keys don't support MDC, so now we do a bit of a hack - if any of the AESes or TWOFISH are in the prefs, we assume that the user can handle a MDC. This is valid for PGP 7, which can handle MDCs though it will not generate them. 2440bis allows this, by the way. */ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256) return 1; if(select_algo_from_prefs(pk_list,PREFTYPE_SYM, CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH) return 1; /* Last try. Use MDC for the modern ciphers. */ if (openpgp_cipher_get_algo_blklen (algo) != 8) return 1; if (opt.verbose) warn_missing_mdc_from_pklist (pk_list); return 0; /* No MDC */ } /* We don't want to use use_seskey yet because older gnupg versions can't handle it, and there isn't really any point unless we're making a message that can be decrypted by a public key or passphrase. */ static int encrypt_simple (const char *filename, int mode, int use_seskey) { iobuf_t inp, out; PACKET pkt; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; byte enckey[33]; int rc = 0; int seskeylen = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t *pfx; int do_compress = !!default_compress_algo(); pfx = new_progress_context (); memset( &cfx, 0, sizeof cfx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); /* Prepare iobufs. */ inp = iobuf_open(filename); if (inp) iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { rc = gpg_error_from_syserror (); log_error(_("can't open '%s': %s\n"), filename? filename: "[stdin]", strerror(errno) ); release_progress_context (pfx); return rc; } handle_progress (pfx, inp, filename); if (opt.textmode) iobuf_push_filter( inp, text_filter, &tfx ); cfx.dek = NULL; if ( mode ) { int canceled; s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; cfx.dek = passphrase_to_dek (NULL, 0, default_cipher_algo(), s2k, 4, NULL, &canceled); if ( !cfx.dek || !cfx.dek->keylen ) { rc = gpg_error (canceled? GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE); xfree (cfx.dek); xfree (s2k); iobuf_close (inp); log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc)); release_progress_context (pfx); return rc; } if (use_seskey && s2k->mode != 1 && s2k->mode != 3) { use_seskey = 0; log_info (_("can't use a symmetric ESK packet " "due to the S2K mode\n")); } if ( use_seskey ) { DEK *dek = NULL; seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); encrypt_seskey( cfx.dek, &dek, enckey ); xfree( cfx.dek ); cfx.dek = dek; } if (opt.verbose) log_info(_("using cipher %s\n"), openpgp_cipher_algo_name (cfx.dek->algo)); cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); } if (do_compress && cfx.dek && cfx.dek->use_mdc && is_file_compressed(filename, &rc)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); do_compress = 0; } if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out ))) { iobuf_cancel (inp); xfree (cfx.dek); xfree (s2k); release_progress_context (pfx); return rc; } if ( opt.armor ) { afx = new_armor_context (); push_armor_filter (afx, out); } if ( s2k ) { PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; if ( use_seskey && seskeylen ) { enc->seskeylen = seskeylen + 1; /* algo id */ memcpy (enc->seskey, enckey, seskeylen + 1 ); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc = build_packet( out, &pkt ))) log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); xfree (enc); } if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); /* Note that PGP 5 has problems decrypting symmetrically encrypted data if the file length is in the inner packet. It works when only partial length headers are use. In the past, we always used partial body length here, but since PGP 2, PGP 6, and PGP 7 need the file length, and nobody should be using PGP 5 nowadays anyway, this is now set to the file length. Note also that this only applies to the RFC-1991 style symmetric messages, and not the RFC-2440 style. PGP 6 and 7 work with either partial length or fixed length with the new style messages. */ if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { /* Note that PT has been initialized above in !no_literal mode. */ pt->timestamp = make_timestamp(); pt->mode = opt.textmode? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0; } else { cfx.datalen = filesize && !do_compress ? filesize : 0; pkt.pkttype = 0; pkt.pkt.generic = NULL; } /* Register the cipher filter. */ if (mode) iobuf_push_filter ( out, cipher_filter, &cfx ); /* Register the compress filter. */ if ( do_compress ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; push_compress_filter (out, &zfx, default_compress_algo()); } /* Do the work. */ if (!opt.no_literal) { if ( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", gpg_strerror (rc) ); } else { /* User requested not to create a literal packet, so we copy the plain data. */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) { log_error ("copying input to output failed: %s\n", gpg_strerror (rc) ); break; } wipememory (copy_buffer, 4096); /* burn buffer */ } /* Finish the stuff. */ iobuf_close (inp); if (rc) iobuf_cancel(out); else { iobuf_close (out); /* fixme: check returncode */ if (mode) write_status ( STATUS_END_ENCRYPTION ); } if (pt) pt->buf = NULL; free_packet (&pkt); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); release_progress_context (pfx); return rc; } int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek) { int canceled; *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY)); (*symkey_s2k)->mode = opt.s2k_mode; (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO; *symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo, *symkey_s2k, 4, NULL, &canceled); if(!*symkey_dek || !(*symkey_dek)->keylen) { xfree(*symkey_dek); xfree(*symkey_s2k); return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE); } return 0; } static int write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, iobuf_t out) { int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo); PKT_symkey_enc *enc; byte enckey[33]; PACKET pkt; enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); encrypt_seskey(symkey_dek,&dek,enckey); enc->version = 4; enc->cipher_algo = opt.s2k_cipher_algo; enc->s2k = *symkey_s2k; enc->seskeylen = seskeylen + 1; /* algo id */ memcpy( enc->seskey, enckey, seskeylen + 1 ); pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc=build_packet(out,&pkt))) log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc)); xfree(enc); return rc; } /* * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. * The caller may provide a checked list of public keys in * PROVIDED_PKS; if not the function builds a list of keys on its own. * * Note that FILEFD is currently only used by cmd_encrypt in the the * not yet finished server.c. */ int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd) { iobuf_t inp = NULL; iobuf_t out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; DEK *symkey_dek = NULL; STRING2KEY *symkey_s2k = NULL; int rc = 0, rc2 = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t *pfx; PK_LIST pk_list; int do_compress; if (filefd != -1 && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ do_compress = !!opt.compress_algo; pfx = new_progress_context (); memset( &cfx, 0, sizeof cfx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if (use_symkey && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) { release_progress_context (pfx); return rc; } if (provided_keys) pk_list = provided_keys; else { if ((rc = build_pk_list (ctrl, remusr, &pk_list))) { release_progress_context (pfx); return rc; } } /* Prepare iobufs. */ #ifdef HAVE_W32_SYSTEM if (filefd == -1) inp = iobuf_open (filename); else { inp = NULL; gpg_err_set_errno (ENOSYS); } #else if (filefd == GNUPG_INVALID_FD) inp = iobuf_open (filename); else inp = iobuf_fdopen_nc (FD2INT(filefd), "rb"); #endif if (inp) iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { char xname[64]; rc = gpg_error_from_syserror (); if (filefd != -1) snprintf (xname, sizeof xname, "[fd %d]", filefd); else if (!filename) strcpy (xname, "[stdin]"); else *xname = 0; log_error (_("can't open '%s': %s\n"), *xname? xname : filename, gpg_strerror (rc) ); goto leave; } if (opt.verbose) log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp)); handle_progress (pfx, inp, filename); if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out); if (rc) goto leave; if (opt.armor) { afx = new_armor_context (); push_armor_filter (afx, out); } /* Create a session key. */ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if (!opt.def_cipher_algo) { /* Try to get it from the prefs. */ cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL); /* The only way select_algo_from_prefs can fail here is when mixing v3 and v4 keys, as v4 keys have an implicit preference entry for 3DES, and the pk_list cannot be empty. In this case, use 3DES anyway as it's the safest choice - perhaps the v3 key is being used in an OpenPGP implementation and we know that the implementation behind any v4 key can handle 3DES. */ if (cfx.dek->algo == -1) { cfx.dek->algo = CIPHER_ALGO_3DES; } /* In case 3DES has been selected, print a warning if any key does not have a preference for AES. This should help to indentify why encrypting to several recipients falls back to 3DES. */ if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (pk_list); } else { if (!opt.expert && (select_algo_from_prefs (pk_list, PREFTYPE_SYM, opt.def_cipher_algo, NULL) != opt.def_cipher_algo)) { log_info(_("WARNING: forcing symmetric cipher %s (%d)" " violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); } cfx.dek->algo = opt.def_cipher_algo; } cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); do_compress = 0; } if (rc2) { rc = rc2; goto leave; } make_session_key (cfx.dek); if (DBG_CRYPTO) log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen ); rc = write_pubkey_enc_from_list (pk_list, cfx.dek, out); if (rc) goto leave; /* We put the passphrase (if any) after any public keys as this seems to be the most useful on the recipient side - there is no point in prompting a user for a passphrase if they have the secret key needed to decrypt. */ if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) goto leave; if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); /* Get the size of the file if possible, i.e., if it is a real file. */ if (filename && *filename && !iobuf_is_pipe_filename (filename) && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; } else cfx.datalen = filesize && !do_compress ? filesize : 0; /* Register the cipher filter. */ iobuf_push_filter (out, cipher_filter, &cfx); /* Register the compress filter. */ if (do_compress) { int compr_algo = opt.compress_algo; if (compr_algo == -1) { compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL); if (compr_algo == -1) compr_algo = DEFAULT_COMPRESS_ALGO; /* Theoretically impossible to get here since uncompressed is implicit. */ } else if (!opt.expert && select_algo_from_prefs(pk_list, PREFTYPE_ZIP, compr_algo, NULL) != compr_algo) { log_info (_("WARNING: forcing compression algorithm %s (%d)" " violates recipient preferences\n"), compress_algo_to_string(compr_algo), compr_algo); } /* Algo 0 means no compression. */ if (compr_algo) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; push_compress_filter (out,&zfx,compr_algo); } } /* Do the work. */ if (!opt.no_literal) { if ((rc = build_packet( out, &pkt ))) log_error ("build_packet failed: %s\n", gpg_strerror (rc)); } else { /* User requested not to create a literal packet, so we copy the plain data. */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1) { rc = iobuf_write (out, copy_buffer, bytes_copied); if (rc) { log_error ("copying input to output failed: %s\n", gpg_strerror (rc)); break; } } wipememory (copy_buffer, 4096); /* Burn the buffer. */ } /* Finish the stuff. */ leave: iobuf_close (inp); if (rc) iobuf_cancel (out); else { iobuf_close (out); /* fixme: check returncode */ write_status (STATUS_END_ENCRYPTION); } if (pt) pt->buf = NULL; free_packet (&pkt); xfree (cfx.dek); xfree (symkey_dek); xfree (symkey_s2k); if (!provided_keys) release_pk_list (pk_list); release_armor_context (afx); release_progress_context (pfx); return rc; } /* * Filter to do a complete public key encryption. */ int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc = 0; if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ { BUG(); /* not used */ } else if ( control == IOBUFCTRL_FLUSH ) /* encrypt */ { if ( !efx->header_okay ) { efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek ); if ( !opt.def_cipher_algo ) { /* Try to get it from the prefs. */ efx->cfx.dek->algo = select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL); if (efx->cfx.dek->algo == -1 ) { /* Because 3DES is implicitly in the prefs, this can only happen if we do not have any public keys in the list. */ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } /* In case 3DES has been selected, print a warning if any key does not have a preference for AES. This should help to indentify why encrypting to several recipients falls back to 3DES. */ if (opt.verbose && efx->cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (efx->pk_list); } else { if (!opt.expert && select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM, opt.def_cipher_algo, NULL) != opt.def_cipher_algo) log_info(_("forcing symmetric cipher %s (%d) " "violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); efx->cfx.dek->algo = opt.def_cipher_algo; } efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen); rc = write_pubkey_enc_from_list (efx->pk_list, efx->cfx.dek, a); if (rc) return rc; - if(efx->symkey_s2k && efx->symkey_dek) - { - rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, - efx->cfx.dek,a); - if(rc) - return rc; - } + if(efx->symkey_s2k && efx->symkey_dek) + { + rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, + efx->cfx.dek,a); + if(rc) + return rc; + } - iobuf_push_filter (a, cipher_filter, &efx->cfx); + iobuf_push_filter (a, cipher_filter, &efx->cfx); - efx->header_okay = 1; - } + efx->header_okay = 1; + } rc = iobuf_write (a, buf, size); } else if (control == IOBUFCTRL_FREE) { xfree (efx->symkey_dek); xfree (efx->symkey_s2k); } else if ( control == IOBUFCTRL_DESC ) { mem2str (buf, "encrypt_filter", *ret_len); } return rc; } /* * Write pubkey-enc packets from the list of PKs to OUT. */ static int write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out) { PACKET pkt; PKT_public_key *pk; PKT_pubkey_enc *enc; int rc; for ( ; pk_list; pk_list = pk_list->next ) { gcry_mpi_t frame; pk = pk_list->pk; print_pubkey_algo_note ( pk->pubkey_algo ); enc = xmalloc_clear ( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = (opt.throw_keyids || (pk_list->flags&1)); if (opt.throw_keyids && (PGP6 || PGP7 || PGP8)) { log_info(_("you may not use %s while in %s mode\n"), "--throw-keyids",compliance_option_string()); compliance_failure(); } /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in an * integer value of n bits. pubkey_nbits gives us the number of * bits we have to use. We then encode the session key in some * way and we get it back in the big intger value FRAME. Then * we use FRAME, the public key PK->PKEY and the algorithm * number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which * returns the encrypted value in the array ENC->DATA. This * array has a size which depends on the used algorithm (e.g. 2 * for Elgamal). We don't need frame anymore because we have * everything now in enc->data which is the passed to * build_packet(). */ frame = encode_session_key (pk->pubkey_algo, dek, pubkey_nbits (pk->pubkey_algo, pk->pkey)); rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey); gcry_mpi_release (frame); if (rc) log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); else { if ( opt.verbose ) { char *ustr = get_user_id_string_native (enc->keyid); log_info (_("%s/%s encrypted for: \"%s\"\n"), openpgp_pk_algo_name (enc->pubkey_algo), openpgp_cipher_algo_name (dek->algo), ustr ); xfree (ustr); } /* And write it. */ init_packet (&pkt); pkt.pkttype = PKT_PUBKEY_ENC; pkt.pkt.pubkey_enc = enc; rc = build_packet (out, &pkt); if (rc) log_error ("build_packet(pubkey_enc) failed: %s\n", gpg_strerror (rc)); } free_pubkey_enc(enc); if (rc) return rc; } return 0; } void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) { int rc = 0; if (opt.outfile) { log_error(_("--output doesn't work for this command\n")); return; } if (!nfiles) { char line[2048]; unsigned int lno = 0; while ( fgets(line, DIM(line), stdin) ) { lno++; if (!*line || line[strlen(line)-1] != '\n') { log_error("input line %u too long or missing LF\n", lno); return; } line[strlen(line)-1] = '\0'; print_file_status(STATUS_FILE_START, line, 2); rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1); if (rc) log_error ("encryption of '%s' failed: %s\n", print_fname_stdin(line), gpg_strerror (rc) ); write_status( STATUS_FILE_DONE ); } } else { while (nfiles--) { print_file_status(STATUS_FILE_START, *files, 2); if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) ) log_error("encryption of '%s' failed: %s\n", print_fname_stdin(*files), gpg_strerror (rc) ); write_status( STATUS_FILE_DONE ); files++; } } } diff --git a/g10/keyring.c b/g10/keyring.c index 7c7b35508..ee079a95f 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -1,1766 +1,1766 @@ /* keyring.c - keyring file handling * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1997-2015 Werner Koch * * 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 "gpg.h" #include "util.h" #include "keyring.h" #include "packet.h" #include "keydb.h" #include "options.h" #include "main.h" /*for check_key_signature()*/ #include "i18n.h" #include "../kbx/keybox.h" typedef struct keyring_resource *KR_RESOURCE; struct keyring_resource { struct keyring_resource *next; int read_only; dotlock_t lockhd; int is_locked; int did_full_scan; char fname[1]; }; typedef struct keyring_resource const * CONST_KR_RESOURCE; static KR_RESOURCE kr_resources; struct keyring_handle { CONST_KR_RESOURCE resource; struct { CONST_KR_RESOURCE kr; IOBUF iobuf; int eof; int error; } current; struct { CONST_KR_RESOURCE kr; off_t offset; size_t pk_no; size_t uid_no; unsigned int n_packets; /*used for delete and update*/ } found, saved_found; struct { char *name; char *pattern; } word_match; }; /* The number of extant handles. */ static int active_handles; static int do_copy (int mode, const char *fname, KBNODE root, off_t start_offset, unsigned int n_packets ); /* We keep a cache of entries that we have entered in the DB. This includes not only public keys, but also subkeys. Note: we'd like to keep the offset of the items that are present, however, this doesn't work, because another concurrent GnuPG process could modify the keyring. */ struct key_present { struct key_present *next; u32 kid[2]; }; /* For the hash table, we use separate chaining with linked lists. This means that we have an array of N linked lists (buckets), which is indexed by KEYID[1] mod N. Elements present in the keyring will be on the list; elements not present in the keyring will not be on the list. Note: since the hash table stores both present and not present information, it cannot be used until we complete a full scan of the keyring. This is indicated by key_present_hash_ready. */ typedef struct key_present **key_present_hash_t; static key_present_hash_t key_present_hash; static int key_present_hash_ready; #define KEY_PRESENT_HASH_BUCKETS 2048 /* Allocate a new value for a key present hash table. */ static struct key_present * key_present_value_new (void) { struct key_present *k; k = xmalloc_clear (sizeof *k); return k; } /* Allocate a new key present hash table. */ static key_present_hash_t key_present_hash_new (void) { struct key_present **tbl; tbl = xmalloc_clear (KEY_PRESENT_HASH_BUCKETS * sizeof *tbl); return tbl; } /* Return whether the value described by KID if it is in the hash table. Otherwise, return NULL. */ static struct key_present * key_present_hash_lookup (key_present_hash_t tbl, u32 *kid) { struct key_present *k; for (k = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return k; return NULL; } /* Add the key to the hash table TBL if it is not already present. */ static void key_present_hash_update (key_present_hash_t tbl, u32 *kid) { struct key_present *k; for (k = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; k; k = k->next) { if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return; } k = key_present_value_new (); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->next = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))] = k; } /* Add all the keys (public and subkeys) present in the keyblock to the hash TBL. */ static void key_present_hash_update_from_kb (key_present_hash_t tbl, KBNODE node) { for (; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; keyid_from_pk (node->pkt->pkt.public_key, aki); key_present_hash_update (tbl, aki); } } } /* * Register a filename for plain keyring files. ptr is set to a * pointer to be used to create a handles etc, or the already-issued * pointer if it has already been registered. The function returns 1 * if a new keyring was registered. */ int keyring_register_filename (const char *fname, int read_only, void **ptr) { KR_RESOURCE kr; if (active_handles) /* There are open handles. */ BUG (); for (kr=kr_resources; kr; kr = kr->next) { if (same_file_p (kr->fname, fname)) { /* Already registered. */ if (read_only) kr->read_only = 1; *ptr=kr; return 0; } } kr = xmalloc (sizeof *kr + strlen (fname)); strcpy (kr->fname, fname); kr->read_only = read_only; kr->lockhd = NULL; kr->is_locked = 0; kr->did_full_scan = 0; /* keep a list of all issued pointers */ kr->next = kr_resources; kr_resources = kr; /* create the offset table the first time a function here is used */ if (!key_present_hash) key_present_hash = key_present_hash_new (); *ptr=kr; return 1; } int keyring_is_writable (void *token) { KR_RESOURCE r = token; return r? (r->read_only || !access (r->fname, W_OK)) : 0; } /* Create a new handle for the resource associated with TOKEN. On error NULL is returned and ERRNO is set. The returned handle must be released using keyring_release (). */ KEYRING_HANDLE keyring_new (void *token) { KEYRING_HANDLE hd; KR_RESOURCE resource = token; assert (resource); hd = xtrycalloc (1, sizeof *hd); if (!hd) return hd; hd->resource = resource; active_handles++; return hd; } void keyring_release (KEYRING_HANDLE hd) { if (!hd) return; assert (active_handles > 0); active_handles--; xfree (hd->word_match.name); xfree (hd->word_match.pattern); iobuf_close (hd->current.iobuf); xfree (hd); } /* Save the current found state in HD for later retrieval by keybox_pop_found_state. Only one state may be saved. */ void keyring_push_found_state (KEYRING_HANDLE hd) { hd->saved_found = hd->found; hd->found.kr = NULL; } /* Restore the saved found state in HD. */ void keyring_pop_found_state (KEYRING_HANDLE hd) { hd->found = hd->saved_found; hd->saved_found.kr = NULL; } const char * keyring_get_resource_name (KEYRING_HANDLE hd) { if (!hd || !hd->resource) return NULL; return hd->resource->fname; } /* * Lock the keyring with the given handle, or unlock if YES is false. * We ignore the handle and lock all registered files. */ int keyring_lock (KEYRING_HANDLE hd, int yes) { KR_RESOURCE kr; int rc = 0; (void)hd; if (yes) { /* first make sure the lock handles are created */ for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (!kr->lockhd) { kr->lockhd = dotlock_create (kr->fname, 0); if (!kr->lockhd) { log_info ("can't allocate lock for '%s'\n", kr->fname ); rc = GPG_ERR_GENERAL; } } } if (rc) return rc; /* and now set the locks */ for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (kr->is_locked) continue; #ifdef HAVE_W32_SYSTEM /* Under Windows we need to CloseHandle the file before we * try to lock it. This is because another process might * have taken the lock and is using keybox_file_rename to * rename the base file. How if our dotlock_take below is * waiting for the lock but we have the base file still * open, keybox_file_rename will never succeed as we are * in a deadlock. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)kr->fname); #endif /*HAVE_W32_SYSTEM*/ if (dotlock_take (kr->lockhd, -1) ) { log_info ("can't lock '%s'\n", kr->fname ); rc = GPG_ERR_GENERAL; } else kr->is_locked = 1; } } if (rc || !yes) { for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (!kr->is_locked) continue; if (dotlock_release (kr->lockhd)) log_info ("can't unlock '%s'\n", kr->fname ); else kr->is_locked = 0; } } return rc; } /* * Return the last found keyblock. Caller must free it. * The returned keyblock has the kbode flag bit 0 set for the node with * the public key used to locate the keyblock or flag bit 1 set for * the user ID node. */ int keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) { PACKET *pkt; int rc; KBNODE keyblock = NULL, node, lastnode; IOBUF a; int in_cert = 0; int pk_no = 0; int uid_no = 0; int save_mode; if (ret_kb) *ret_kb = NULL; if (!hd->found.kr) return -1; /* no successful search */ a = iobuf_open (hd->found.kr->fname); if (!a) { log_error(_("can't open '%s'\n"), hd->found.kr->fname); return GPG_ERR_KEYRING_OPEN; } if (iobuf_seek (a, hd->found.offset) ) { log_error ("can't seek '%s'\n", hd->found.kr->fname); iobuf_close(a); return GPG_ERR_KEYRING_OPEN; } pkt = xmalloc (sizeof *pkt); init_packet (pkt); hd->found.n_packets = 0;; lastnode = NULL; save_mode = set_packet_list_mode(0); while ((rc=parse_packet (a, pkt)) != -1) { hd->found.n_packets++; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) { free_packet (pkt); init_packet (pkt); continue; } if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { if (in_cert) /* It is not this key that is problematic, but the following key. */ { rc = 0; hd->found.n_packets --; } else /* Upper layer needs to handle this. */ { } break; } if (rc) { log_error ("keyring_get_keyblock: read error: %s\n", gpg_strerror (rc) ); rc = GPG_ERR_INV_KEYRING; break; } /* Filter allowed packets. */ switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_SIGNATURE: break; /* Allowed per RFC. */ case PKT_RING_TRUST: case PKT_OLD_COMMENT: case PKT_COMMENT: case PKT_GPG_CONTROL: break; /* Allowed by us. */ default: log_error ("skipped packet of type %d in keyring\n", (int)pkt->pkttype); free_packet(pkt); init_packet(pkt); continue; } if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { hd->found.n_packets--; /* fix counter */ break; /* ready */ } in_cert = 1; if (pkt->pkttype == PKT_RING_TRUST) { /*(this code is duplicated after the loop)*/ if ( lastnode && lastnode->pkt->pkttype == PKT_SIGNATURE && (pkt->pkt.ring_trust->sigcache & 1) ) { /* This is a ring trust packet with a checked signature * status cache following directly a signature paket. * Set the cache status into that signature packet. */ PKT_signature *sig = lastnode->pkt->pkt.signature; sig->flags.checked = 1; sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2); } /* Reset LASTNODE, so that we set the cache status only from * the ring trust packet immediately following a signature. */ lastnode = NULL; free_packet(pkt); init_packet(pkt); continue; } node = lastnode = new_kbnode (pkt); if (!keyblock) keyblock = node; else add_kbnode (keyblock, node); switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: if (++pk_no == hd->found.pk_no) node->flag |= 1; break; case PKT_USER_ID: if (++uid_no == hd->found.uid_no) node->flag |= 2; break; default: break; } pkt = xmalloc (sizeof *pkt); init_packet(pkt); } set_packet_list_mode(save_mode); if (rc == -1 && keyblock) rc = 0; /* got the entire keyblock */ if (rc || !ret_kb) release_kbnode (keyblock); else { /*(duplicated from the loop body)*/ if ( pkt && pkt->pkttype == PKT_RING_TRUST && lastnode && lastnode->pkt->pkttype == PKT_SIGNATURE && (pkt->pkt.ring_trust->sigcache & 1) ) { PKT_signature *sig = lastnode->pkt->pkt.signature; sig->flags.checked = 1; sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2); } *ret_kb = keyblock; } free_packet (pkt); xfree (pkt); iobuf_close(a); /* Make sure that future search operations fail immediately when * we know that we are working on a invalid keyring */ if (gpg_err_code (rc) == GPG_ERR_INV_KEYRING) hd->current.error = rc; return rc; } int keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb) { int rc; if (!hd->found.kr) return -1; /* no successful prior search */ if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); if (!hd->found.n_packets) { /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); return rc; } if (!hd->found.n_packets) BUG (); } /* The open iobuf isn't needed anymore and in fact is a problem when it comes to renaming the keyring files on some operating systems, so close it here */ iobuf_close(hd->current.iobuf); hd->current.iobuf = NULL; /* do the update */ rc = do_copy (3, hd->found.kr->fname, kb, hd->found.offset, hd->found.n_packets ); if (!rc) { if (key_present_hash) { key_present_hash_update_from_kb (key_present_hash, kb); } /* better reset the found info */ hd->found.kr = NULL; hd->found.offset = 0; } return rc; } int keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) { int rc; const char *fname; if (!hd) fname = NULL; else if (hd->found.kr) { fname = hd->found.kr->fname; if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); } else if (hd->current.kr) { fname = hd->current.kr->fname; if (hd->current.kr->read_only) return gpg_error (GPG_ERR_EACCES); } else fname = hd->resource? hd->resource->fname:NULL; if (!fname) return GPG_ERR_GENERAL; /* Close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position * after the write opertions. */ iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; /* do the insert */ rc = do_copy (1, fname, kb, 0, 0 ); if (!rc && key_present_hash) { key_present_hash_update_from_kb (key_present_hash, kb); } return rc; } int keyring_delete_keyblock (KEYRING_HANDLE hd) { int rc; if (!hd->found.kr) return -1; /* no successful prior search */ if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); if (!hd->found.n_packets) { /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); return rc; } if (!hd->found.n_packets) BUG (); } /* close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position * after the write opertions. */ iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; /* do the delete */ rc = do_copy (2, hd->found.kr->fname, NULL, hd->found.offset, hd->found.n_packets ); if (!rc) { /* better reset the found info */ hd->found.kr = NULL; hd->found.offset = 0; /* Delete is a rare operations, so we don't remove the keys * from the offset table */ } return rc; } /* * Start the next search on this handle right at the beginning */ int keyring_search_reset (KEYRING_HANDLE hd) { assert (hd); hd->current.kr = NULL; iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.eof = 0; hd->current.error = 0; hd->found.kr = NULL; hd->found.offset = 0; return 0; } static int prepare_search (KEYRING_HANDLE hd) { if (hd->current.error) { /* If the last key was a legacy key, we simply ignore the error so that we can easily use search_next. */ if (gpg_err_code (hd->current.error) == GPG_ERR_LEGACY_KEY) { if (DBG_LOOKUP) log_debug ("%s: last error was GPG_ERR_LEGACY_KEY, clearing\n", __func__); hd->current.error = 0; } else { if (DBG_LOOKUP) log_debug ("%s: returning last error: %s\n", __func__, gpg_strerror (hd->current.error)); return hd->current.error; /* still in error state */ } } if (hd->current.kr && !hd->current.eof) { if ( !hd->current.iobuf ) { if (DBG_LOOKUP) log_debug ("%s: missing iobuf!\n", __func__); return GPG_ERR_GENERAL; /* Position invalid after a modify. */ } return 0; /* okay */ } if (!hd->current.kr && hd->current.eof) { if (DBG_LOOKUP) log_debug ("%s: EOF!\n", __func__); return -1; /* still EOF */ } if (!hd->current.kr) { /* start search with first keyring */ hd->current.kr = hd->resource; if (!hd->current.kr) { if (DBG_LOOKUP) log_debug ("%s: keyring not available!\n", __func__); - hd->current.eof = 1; - return -1; /* keyring not available */ + hd->current.eof = 1; + return -1; /* keyring not available */ } assert (!hd->current.iobuf); } else { /* EOF */ if (DBG_LOOKUP) log_debug ("%s: EOF\n", __func__); iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.kr = NULL; hd->current.eof = 1; return -1; } hd->current.eof = 0; hd->current.iobuf = iobuf_open (hd->current.kr->fname); if (!hd->current.iobuf) { hd->current.error = gpg_error_from_syserror (); log_error(_("can't open '%s'\n"), hd->current.kr->fname ); return hd->current.error; } return 0; } /* A map of the all characters valid used for word_match() * Valid characters are in in this table converted to uppercase. * because the upper 128 bytes have special meaning, we assume * that they are all valid. * Note: We must use numerical values here in case that this program * will be converted to those little blue HAL9000s with their strange * EBCDIC character set (user ids are UTF-8). * wk 2000-04-13: Hmmm, does this really make sense, given the fact that * we can run gpg now on a S/390 running GNU/Linux, where the code * translation is done by the device drivers? */ static const byte word_match_chars[256] = { /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 40 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 58 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 68 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 78 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* a0 */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a8 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* b0 */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b8 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* c0 */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* c8 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* d0 */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* d8 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* e0 */ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e8 */ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* f0 */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f8 */ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; /**************** * Do a word match (original user id starts with a '+'). * The pattern is already tokenized to a more suitable format: * There are only the real words in it delimited by one space * and all converted to uppercase. * * Returns: 0 if all words match. * * Note: This algorithm is a straightforward one and not very * fast. It works for UTF-8 strings. The uidlen should * be removed but due to the fact that old versions of * pgp don't use UTF-8 we still use the length; this should * be fixed in parse-packet (and replace \0 by some special * UTF-8 encoding) */ static int word_match( const byte *uid, size_t uidlen, const byte *pattern ) { size_t wlen, n; const byte *p; const byte *s; for( s=pattern; *s; ) { do { /* skip leading delimiters */ while( uidlen && !word_match_chars[*uid] ) uid++, uidlen--; /* get length of the word */ n = uidlen; p = uid; while( n && word_match_chars[*p] ) p++, n--; wlen = p - uid; /* and compare against the current word from pattern */ for(n=0, p=uid; n < wlen && s[n] != ' ' && s[n] ; n++, p++ ) { if( word_match_chars[*p] != s[n] ) break; } if( n == wlen && (s[n] == ' ' || !s[n]) ) break; /* found */ uid += wlen; uidlen -= wlen; } while( uidlen ); if( !uidlen ) return -1; /* not found */ /* advance to next word in pattern */ for(; *s != ' ' && *s ; s++ ) ; if( *s ) s++ ; } return 0; /* found */ } /**************** * prepare word word_match; that is parse the name and * build the pattern. * caller has to free the returned pattern */ static char* prepare_word_match (const byte *name) { byte *pattern, *p; int c; /* the original length is always enough for the pattern */ p = pattern = xmalloc(strlen(name)+1); do { /* skip leading delimiters */ while( *name && !word_match_chars[*name] ) name++; /* copy as long as we don't have a delimiter and convert * to uppercase. * fixme: how can we handle utf8 uppercasing */ for( ; *name && (c=word_match_chars[*name]); name++ ) *p++ = c; *p++ = ' '; /* append pattern delimiter */ } while( *name ); p[-1] = 0; /* replace last pattern delimiter by EOS */ return pattern; } static int compare_name (int mode, const char *name, const char *uid, size_t uidlen) { int i; const char *s, *se; if (mode == KEYDB_SEARCH_MODE_EXACT) { for (i=0; name[i] && uidlen; i++, uidlen--) if (uid[i] != name[i]) break; if (!uidlen && !name[i]) return 0; /* found */ } else if (mode == KEYDB_SEARCH_MODE_SUBSTR) { if (ascii_memistr( uid, uidlen, name )) return 0; } else if ( mode == KEYDB_SEARCH_MODE_MAIL || mode == KEYDB_SEARCH_MODE_MAILSUB || mode == KEYDB_SEARCH_MODE_MAILEND) { for (i=0, s= uid; i < uidlen && *s != '<'; s++, i++) ; if (i < uidlen) { /* skip opening delim and one char and look for the closing one*/ s++; i++; for (se=s+1, i++; i < uidlen && *se != '>'; se++, i++) ; if (i < uidlen) { i = se - s; if (mode == KEYDB_SEARCH_MODE_MAIL) { if( strlen(name)-2 == i && !ascii_memcasecmp( s, name+1, i) ) return 0; } else if (mode == KEYDB_SEARCH_MODE_MAILSUB) { if( ascii_memistr( s, i, name ) ) return 0; } else { /* email from end */ /* nyi */ } } } } else if (mode == KEYDB_SEARCH_MODE_WORDS) return word_match (uid, uidlen, name); else BUG(); return -1; /* not found */ } /* * Search through the keyring(s), starting at the current position, * for a keyblock which contains one of the keys described in the DESC array. */ int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t *descindex, int ignore_legacy) { int rc; PACKET pkt; int save_mode; off_t offset, main_offset; size_t n; int need_uid, need_words, need_keyid, need_fpr, any_skip; int pk_no, uid_no; int initial_skip; int scanned_from_start; int use_key_present_hash; PKT_user_id *uid = NULL; PKT_public_key *pk = NULL; u32 aki[2]; /* figure out what information we need */ need_uid = need_words = need_keyid = need_fpr = any_skip = 0; for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: need_uid = 1; break; case KEYDB_SEARCH_MODE_WORDS: need_uid = 1; need_words = 1; break; case KEYDB_SEARCH_MODE_SHORT_KID: case KEYDB_SEARCH_MODE_LONG_KID: need_keyid = 1; break; case KEYDB_SEARCH_MODE_FPR16: case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: need_fpr = 1; break; case KEYDB_SEARCH_MODE_FIRST: /* always restart the search in this mode */ keyring_search_reset (hd); break; default: break; } if (desc[n].skipfnc) { any_skip = 1; need_keyid = 1; } } if (DBG_LOOKUP) log_debug ("%s: need_uid = %d; need_words = %d; need_keyid = %d; need_fpr = %d; any_skip = %d\n", __func__, need_uid, need_words, need_keyid, need_fpr, any_skip); rc = prepare_search (hd); if (rc) { if (DBG_LOOKUP) log_debug ("%s: prepare_search failed: %s (%d)\n", __func__, gpg_strerror (rc), gpg_err_code (rc)); return rc; } use_key_present_hash = !!key_present_hash; if (!use_key_present_hash) { if (DBG_LOOKUP) log_debug ("%s: no offset table.\n", __func__); } else if (!key_present_hash_ready) { if (DBG_LOOKUP) log_debug ("%s: initializing offset table. (need_keyid: %d => 1)\n", __func__, need_keyid); need_keyid = 1; } else if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID) { struct key_present *oi; if (DBG_LOOKUP) log_debug ("%s: look up by long key id, checking cache\n", __func__); oi = key_present_hash_lookup (key_present_hash, desc[0].u.kid); if (!oi) { /* We know that we don't have this key */ if (DBG_LOOKUP) log_debug ("%s: cache says not present\n", __func__); hd->found.kr = NULL; hd->current.eof = 1; return -1; } /* We could now create a positive search status and return. * However the problem is that another instance of gpg may * have changed the keyring so that the offsets are not valid * anymore - therefore we don't do it */ } if (need_words) { const char *name = NULL; log_debug ("word search mode does not yet work\n"); /* FIXME: here is a long standing bug in our function and in addition we just use the first search description */ for (n=0; n < ndesc && !name; n++) { if (desc[n].mode == KEYDB_SEARCH_MODE_WORDS) name = desc[n].u.name; } assert (name); if ( !hd->word_match.name || strcmp (hd->word_match.name, name) ) { /* name changed */ xfree (hd->word_match.name); xfree (hd->word_match.pattern); hd->word_match.name = xstrdup (name); hd->word_match.pattern = prepare_word_match (name); } /* name = hd->word_match.pattern; */ } init_packet(&pkt); save_mode = set_packet_list_mode(0); hd->found.kr = NULL; main_offset = 0; pk_no = uid_no = 0; initial_skip = 1; /* skip until we see the start of a keyblock */ scanned_from_start = iobuf_tell (hd->current.iobuf) == 0; if (DBG_LOOKUP) log_debug ("%s: %ssearching from start of resource.\n", __func__, scanned_from_start ? "" : "not "); while (1) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; rc = search_packet (hd->current.iobuf, &pkt, &offset, need_uid); if (ignore_legacy && gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { free_packet (&pkt); continue; } if (rc) break; if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY) { main_offset = offset; pk_no = uid_no = 0; initial_skip = 0; } if (initial_skip) { free_packet (&pkt); continue; } pk = NULL; uid = NULL; if ( pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_PUBLIC_SUBKEY || pkt.pkttype == PKT_SECRET_KEY || pkt.pkttype == PKT_SECRET_SUBKEY) { pk = pkt.pkt.public_key; ++pk_no; if (need_fpr) { fingerprint_from_pk (pk, afp, &an); while (an < 20) /* fill up to 20 bytes */ afp[an++] = 0; } if (need_keyid) keyid_from_pk (pk, aki); if (use_key_present_hash && !key_present_hash_ready && scanned_from_start) key_present_hash_update (key_present_hash, aki); } else if (pkt.pkttype == PKT_USER_ID) { uid = pkt.pkt.user_id; ++uid_no; } for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_NONE: BUG (); break; case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: case KEYDB_SEARCH_MODE_WORDS: if ( uid && !compare_name (desc[n].mode, desc[n].u.name, uid->name, uid->len)) goto found; break; case KEYDB_SEARCH_MODE_SHORT_KID: if (pk && desc[n].u.kid[1] == aki[1]) goto found; break; case KEYDB_SEARCH_MODE_LONG_KID: if (pk && desc[n].u.kid[0] == aki[0] && desc[n].u.kid[1] == aki[1]) goto found; break; case KEYDB_SEARCH_MODE_FPR16: if (pk && !memcmp (desc[n].u.fpr, afp, 16)) goto found; break; case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: if (pk && !memcmp (desc[n].u.fpr, afp, 20)) goto found; break; case KEYDB_SEARCH_MODE_FIRST: if (pk) goto found; break; case KEYDB_SEARCH_MODE_NEXT: if (pk) goto found; break; default: rc = GPG_ERR_INV_ARG; goto found; } } free_packet (&pkt); continue; found: if (rc) goto real_found; if (DBG_LOOKUP) log_debug ("%s: packet starting at offset %lld matched descriptor %zu\n" , __func__, (long long)offset, n); /* Record which desc we matched on. Note this value is only meaningful if this function returns with no errors. */ if(descindex) *descindex=n; for (n=any_skip?0:ndesc; n < ndesc; n++) { if (desc[n].skipfnc && desc[n].skipfnc (desc[n].skipfncvalue, aki, uid_no)) { if (DBG_LOOKUP) log_debug ("%s: skipping match: desc %zd's skip function returned TRUE\n", __func__, n); break; } } if (n == ndesc) goto real_found; free_packet (&pkt); } real_found: if (!rc) { if (DBG_LOOKUP) log_debug ("%s: returning success\n", __func__); hd->found.offset = main_offset; hd->found.kr = hd->current.kr; hd->found.pk_no = pk? pk_no : 0; hd->found.uid_no = uid? uid_no : 0; } else if (rc == -1) { if (DBG_LOOKUP) log_debug ("%s: no matches (EOF)\n", __func__); hd->current.eof = 1; /* if we scanned all keyrings, we are sure that * all known key IDs are in our offtbl, mark that. */ if (use_key_present_hash && !key_present_hash_ready && scanned_from_start) { KR_RESOURCE kr; /* First set the did_full_scan flag for this keyring. */ for (kr=kr_resources; kr; kr = kr->next) { if (hd->resource == kr) { kr->did_full_scan = 1; break; } } /* Then check whether all flags are set and if so, mark the offtbl ready */ for (kr=kr_resources; kr; kr = kr->next) { if (!kr->did_full_scan) break; } if (!kr) key_present_hash_ready = 1; } } else { if (DBG_LOOKUP) log_debug ("%s: error encountered during search: %s (%d)\n", __func__, gpg_strerror (rc), rc); hd->current.error = rc; } free_packet(&pkt); set_packet_list_mode(save_mode); return rc; } static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, IOBUF *r_fp) { gpg_error_t err; mode_t oldmask; err = keybox_tmp_names (template, 1, r_bakfname, r_tmpfname); if (err) return err; /* Create the temp file with limited access. Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (*r_tmpfname)) { *r_fp = NULL; gpg_err_set_errno (EPERM); } else *r_fp = iobuf_create (*r_tmpfname, 1); umask (oldmask); if (!*r_fp) { err = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), *r_tmpfname, gpg_strerror (err)); xfree (*r_tmpfname); *r_tmpfname = NULL; xfree (*r_bakfname); *r_bakfname = NULL; } return err; } static int rename_tmp_file (const char *bakfname, const char *tmpfname, const char *fname) { int rc = 0; /* Invalidate close caches. */ if (iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname )) { rc = gpg_error_from_syserror (); goto fail; } iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); /* First make a backup file. */ rc = keybox_file_rename (fname, bakfname); if (rc) goto fail; /* then rename the file */ rc = keybox_file_rename (tmpfname, fname); if (rc) { register_secured_file (fname); goto fail; } /* Now make sure the file has the same permissions as the original */ #ifndef HAVE_DOSISH_SYSTEM { struct stat statbuf; statbuf.st_mode=S_IRUSR | S_IWUSR; if (!stat (bakfname, &statbuf) && !chmod (fname, statbuf.st_mode)) ; else log_error ("WARNING: unable to restore permissions to '%s': %s", fname, strerror(errno)); } #endif return 0; fail: return rc; } static int write_keyblock (IOBUF fp, KBNODE keyblock) { KBNODE kbctx = NULL, node; int rc; while ( (node = walk_kbnode (keyblock, &kbctx, 0)) ) { if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* we write it later on our own */ if ( (rc = build_packet (fp, node->pkt) )) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); return rc; } if (node->pkt->pkttype == PKT_SIGNATURE) { /* always write a signature cache packet */ PKT_signature *sig = node->pkt->pkt.signature; unsigned int cacheval = 0; if (sig->flags.checked) { cacheval |= 1; if (sig->flags.valid) cacheval |= 2; } iobuf_put (fp, 0xb0); /* old style packet 12, 1 byte len*/ iobuf_put (fp, 2); /* 2 bytes */ iobuf_put (fp, 0); /* unused */ if (iobuf_put (fp, cacheval)) { rc = gpg_error_from_syserror (); log_error ("writing sigcache packet failed\n"); return rc; } } } return 0; } /* * Walk over all public keyrings, check the signatures and replace the * keyring with a new one where the signature cache is then updated. * This is only done for the public keyrings. */ int keyring_rebuild_cache (void *token,int noisy) { KEYRING_HANDLE hd; KEYDB_SEARCH_DESC desc; KBNODE keyblock = NULL, node; const char *lastresname = NULL, *resname; IOBUF tmpfp = NULL; char *tmpfilename = NULL; char *bakfilename = NULL; int rc; ulong count = 0, sigcount = 0; hd = keyring_new (token); if (!hd) return gpg_error_from_syserror (); memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; rc=keyring_lock (hd, 1); if(rc) goto leave; for (;;) { rc = keyring_search (hd, &desc, 1, NULL, 0); if (rc) break; /* ready. */ desc.mode = KEYDB_SEARCH_MODE_NEXT; resname = keyring_get_resource_name (hd); if (lastresname != resname ) { /* we have switched to a new keyring - commit changes */ if (tmpfp) { if (iobuf_close (tmpfp)) { rc = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", tmpfilename, strerror (errno)); goto leave; } /* because we have switched resources, we can be sure that * the original file is closed */ tmpfp = NULL; } /* Static analyzer note: BAKFILENAME is never NULL here because it is controlled by LASTRESNAME. */ rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; if (rc) goto leave; lastresname = resname; if (noisy && !opt.quiet) log_info (_("caching keyring '%s'\n"), resname); rc = create_tmp_file (resname, &bakfilename, &tmpfilename, &tmpfp); if (rc) goto leave; } if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; release_kbnode (keyblock); rc = keyring_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keyring_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { /* We had a few reports about corrupted keyrings; if we have been called directly from the command line we delete such a keyblock instead of bailing out. */ log_error ("unexpected keyblock found (pkttype=%d)%s\n", keyblock->pkt->pkttype, noisy? " - deleted":""); if (noisy) continue; log_info ("Hint: backup your keys and try running '%s'\n", "gpg --rebuild-keydb-caches"); rc = gpg_error (GPG_ERR_INV_KEYRING); goto leave; } if (keyblock->pkt->pkt.public_key->version < 4) { /* We do not copy/cache v3 keys or any other unknown packets. It is better to remove them from the keyring. The code required to keep them in the keyring would be too complicated. Given that we do not touch the old secring.gpg a suitable backup for decryption of v3 stuff using an older gpg version will always be available. Note: This test is actually superfluous because we already acted upon GPG_ERR_LEGACY_KEY. */ } else { /* Check all signature to set the signature's cache flags. */ for (node=keyblock; node; node=node->next) { /* Note that this doesn't cache the result of a revocation issued by a designated revoker. This is because the pk in question does not carry the revkeys as we haven't merged the key and selfsigs. It is questionable whether this matters very much since there are very very few designated revoker revocation packets out there. */ if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig=node->pkt->pkt.signature; if(!opt.no_sig_cache && sig->flags.checked && sig->flags.valid && (openpgp_md_test_algo(sig->digest_algo) || openpgp_pk_test_algo(sig->pubkey_algo))) sig->flags.checked=sig->flags.valid=0; else check_key_signature (keyblock, node, NULL); sigcount++; } } /* Write the keyblock to the temporary file. */ rc = write_keyblock (tmpfp, keyblock); if (rc) goto leave; if ( !(++count % 50) && noisy && !opt.quiet) log_info (ngettext("%lu keys cached so far (%lu signature)\n", "%lu keys cached so far (%lu signatures)\n", sigcount), count, sigcount); } } /* end main loop */ if (rc == -1) rc = 0; if (rc) { log_error ("keyring_search failed: %s\n", gpg_strerror (rc)); goto leave; } if (noisy || opt.verbose) { log_info (ngettext("%lu key cached", "%lu keys cached", count), count); log_printf (ngettext(" (%lu signature)\n", " (%lu signatures)\n", sigcount), sigcount); } if (tmpfp) { if (iobuf_close (tmpfp)) { rc = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", tmpfilename, strerror (errno)); goto leave; } /* because we have switched resources, we can be sure that * the original file is closed */ tmpfp = NULL; } rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; leave: if (tmpfp) iobuf_cancel (tmpfp); xfree (tmpfilename); xfree (bakfilename); release_kbnode (keyblock); keyring_lock (hd, 0); keyring_release (hd); return rc; } /**************** * Perform insert/delete/update operation. * mode 1 = insert * 2 = delete * 3 = update */ static int do_copy (int mode, const char *fname, KBNODE root, off_t start_offset, unsigned int n_packets ) { IOBUF fp, newfp; int rc=0; char *bakfname = NULL; char *tmpfname = NULL; /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return gpg_error_from_syserror (); fp = iobuf_open (fname); if (mode == 1 && !fp && errno == ENOENT) { /* insert mode but file does not exist: create a new file */ KBNODE kbctx, node; mode_t oldmask; oldmask=umask(077); if (is_secured_filename (fname)) { newfp = NULL; gpg_err_set_errno (EPERM); } else newfp = iobuf_create (fname, 1); umask(oldmask); if( !newfp ) { rc = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), fname, strerror(errno)); return rc; } if( !opt.quiet ) log_info(_("%s: keyring created\n"), fname ); kbctx=NULL; while ( (node = walk_kbnode( root, &kbctx, 0 )) ) { if( (rc = build_packet( newfp, node->pkt )) ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); iobuf_cancel(newfp); return rc; } } if( iobuf_close(newfp) ) { rc = gpg_error_from_syserror (); log_error ("%s: close failed: %s\n", fname, strerror(errno)); return rc; } return 0; /* ready */ } if( !fp ) { rc = gpg_error_from_syserror (); log_error(_("can't open '%s': %s\n"), fname, strerror(errno) ); goto leave; } /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { iobuf_close(fp); goto leave; } if( mode == 1 ) { /* insert */ /* copy everything to the new file */ rc = copy_all_packets (fp, newfp); if( rc != -1 ) { log_error("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 2 || mode == 3 ) { /* delete or update */ /* copy first part to the new file */ rc = copy_some_packets( fp, newfp, start_offset ); if( rc ) { /* should never get EOF here */ log_error ("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } /* skip this keyblock */ assert( n_packets ); rc = skip_some_packets( fp, n_packets ); if( rc ) { log_error("%s: skipping %u packets failed: %s\n", fname, n_packets, gpg_strerror (rc)); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 1 || mode == 3 ) { /* insert or update */ rc = write_keyblock (newfp, root); if (rc) { iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 2 || mode == 3 ) { /* delete or update */ /* copy the rest */ rc = copy_all_packets( fp, newfp ); if( rc != -1 ) { log_error("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } /* close both files */ if( iobuf_close(fp) ) { rc = gpg_error_from_syserror (); log_error("%s: close failed: %s\n", fname, strerror(errno) ); goto leave; } if( iobuf_close(newfp) ) { rc = gpg_error_from_syserror (); log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); goto leave; } rc = rename_tmp_file (bakfname, tmpfname, fname); leave: xfree(bakfname); xfree(tmpfname); return rc; }