diff options
Diffstat (limited to 'common')
36 files changed, 1166 insertions, 246 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index d288fa36b..b6a6605f1 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -83,7 +83,7 @@ common_sources = \ localename.c \ session-env.c session-env.h \ userids.c userids.h \ - openpgp-oid.c \ + openpgp-oid.c openpgp-s2k.c \ ssh-utils.c ssh-utils.h \ agent-opt.c \ helpfile.c \ diff --git a/common/argparse.c b/common/argparse.c index 331998bb2..db0b7e079 100644 --- a/common/argparse.c +++ b/common/argparse.c @@ -408,7 +408,7 @@ static void store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) { /* TODO: replace this dummy function with a rea one - * and fix the probelms IRIX has with (ALIAS_DEV)arg.. + * and fix the problems IRIX has with (ALIAS_DEV)arg.. * used as lvalue */ (void)arg; @@ -439,7 +439,7 @@ ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) /* Add the keywords up to the next LF to the list of to be ignored options. After returning FP will either be at EOF or the next - character read wll be the first of a new line. The function + character read will be the first of a new line. The function returns 0 on success or true on malloc failure. */ static int ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) @@ -1280,7 +1280,7 @@ long_opt_strlen( ARGPARSE_OPTS *o ) * this option * - a description,ine which starts with a '@' and is followed by * any other characters is printed as is; this may be used for examples - * ans such. + * and such. * - A description which starts with a '|' outputs the string between this * bar and the next one as arguments of the long option. */ diff --git a/common/audit.h b/common/audit.h index 4ef2645da..05f39533d 100644 --- a/common/audit.h +++ b/common/audit.h @@ -185,7 +185,7 @@ typedef enum if no real recipient has been given. */ AUDIT_SESSION_KEY, /* string */ - /* Mark the creation or availibility of the session key. The + /* Mark the creation or availability of the session key. The parameter is the algorithm ID. */ AUDIT_ENCRYPTED_TO, /* cert, err */ diff --git a/common/convert.c b/common/convert.c index 6d03adc3d..40fb4eecf 100644 --- a/common/convert.c +++ b/common/convert.c @@ -177,7 +177,7 @@ bin2hexcolon (const void *buffer, size_t length, char *stringbuf) string or a white space character. The function makes sure that the resulting string in BUFFER is terminated by a Nul byte. Note that the returned string may include embedded Nul bytes; the extra - Nul byte at the end is used to make sure tha the result can always + Nul byte at the end is used to make sure that the result can always be used as a C-string. BUFSIZE is the available length of BUFFER; if the converted result diff --git a/common/dotlock.c b/common/dotlock.c index 5227bb64e..1bc31d8a6 100644 --- a/common/dotlock.c +++ b/common/dotlock.c @@ -140,7 +140,7 @@ you pass (0) instead of (-1) the function does not wait in case the file is already locked but returns -1 and sets ERRNO to EACCES. Any other positive value for the second parameter is considered a - timeout valuie in milliseconds. + timeout value in milliseconds. To release the lock you call: diff --git a/common/exectool.c b/common/exectool.c index 3458de4a7..82b398542 100644 --- a/common/exectool.c +++ b/common/exectool.c @@ -53,6 +53,7 @@ typedef struct exec_tool_status_cb_t status_cb; void *status_cb_value; int cont; + int quiet; size_t used; size_t buffer_size; char *buffer; @@ -110,6 +111,8 @@ read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr) state->status_cb (state->status_cb_value, state->buffer + 9, rest); } + else if (state->quiet) + ; else if (!state->cont && !strncmp (state->buffer, pname, len) && strlen (state->buffer) > strlen (pname) @@ -331,10 +334,16 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], int count; read_and_log_buffer_t fderrstate; struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL; + int quiet = 0; + int dummy_exitcode; memset (fds, 0, sizeof fds); memset (&fderrstate, 0, sizeof fderrstate); + /* If the first argument to the program is "--quiet" avoid all extra + * diagnostics. */ + quiet = (argv && argv[0] && !strcmp (argv[0], "--quiet")); + cpbuf_in = xtrymalloc (sizeof *cpbuf_in); if (cpbuf_in == NULL) { @@ -360,6 +369,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], copy_buffer_init (cpbuf_extra); fderrstate.pgmname = pgmname; + fderrstate.quiet = quiet; fderrstate.status_cb = status_cb; fderrstate.status_cb_value = status_cb_value; fderrstate.buffer_size = 256; @@ -375,7 +385,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1); if (err) { - log_error ("error running outbound pipe for extra fp: %s\n", + log_error ("error creating outbound pipe for extra fp: %s\n", gpg_strerror (err)); goto leave; } @@ -411,7 +421,8 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], argv[argsaveidx] = argsave; if (err) { - log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); + if (!quiet) + log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); goto leave; } @@ -535,7 +546,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], es_fclose (outfp); outfp = NULL; es_fclose (errfp); errfp = NULL; - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL); pid = (pid_t)(-1); leave: @@ -547,7 +558,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], es_fclose (outfp); es_fclose (errfp); if (pid != (pid_t)(-1)) - gnupg_wait_process (pgmname, pid, 1, NULL); + gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL); gnupg_release_process (pid); copy_buffer_shred (cpbuf_in); diff --git a/common/iobuf.c b/common/iobuf.c index 02c9b491c..05944255f 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -68,8 +68,8 @@ /*-- End configurable part. --*/ -/* The size of the iobuffers. This can be chnages using the - * iobuf_set_buffer_size fucntion. */ +/* The size of the iobuffers. This can be changed using the + * iobuf_set_buffer_size function. */ static unsigned int iobuf_buffer_size = DEFAULT_IOBUF_BUFFER_SIZE; @@ -878,9 +878,9 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, } else if (c == 255) { - a->size = (size_t)iobuf_get (chain) << 24; - a->size |= iobuf_get (chain) << 16; - a->size |= iobuf_get (chain) << 8; + a->size = iobuf_get_noeof (chain) << 24; + a->size |= iobuf_get_noeof (chain) << 16; + a->size |= iobuf_get_noeof (chain) << 8; if ((c = iobuf_get (chain)) == -1) { log_error ("block_filter: invalid 4 byte length\n"); @@ -2262,6 +2262,7 @@ iobuf_copy (iobuf_t dest, iobuf_t source) size_t nread; size_t nwrote = 0; + size_t max_read = 0; int err; assert (source->use == IOBUF_INPUT || source->use == IOBUF_INPUT_TEMP); @@ -2278,6 +2279,9 @@ iobuf_copy (iobuf_t dest, iobuf_t source) /* EOF. */ break; + if (nread > max_read) + max_read = nread; + err = iobuf_write (dest, temp, nread); if (err) break; @@ -2285,7 +2289,8 @@ iobuf_copy (iobuf_t dest, iobuf_t source) } /* Burn the buffer. */ - wipememory (temp, sizeof (temp)); + if (max_read) + wipememory (temp, max_read); xfree (temp); return nwrote; @@ -2610,12 +2615,50 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, } p = buffer; - while ((c = iobuf_get (a)) != -1) + while (1) { - *p++ = c; - nbytes++; - if (c == '\n') - break; + if (!a->nofast && a->d.start < a->d.len && nbytes < length - 1) + /* Fast path for finding '\n' by using standard C library's optimized + memchr. */ + { + unsigned size = a->d.len - a->d.start; + byte *newline_pos; + + if (size > length - 1 - nbytes) + size = length - 1 - nbytes; + + newline_pos = memchr (a->d.buf + a->d.start, '\n', size); + if (newline_pos) + { + /* Found newline, copy buffer and return. */ + size = (newline_pos - (a->d.buf + a->d.start)) + 1; + memcpy (p, a->d.buf + a->d.start, size); + p += size; + nbytes += size; + a->d.start += size; + a->nbytes += size; + break; + } + else + { + /* No newline, copy buffer and continue. */ + memcpy (p, a->d.buf + a->d.start, size); + p += size; + nbytes += size; + a->d.start += size; + a->nbytes += size; + } + } + else + { + c = iobuf_readbyte (a); + if (c == -1) + break; + *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 diff --git a/common/mbox-util.c b/common/mbox-util.c index 76255ba38..a9086a3f5 100644 --- a/common/mbox-util.c +++ b/common/mbox-util.c @@ -173,11 +173,12 @@ is_valid_mailbox (const char *name) /* Return the mailbox (local-part@domain) form a standard user id. - All plain ASCII characters in the result are converted to - lowercase. Caller must free the result. Returns NULL if no valid - mailbox was found (or we are out of memory). */ + * All plain ASCII characters in the result are converted to + * lowercase. If SUBADDRESS is 1, '+' denoted sub-addresses are not + * included in the result. Caller must free the result. Returns NULL + * if no valid mailbox was found (or we are out of memory). */ char * -mailbox_from_userid (const char *userid) +mailbox_from_userid (const char *userid, int subaddress) { const char *s, *s_end; size_t len; @@ -226,6 +227,29 @@ mailbox_from_userid (const char *userid) else errno = EINVAL; + if (result && subaddress == 1) + { + char *atsign, *plus; + + if ((atsign = strchr (result, '@'))) + { + /* We consider a subaddress only if there is a single '+' + * in the local part and the '+' is not the first or last + * character. */ + *atsign = 0; + if ((plus = strchr (result, '+')) + && !strchr (plus+1, '+') + && result != plus + && plus[1] ) + { + *atsign = '@'; + memmove (plus, atsign, strlen (atsign)+1); + } + else + *atsign = '@'; + } + } + return result? ascii_strlwr (result): NULL; } diff --git a/common/mbox-util.h b/common/mbox-util.h index 7355ceef5..10ff2c4a0 100644 --- a/common/mbox-util.h +++ b/common/mbox-util.h @@ -22,7 +22,7 @@ int has_invalid_email_chars (const void *buffer, size_t length); int is_valid_mailbox (const char *name); int is_valid_mailbox_mem (const void *buffer, size_t length); -char *mailbox_from_userid (const char *userid); +char *mailbox_from_userid (const char *userid, int subaddress); int is_valid_user_id (const char *uid); int is_valid_domain_name (const char *string); diff --git a/common/miscellaneous.c b/common/miscellaneous.c index 0b374e6c8..260552828 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -328,6 +328,82 @@ make_printable_string (const void *p, size_t n, int delim ) } +/* Decode the C formatted string SRC and return the result in a newly + * allocated buffer. In error returns NULL and sets ERRNO. */ +char * +decode_c_string (const char *src) +{ + char *buffer, *dst; + int val; + + /* The converted string will never be larger than the original + string. */ + buffer = dst = xtrymalloc (strlen (src) + 1); + if (!buffer) + return NULL; + + while (*src) + { + if (*src != '\\') + { + *dst++ = *src++; + continue; + } + +#define DECODE_ONE(_m,_r) case _m: src += 2; *dst++ = _r; break; + + switch (src[1]) + { + DECODE_ONE ('n', '\n'); + DECODE_ONE ('r', '\r'); + DECODE_ONE ('f', '\f'); + DECODE_ONE ('v', '\v'); + DECODE_ONE ('b', '\b'); + DECODE_ONE ('t', '\t'); + DECODE_ONE ('\\', '\\'); + DECODE_ONE ('\'', '\''); + DECODE_ONE ('\"', '\"'); + + case 'x': + val = hextobyte (src+2); + if (val == -1) /* Bad coding, keep as is. */ + { + *dst++ = *src++; + *dst++ = *src++; + if (*src) + *dst++ = *src++; + if (*src) + *dst++ = *src++; + } + else if (!val) + { + /* A binary zero is not representable in a C string thus + * we keep the C-escaping. Note that this will also + * never be larger than the source string. */ + *dst++ = '\\'; + *dst++ = '0'; + src += 4; + } + else + { + *(unsigned char *)dst++ = val; + src += 4; + } + break; + + default: /* Bad coding; keep as is.. */ + *dst++ = *src++; + *dst++ = *src++; + break; + } +#undef DECODE_ONE + } + *dst++ = 0; + + return buffer; +} + + /* Check whether (BUF,LEN) is valid header for an OpenPGP compressed * packet. LEN should be at least 6. */ static int diff --git a/common/mischelp.c b/common/mischelp.c index 75ba60714..81dd501f8 100644 --- a/common/mischelp.c +++ b/common/mischelp.c @@ -49,6 +49,22 @@ #include "mischelp.h" +void +wipememory (void *ptr, size_t len) +{ +#if defined(HAVE_W32_SYSTEM) && defined(SecureZeroMemory) + SecureZeroMemory (ptr, len); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero (ptr, len); +#else + /* Prevent compiler from optimizing away the call to memset by accessing + memset through volatile pointer. */ + static void *(*volatile memset_ptr)(void *, int, size_t) = (void *)memset; + memset_ptr (ptr, 0, len); +#endif +} + + /* Check whether the files NAME1 and NAME2 are identical. This is for example achieved by comparing the inode numbers of the files. */ int diff --git a/common/mischelp.h b/common/mischelp.h index 18ec96edf..bdee5a443 100644 --- a/common/mischelp.h +++ b/common/mischelp.h @@ -47,15 +47,9 @@ time_t timegm (struct tm *tm); #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) -/* To avoid that a compiler optimizes certain memset calls away, these - macros may be used instead. */ -#define wipememory2(_ptr,_set,_len) do { \ - volatile char *_vptr=(volatile char *)(_ptr); \ - size_t _vlen=(_len); \ - while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ - } while(0) -#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) - +/* To avoid that a compiler optimizes certain memset calls away, + wipememory function may be used instead. */ +void wipememory(void *ptr, size_t len); /* Include hacks which are mainly required for Slowaris. */ #ifdef GNUPG_COMMON_NEED_AFLOCAL diff --git a/common/mkerrors b/common/mkerrors index 138d3c1d1..2a6960ab6 100755 --- a/common/mkerrors +++ b/common/mkerrors @@ -30,7 +30,7 @@ cat <<EOF * gnupg_strerror: * @err: Error code * - * This function returns a textual representaion of the given + * This function returns a textual representation of the given * errorcode. If this is an unknown value, a string with the value * is returned (Beware: it is hold in a static buffer). * diff --git a/common/mkerrtok b/common/mkerrtok index e6310722a..49c10e595 100755 --- a/common/mkerrtok +++ b/common/mkerrtok @@ -26,7 +26,7 @@ cat <<EOF * gnupg_error_token: * @err: Error code * - * This function returns a textual representaion of the given + * This function returns a textual representation of the given * errorcode. If this is an unknown value, a static string is returned. * This function differs from gnupg_strerror that it yields the string * representation of the macro which is never subject to i18n. diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index d800e7d57..419471870 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -184,48 +184,36 @@ openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) } -/* Return a malloced string represenation of the OID in the opaque MPI - A. In case of an error NULL is returned and ERRNO is set. */ +/* Return a malloced string representation of the OID in the buffer + * (BUF,LEN). In case of an error NULL is returned and ERRNO is set. + * As per OpenPGP spec the first byte of the buffer is the length of + * the rest; the function performs a consistency check. */ char * -openpgp_oid_to_str (gcry_mpi_t a) +openpgp_oidbuf_to_str (const unsigned char *buf, size_t len) { - const unsigned char *buf; - size_t length; - unsigned int lengthi; char *string, *p; int n = 0; unsigned long val, valmask; valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); - - if (!a - || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE) - || !(buf = gcry_mpi_get_opaque (a, &lengthi))) - { - gpg_err_set_errno (EINVAL); - return NULL; - } - - buf = gcry_mpi_get_opaque (a, &lengthi); - length = (lengthi+7)/8; - /* The first bytes gives the length; check consistency. */ - if (!length || buf[0] != length -1) + + if (!len || buf[0] != len -1) { gpg_err_set_errno (EINVAL); return NULL; } /* Skip length byte. */ - length--; + len--; buf++; /* To calculate the length of the string we can safely assume an upper limit of 3 decimal characters per byte. Two extra bytes - account for the special first octect */ - string = p = xtrymalloc (length*(1+3)+2+1); + account for the special first octet */ + string = p = xtrymalloc (len*(1+3)+2+1); if (!string) return NULL; - if (!length) + if (!len) { *p = 0; return string; @@ -237,7 +225,7 @@ openpgp_oid_to_str (gcry_mpi_t a) p += sprintf (p, "1.%d", buf[n]-40); else { val = buf[n] & 0x7f; - while ( (buf[n]&0x80) && ++n < length ) + while ( (buf[n]&0x80) && ++n < len ) { if ( (val & valmask) ) goto badoid; /* Overflow. */ @@ -250,10 +238,10 @@ openpgp_oid_to_str (gcry_mpi_t a) sprintf (p, "2.%lu", val); p += strlen (p); } - for (n++; n < length; n++) + for (n++; n < len; n++) { val = buf[n] & 0x7f; - while ( (buf[n]&0x80) && ++n < length ) + while ( (buf[n]&0x80) && ++n < len ) { if ( (val & valmask) ) goto badoid; /* Overflow. */ @@ -278,6 +266,35 @@ openpgp_oid_to_str (gcry_mpi_t a) } +/* Return a malloced string representation of the OID in the opaque + * MPI A. In case of an error NULL is returned and ERRNO is set. */ +char * +openpgp_oid_to_str (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int lengthi; + + if (!a + || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE) + || !(buf = gcry_mpi_get_opaque (a, &lengthi))) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + buf = gcry_mpi_get_opaque (a, &lengthi); + return openpgp_oidbuf_to_str (buf, (lengthi+7)/8); +} + + +/* Return true if (BUF,LEN) represents the OID for Ed25519. */ +int +openpgp_oidbuf_is_ed25519 (const void *buf, size_t len) +{ + return (buf && len == DIM (oid_ed25519) + && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); +} + /* Return true if A represents the OID for Ed25519. */ int @@ -285,32 +302,36 @@ openpgp_oid_is_ed25519 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; - size_t n; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); - n = (nbits+7)/8; - return (n == DIM (oid_ed25519) - && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); + return openpgp_oidbuf_is_ed25519 (buf, (nbits+7)/8); } +/* Return true if (BUF,LEN) represents the OID for Curve25519. */ +int +openpgp_oidbuf_is_cv25519 (const void *buf, size_t len) +{ + return (buf && len == DIM (oid_cv25519) + && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))); +} + + +/* Return true if the MPI A represents the OID for Curve25519. */ int openpgp_oid_is_cv25519 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; - size_t n; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); - n = (nbits+7)/8; - return (n == DIM (oid_cv25519) - && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))); + return openpgp_oidbuf_is_cv25519 (buf, (nbits+7)/8); } diff --git a/common/openpgp-s2k.c b/common/openpgp-s2k.c new file mode 100644 index 000000000..2b0ba604b --- /dev/null +++ b/common/openpgp-s2k.c @@ -0,0 +1,67 @@ +/* openpgp-s2ks.c - OpenPGP S2K helper functions + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005, 2006 Free Software Foundation, Inc. + * Copyright (C) 2010, 2019 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> + +#include "util.h" +#include "openpgpdefs.h" + + +/* Pack an s2k iteration count into the form specified in RFC-48800. + * If we're in between valid values, round up. */ +unsigned char +encode_s2k_iterations (int iterations) +{ + unsigned char c=0; + unsigned char result; + unsigned int count; + + if (iterations <= 1024) + return 0; /* Command line arg compatibility. */ + + if (iterations >= 65011712) + return 255; + + /* Need count to be in the range 16-31 */ + for (count=iterations>>6; count>=32; count>>=1) + c++; + + result = (c<<4)|(count-16); + + if (S2K_DECODE_COUNT(result) < iterations) + result++; + + return result; +} diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h index 8699a178d..2f7ff456e 100644 --- a/common/openpgpdefs.h +++ b/common/openpgpdefs.h @@ -196,5 +196,19 @@ typedef enum } compress_algo_t; +/* Limits to be used for static arrays. */ +#define OPENPGP_MAX_NPKEY 5 /* Maximum number of public key parameters. */ +#define OPENPGP_MAX_NSKEY 7 /* Maximum number of secret key parameters. */ +#define OPENPGP_MAX_NSIG 2 /* Maximum number of signature parameters. */ +#define OPENPGP_MAX_NENC 2 /* Maximum number of encryption parameters. */ + + +/* Decode an rfc4880 encoded S2K count. */ +#define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) + + +/*--openpgp-s2k.c --*/ +unsigned char encode_s2k_iterations (int iterations); + #endif /*GNUPG_COMMON_OPENPGPDEFS_H*/ diff --git a/common/percent.c b/common/percent.c index eeb026fbe..ecc6a1959 100644 --- a/common/percent.c +++ b/common/percent.c @@ -37,16 +37,16 @@ /* Create a newly alloced string from STRING with all spaces and - control characters converted to plus signs or %xx sequences. The - function returns the new string or NULL in case of a malloc - failure. - - Note that we also escape the quote character to work around a bug - in the mingw32 runtime which does not correcty handle command line - quoting. We correctly double the quote mark when calling a program - (i.e. gpg-protect-tool), but the pre-main code does not notice the - double quote as an escaped quote. We do this also on POSIX systems - for consistency. */ + * control characters converted to plus signs or %xx sequences. The + * function returns the new string or NULL in case of a malloc + * failure. + * + * Note that this fucntion also escapes the quote character to work + * around a bug in the mingw32 runtime which does not correctly handle + * command line quoting. We correctly double the quote mark when + * calling a program (i.e. gpg-protect-tool), but the pre-main code + * does not notice the double quote as an escaped quote. We do this + * also on POSIX systems for consistency. */ char * percent_plus_escape (const char *string) { @@ -87,19 +87,36 @@ percent_plus_escape (const char *string) } -/* Create a newly alloced string from (DATA,DATALEN) with embedded - * Nuls quoted as %00. The standard percent unescaping can be - * used to reverse this encoding. */ +/* Create a newly malloced string from (DATA,DATALEN) with embedded + * nuls quoted as %00. The standard percent unescaping can be used to + * reverse this encoding. With PLUS_ESCAPE set plus-escaping (spaces + * are replaced by a '+') and escaping of characters with values less + * than 0x20 is used. If PREFIX is not NULL it will be prepended to + * the output in standard escape format; that is PLUS_ESCAPING is + * ignored for PREFIX. */ char * -percent_data_escape (const void *data, size_t datalen) +percent_data_escape (int plus_escape, const char *prefix, + const void *data, size_t datalen) { char *buffer, *p; - const char *s; - size_t n, length; + const unsigned char *s; + size_t n; + size_t length = 1; + + if (prefix) + { + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + length += 3; + else + length++; + } + } - for (length=1, s=data, n=datalen; n; s++, n--) + for (s=data, n=datalen; n; s++, n--) { - if (!*s || *s == '%') + if (!*s || *s == '%' || (plus_escape && (*s < ' ' || *s == '+'))) length += 3; else length++; @@ -109,6 +126,20 @@ percent_data_escape (const void *data, size_t datalen) if (!buffer) return NULL; + if (prefix) + { + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + { + snprintf (p, 4, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + } + for (s=data, n=datalen; n; s++, n--) { if (!*s) @@ -121,13 +152,21 @@ percent_data_escape (const void *data, size_t datalen) memcpy (p, "%25", 3); p += 3; } + else if (plus_escape && *s == ' ') + { + *p++ = '+'; + } + else if (plus_escape && (*s < ' ' || *s == '+')) + { + snprintf (p, 4, "%%%02X", *s); + p += 3; + } else *p++ = *s; } *p = 0; return buffer; - } diff --git a/common/server-help.c b/common/server-help.c index 53a888a86..e5a69e02d 100644 --- a/common/server-help.c +++ b/common/server-help.c @@ -30,8 +30,22 @@ #include <config.h> #include <string.h> -#include "server-help.h" #include "util.h" +#include "server-help.h" + + +static GPGRT_INLINE gpg_error_t +my_error (int e) +{ + return gpg_err_make (default_errsource, (e)); +} + +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + /* Skip over options in LINE. @@ -114,6 +128,40 @@ has_option_name (const char *line, const char *name) } +/* Parse an option with the format "--NAME=VALUE" which must occur in + * LINE before a double-dash. LINE is written to but not modified by + * this function. If the option is found and has a value the value is + * stored as a malloced string at R_VALUE. If the option was not + * found or an error occurred NULL is stored there. Note that + * currently the value must be a string without any space; we may + * eventually update this function to allow for a quoted value. */ +gpg_error_t +get_option_value (char *line, const char *name, char **r_value) +{ + char *p, *pend; + int c; + + *r_value = NULL; + + p = (char*)has_option_name (line, name); + if (!p || p >= skip_options (line)) + return 0; + + if (*p != '=' || !p[1] || spacep (p+1)) + return my_error (GPG_ERR_INV_ARG); + p++; + for (pend = p; *pend && !spacep (pend); pend++) + ; + c = *pend; + *pend = 0; + *r_value = xtrystrdup (p); + *pend = c; + if (!p) + return my_error_from_syserror (); + return 0; +} + + /* Return a pointer to the argument of the option with NAME. If such an option is not given, NULL is returned. */ char * diff --git a/common/server-help.h b/common/server-help.h index 9e3d7ada1..9d2f4cfba 100644 --- a/common/server-help.h +++ b/common/server-help.h @@ -55,6 +55,14 @@ int has_leading_option (const char *line, const char *name); or a space. */ const char *has_option_name (const char *line, const char *name); +/* Same as has_option_name but ignores all options after a "--" and + * does not return a const char ptr. */ +char *has_leading_option_name (char *line, const char *name); + +/* Parse an option with the format "--NAME=VALUE" and return the value + * as a malloced string. */ +gpg_error_t get_option_value (char *line, const char *name, char **r_value); + /* Return a pointer to the argument of the option with NAME. If such an option is not given, NULL is returned. */ char *option_value (const char *line, const char *name); diff --git a/common/sexp-parse.h b/common/sexp-parse.h index 4f77f1430..0403d65f5 100644 --- a/common/sexp-parse.h +++ b/common/sexp-parse.h @@ -105,7 +105,7 @@ smatch (unsigned char const **buf, size_t buflen, const char *token) } /* Format VALUE for use as the length indicatior of an S-expression. - The caller needs to provide a buffer HELP_BUFFER wth a length of + The caller needs to provide a buffer HELP_BUFFER with a length of HELP_BUFLEN. The return value is a pointer into HELP_BUFFER with the formatted length string. The colon and a trailing nul are appended. HELP_BUFLEN must be at least 3 - a more useful value is diff --git a/common/sexputil.c b/common/sexputil.c index f30790aa1..d3020e169 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -303,7 +303,7 @@ make_simple_sexp_from_hexstr (const char *line, size_t *nscanned) for (; n > 1; n -=2, s += 2) *p++ = xtoi_2 (s); *p++ = ')'; - *p = 0; /* (Not really neaded.) */ + *p = 0; /* (Not really needed.) */ return buf; } @@ -577,3 +577,61 @@ get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen) gcry_sexp_release (sexp); return algo; } + + +/* Given the public key S_PKEY, return a new buffer with a descriptive + * string for its algorithm. This function may return NULL on memory + * error. */ +char * +pubkey_algo_string (gcry_sexp_t s_pkey) +{ + const char *prefix; + gcry_sexp_t l1; + char *algoname; + int algo; + char *result; + + l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); + if (!l1) + return xtrystrdup ("E_no_key"); + { + gcry_sexp_t l_tmp = gcry_sexp_cadr (l1); + gcry_sexp_release (l1); + l1 = l_tmp; + } + algoname = gcry_sexp_nth_string (l1, 0); + gcry_sexp_release (l1); + if (!algoname) + return xtrystrdup ("E_no_algo"); + + algo = gcry_pk_map_name (algoname); + switch (algo) + { + case GCRY_PK_RSA: prefix = "rsa"; break; + case GCRY_PK_ELG: prefix = "elg"; break; + case GCRY_PK_DSA: prefix = "dsa"; break; + case GCRY_PK_ECC: prefix = ""; break; + default: prefix = NULL; break; + } + + if (prefix && *prefix) + result = xtryasprintf ("%s%u", prefix, gcry_pk_get_nbits (s_pkey)); + else if (prefix) + { + const char *curve = gcry_pk_get_curve (s_pkey, 0, NULL); + const char *name = openpgp_oid_to_curve + (openpgp_curve_to_oid (curve, NULL), 0); + + if (name) + result = xtrystrdup (name); + else if (curve) + result = xtryasprintf ("X_%s", curve); + else + result = xtrystrdup ("E_unknown"); + } + else + result = xtryasprintf ("X_algo_%d", algo); + + xfree (algoname); + return result; +} diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c index e7f4af341..7688c846d 100644 --- a/common/simple-pwquery.c +++ b/common/simple-pwquery.c @@ -75,7 +75,7 @@ /* Name of the socket to be used. This is a kludge to keep on using - the existsing code despite that we only support a standard socket. */ + the existing code despite that we only support a standard socket. */ static char *default_gpg_agent_info; @@ -246,6 +246,7 @@ agent_open (assuan_context_t *ctx) #ifdef SPWQ_USE_LOGGING log_error (_("no gpg-agent running in this session\n")); #endif + *ctx = NULL; return SPWQ_NO_AGENT; } diff --git a/common/ssh-utils.c b/common/ssh-utils.c index 38d6e8aa2..013b28e5b 100644 --- a/common/ssh-utils.c +++ b/common/ssh-utils.c @@ -247,7 +247,7 @@ get_fingerprint (gcry_sexp_t key, int algo, goto leave; } - strncpy (*r_fpr, algo_name, strlen (algo_name)); + memcpy (*r_fpr, algo_name, strlen (algo_name)); fpr = (char *) *r_fpr + strlen (algo_name); *fpr++ = ':'; diff --git a/common/status.c b/common/status.c index 50afce496..269ffea5d 100644 --- a/common/status.c +++ b/common/status.c @@ -34,6 +34,10 @@ #include "status.h" #include "status-codes.h" +/* The stream to output the status information. Output is disabled if + * this is NULL. */ +static estream_t statusfp; + /* Return the status string for code NO. */ const char * @@ -47,6 +51,60 @@ get_status_string ( int no ) } +/* Set a global status FD. */ +void +gnupg_set_status_fd (int fd) +{ + static int last_fd = -1; + + if (fd != -1 && last_fd == fd) + return; + + if (statusfp && statusfp != es_stdout && statusfp != es_stderr) + es_fclose (statusfp); + statusfp = NULL; + if (fd == -1) + return; + + if (fd == 1) + statusfp = es_stdout; + else if (fd == 2) + statusfp = es_stderr; + else + statusfp = es_fdopen (fd, "w"); + if (!statusfp) + { + log_fatal ("can't open fd %d for status output: %s\n", + fd, gpg_strerror (gpg_error_from_syserror ())); + } + last_fd = fd; +} + + +/* Write a status line with code NO followed by the output of the + * printf style FORMAT. The caller needs to make sure that LFs and + * CRs are not printed. */ +void +gnupg_status_printf (int no, const char *format, ...) +{ + va_list arg_ptr; + + if (!statusfp) + return; /* Not enabled. */ + + es_fputs ("[GNUPG:] ", statusfp); + es_fputs (get_status_string (no), statusfp); + if (format) + { + es_putc (' ', statusfp); + va_start (arg_ptr, format); + es_vfprintf (statusfp, format, arg_ptr); + va_end (arg_ptr); + } + es_putc ('\n', statusfp); +} + + const char * get_inv_recpsgnr_code (gpg_error_t err) { diff --git a/common/status.h b/common/status.h index dc62f3629..aeab54202 100644 --- a/common/status.h +++ b/common/status.h @@ -163,6 +163,10 @@ enum const char *get_status_string (int code); +void gnupg_set_status_fd (int fd); +void gnupg_status_printf (int no, const char *format, + ...) GPGRT_ATTR_PRINTF(2,3); + const char *get_inv_recpsgnr_code (gpg_error_t err); diff --git a/common/stringhelp.c b/common/stringhelp.c index 0abac8ae5..dd1711684 100644 --- a/common/stringhelp.c +++ b/common/stringhelp.c @@ -810,6 +810,19 @@ ascii_strlwr (char *s) return s; } +/* Upcase all ASCII characters in S. */ +char * +ascii_strupr (char *s) +{ + char *p = s; + + for (p=s; *p; p++ ) + if (isascii (*p) && *p >= 'a' && *p <= 'z') + *p &= ~0x20; + + return s; +} + int ascii_strcasecmp( const char *a, const char *b ) { @@ -1400,7 +1413,7 @@ parse_version_number (const char *s, int *number) /* This function breaks up the complete string-representation of the - version number S, which is of the following struture: <major + version number S, which is of the following structure: <major number>.<minor number>[.<micro number>]<patch level>. The major, minor, and micro number components will be stored in *MAJOR, *MINOR and *MICRO. If MICRO is not given 0 is used instead. diff --git a/common/stringhelp.h b/common/stringhelp.h index 5b07af95e..7df6c7656 100644 --- a/common/stringhelp.h +++ b/common/stringhelp.h @@ -76,6 +76,7 @@ int ascii_islower (int c); int ascii_toupper (int c); int ascii_tolower (int c); char *ascii_strlwr (char *s); +char *ascii_strupr (char *s); int ascii_strcasecmp( const char *a, const char *b ); int ascii_strncasecmp (const char *a, const char *b, size_t n); int ascii_memcasecmp( const void *a, const void *b, size_t n ); diff --git a/common/sysutils.c b/common/sysutils.c index 55a7ee9ec..0a3dc2eaf 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -551,14 +551,13 @@ gnupg_tmpfile (void) void gnupg_reopen_std (const char *pgmname) { -#if defined(HAVE_STAT) && !defined(HAVE_W32_SYSTEM) - struct stat statbuf; +#ifdef F_GETFD int did_stdin = 0; int did_stdout = 0; int did_stderr = 0; FILE *complain; - if (fstat (STDIN_FILENO, &statbuf) == -1 && errno ==EBADF) + if (fcntl (STDIN_FILENO, F_GETFD) == -1 && errno ==EBADF) { if (open ("/dev/null",O_RDONLY) == STDIN_FILENO) did_stdin = 1; @@ -566,7 +565,7 @@ gnupg_reopen_std (const char *pgmname) did_stdin = 2; } - if (fstat (STDOUT_FILENO, &statbuf) == -1 && errno == EBADF) + if (fcntl (STDOUT_FILENO, F_GETFD) == -1 && errno == EBADF) { if (open ("/dev/null",O_WRONLY) == STDOUT_FILENO) did_stdout = 1; @@ -574,7 +573,7 @@ gnupg_reopen_std (const char *pgmname) did_stdout = 2; } - if (fstat (STDERR_FILENO, &statbuf)==-1 && errno==EBADF) + if (fcntl (STDERR_FILENO, F_GETFD)==-1 && errno==EBADF) { if (open ("/dev/null", O_WRONLY) == STDERR_FILENO) did_stderr = 1; @@ -607,7 +606,7 @@ gnupg_reopen_std (const char *pgmname) if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) exit (3); -#else /* !(HAVE_STAT && !HAVE_W32_SYSTEM) */ +#else /* !F_GETFD */ (void)pgmname; #endif } diff --git a/common/t-exechelp.c b/common/t-exechelp.c index cf967fcc7..3bf082bbb 100644 --- a/common/t-exechelp.c +++ b/common/t-exechelp.c @@ -131,7 +131,7 @@ test_close_all_fds (void) free (array); /* Now let's check the realloc we use. We do this and the next - tests only if we are allowed to open enought descriptors. */ + tests only if we are allowed to open enough descriptors. */ if (get_max_fds () > 32) { int except[] = { 20, 23, 24, -1 }; diff --git a/common/t-mbox-util.c b/common/t-mbox-util.c index fb1ac12e0..ae717f96f 100644 --- a/common/t-mbox-util.c +++ b/common/t-mbox-util.c @@ -25,6 +25,9 @@ #include "util.h" #include "mbox-util.h" +#define PGM "t-mbox-util" + + #define pass() do { ; } while(0) #define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ __FILE__,__LINE__, (a)); \ @@ -32,6 +35,10 @@ } while(0) +static int verbose; +static int debug; + + static void run_mbox_test (void) { @@ -76,7 +83,86 @@ run_mbox_test (void) for (idx=0; testtbl[idx].userid; idx++) { - char *mbox = mailbox_from_userid (testtbl[idx].userid); + char *mbox = mailbox_from_userid (testtbl[idx].userid, 0); + + if (!testtbl[idx].mbox) + { + if (mbox) + fail (idx); + } + else if (!mbox) + fail (idx); + else if (strcmp (mbox, testtbl[idx].mbox)) + fail (idx); + + xfree (mbox); + } +} + + +static void +run_mbox_no_sub_test (void) +{ + static struct + { + const char *userid; + const char *mbox; + } testtbl[] = + { + { "[email protected]", "[email protected]" }, + { "Werner Koch <[email protected]>", "[email protected]" }, + { "<[email protected]>", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected] ", NULL }, + { " [email protected]", NULL }, + { "Werner Koch (test) <[email protected]>", "[email protected]" }, + { "Werner Koch <[email protected]> (test)", "[email protected]" }, + { "Werner Koch <[email protected] (test)", NULL }, + { "Werner Koch <[email protected] >", NULL }, + { "Werner Koch <[email protected]", NULL }, + { "", NULL }, + { "@", NULL }, + { "bar <>", NULL }, + { "<[email protected]>", "[email protected]" }, + { "<[email protected]>", "[email protected]" }, + { "<[email protected]>", "[email protected]" }, + { "<[email protected]>", "[email protected]" }, + { "<[email protected]>", "[email protected]" }, + { "<[email protected].>", NULL }, + { "<[email protected]>", NULL }, + { "<foo@.>", NULL }, + { "<@example.org>", NULL }, + { "<foo@@example.org>", NULL }, + { "<@[email protected]>", NULL }, + { "<[email protected]> ()", "[email protected]" }, + { "<fo()[email protected]> ()", "fo()[email protected]" }, + { "<fo()[email protected]> ()", "fo()[email protected]" }, + { "fo()[email protected]", NULL}, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + { "[email protected]", "[email protected]" }, + + { NULL, NULL } + }; + int idx; + + for (idx=0; testtbl[idx].userid; idx++) + { + char *mbox = mailbox_from_userid (testtbl[idx].userid, 1); if (!testtbl[idx].mbox) { @@ -143,14 +229,105 @@ run_dns_test (void) } +static void +run_filter (int no_sub) +{ + char buf[4096]; + int c; + char *p, *mbox; + unsigned int count1 = 0; + unsigned int count2 = 0; + + while (fgets (buf, sizeof buf, stdin)) + { + p = strchr (buf, '\n'); + if (p) + *p = 0; + else + { + /* Skip to the end of the line. */ + while ((c = getc (stdin)) != EOF && c != '\n') + ; + } + count1++; + trim_spaces (buf); + mbox = mailbox_from_userid (buf, no_sub); + if (mbox) + { + printf ("%s\n", mbox); + xfree (mbox); + count2++; + } + } + if (verbose) + fprintf (stderr, PGM ": lines=%u mboxes=%u\n", count1, count2); +} + + int main (int argc, char **argv) { - (void)argc; - (void)argv; + int last_argc = -1; + int opt_filter = 0; + int opt_no_sub = 0; - run_mbox_test (); - run_dns_test (); + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [FILE]\n" + "Options:\n" + " --verbose Print timings etc.\n" + " --debug Flyswatter\n" + " --filter Filter mboxes from input lines\n" + " --no-sub Ignore '+'-sub-addresses\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--filter")) + { + opt_filter = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--no-sub")) + { + opt_no_sub = 1; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + if (opt_filter) + run_filter (opt_no_sub); + else + { + run_mbox_test (); + run_mbox_no_sub_test (); + run_dns_test (); + } return 0; } diff --git a/common/t-openpgp-oid.c b/common/t-openpgp-oid.c index cb5709d98..fd9de5dde 100644 --- a/common/t-openpgp-oid.c +++ b/common/t-openpgp-oid.c @@ -142,7 +142,15 @@ test_openpgp_oid_to_str (void) fail (idx, 0); xfree (string); gcry_mpi_release (a); - } + + /* Again using the buffer variant. */ + string = openpgp_oidbuf_to_str (samples[idx].der, samples[idx].der[0]+1); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, samples[idx].string)) + fail (idx, 0); + xfree (string); +} } diff --git a/common/t-percent.c b/common/t-percent.c index 94ece9249..774fa80ee 100644 --- a/common/t-percent.c +++ b/common/t-percent.c @@ -103,25 +103,182 @@ static void test_percent_data_escape (void) { static struct { + const char *prefix; const char *data; size_t datalen; const char *expect; } tbl[] = { { + NULL, "", 0, "" }, { + NULL, "a", 1, "a", }, { + NULL, "%22", 3, "%2522" }, { + NULL, "%%", 3, "%25%25%00" }, { + NULL, "\n \0BC\t", 6, "\n %00BC\t" + }, { + "", + "", 0, + "" + }, { + "", + "a", 1, + "a", + }, { + "", + "%22", 3, + "%2522" + }, { + "", + "%%", 3, + "%25%25%00" + }, { + "", + "\n \0BC\t", 6, + "\n %00BC\t" + }, { + "a", + "", 0, + "a" + }, { + "a", + "a", 1, + "aa", + }, { + "a", + "%22", 3, + "a%2522" + }, { + "a", + "%%", 3, + "a%25%25%00" + }, { + "a", + "\n \0BC\t", 6, + "a\n %00BC\t" + }, { + " ", + "%%", 3, + " %25%25%00" + }, { + "+", + "%%", 3, + "+%25%25%00" + }, { + "%", + "%%", 3, + "%25%25%25%00" + }, { + "a b", + "%%", 3, + "a b%25%25%00" + }, { + "a%2Bb", + "%%", 3, + "a%252Bb%25%25%00" + }, { + "\n", + "%%", 3, + "%0A%25%25%00" + }, { + NULL, + NULL, 0, + NULL } + }; + char *buf; + int i; + size_t len, prefixlen; + + for (i=0; tbl[i].data; i++) + { + buf = percent_data_escape (0, tbl[i].prefix, tbl[i].data, tbl[i].datalen); + if (!buf) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf, tbl[i].expect)) + { + fail (i); + } + len = percent_plus_unescape_inplace (buf, 0); + prefixlen = tbl[i].prefix? strlen (tbl[i].prefix) : 0; + if (len != tbl[i].datalen + prefixlen) + fail (i); + else if (tbl[i].prefix && memcmp (buf, tbl[i].prefix, prefixlen) + && !(prefixlen == 1 && *tbl[i].prefix == '+' && *buf == ' ')) + { + /* Note extra condition above handles the one test case + * which reverts a plus to a space due to the use of the + * plus-unescape fucntion also for the prefix part. */ + fail (i); + } + else if (memcmp (buf+prefixlen, tbl[i].data, tbl[i].datalen)) + { + fail (i); + } + xfree (buf); + } +} + + + +static void +test_percent_data_escape_plus (void) +{ + static struct { + const char *data; + size_t datalen; + const char *expect; + } tbl[] = { + { + "", 0, + "" + }, { + "a", 1, + "a", + }, { + "%22", 3, + "%2522" + }, { + "%%", 3, + "%25%25%00" + }, { + "\n \0BC\t", 6, + "%0A+%00BC%09" + }, { + " ", 1, + "+" + }, { + " ", 2, + "++" + }, { + "+ +", 3, + "%2B+%2B" + }, { + "\" \"", 3, /* Note: This function does not escape quotes. */ + "\"+\"" + }, { + "%22", 3, + "%2522" + }, { + "%% ", 3, + "%25%25+" + }, { + "\n ABC\t", 6, + "%0A+ABC%09" }, { NULL, 0, NULL } }; char *buf; @@ -130,14 +287,16 @@ test_percent_data_escape (void) for (i=0; tbl[i].data; i++) { - buf = percent_data_escape (tbl[i].data, tbl[i].datalen); + buf = percent_data_escape (1, NULL, tbl[i].data, tbl[i].datalen); if (!buf) { fprintf (stderr, "out of core: %s\n", strerror (errno)); exit (2); } if (strcmp (buf, tbl[i].expect)) - fail (i); + { + fail (i); + } len = percent_plus_unescape_inplace (buf, 0); if (len != tbl[i].datalen) fail (i); @@ -148,16 +307,15 @@ test_percent_data_escape (void) } - int main (int argc, char **argv) { (void)argc; (void)argv; - /* FIXME: We escape_unescape is not tested - only - percent_plus_unescape. */ + /* FIXME: escape_unescape is not tested - only percent_plus_unescape. */ test_percent_plus_escape (); test_percent_data_escape (); + test_percent_data_escape_plus (); return 0; } diff --git a/common/ttyio.c b/common/ttyio.c index c7c9d85ab..4c095bc03 100644 --- a/common/ttyio.c +++ b/common/ttyio.c @@ -404,166 +404,192 @@ tty_print_utf8_string( const byte *p, size_t n ) static char * do_get( const char *prompt, int hidden ) { - char *buf; + char *buf; #ifndef __riscos__ - byte cbuf[1]; + byte cbuf[1]; #endif - int c, n, i; + int c, n, i; - if( batchmode ) { - log_error("Sorry, we are in batchmode - can't get input\n"); - exit(2); + if (batchmode) + { + log_error ("Sorry, we are in batchmode - can't get input\n"); + exit (2); } - if (no_terminal) { - log_error("Sorry, no terminal at all requested - can't get input\n"); - exit(2); + if (no_terminal) + { + log_error ("Sorry, no terminal at all requested - can't get input\n"); + exit (2); } - if( !initialized ) - init_ttyfp(); + if (!initialized) + init_ttyfp (); - last_prompt_len = 0; - tty_printf( "%s", prompt ); - buf = xmalloc((n=50)); - i = 0; + last_prompt_len = 0; + tty_printf ("%s", prompt); + buf = xmalloc ((n=50)); + i = 0; #ifdef USE_W32_CONSOLE - if( hidden ) - SetConsoleMode(con.in, HID_INPMODE ); - - for(;;) { - DWORD nread; + if (hidden) + SetConsoleMode(con.in, HID_INPMODE ); - if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) ) - log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() ); - if( !nread ) - continue; - if( *cbuf == '\n' ) - break; - - if( !hidden ) - last_prompt_len++; - c = *cbuf; - if( c == '\t' ) - c = ' '; - else if( c > 0xa0 ) - ; /* we don't allow 0xa0, as this is a protected blank which may - * confuse the user */ - else if( iscntrl(c) ) - continue; - if( !(i < n-1) ) { - n += 50; - buf = xrealloc (buf, n); - } - buf[i++] = c; + for (;;) + { + DWORD nread; + + if (!ReadConsoleA( con.in, cbuf, 1, &nread, NULL)) + log_fatal ("ReadConsole failed: rc=%d", (int)GetLastError ()); + if (!nread) + continue; + if (*cbuf == '\n') + break; + + if (!hidden) + last_prompt_len++; + c = *cbuf; + if (c == '\t') + c = ' '; + else if ( (c >= 0 && c <= 0x1f) || c == 0x7f) + continue; + if (!(i < n-1)) + { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; } - if( hidden ) - SetConsoleMode(con.in, DEF_INPMODE ); + if (hidden) + SetConsoleMode(con.in, DEF_INPMODE ); #elif defined(__riscos__) || defined(HAVE_W32CE_SYSTEM) - do { + do + { #ifdef HAVE_W32CE_SYSTEM /* Using getchar is not a correct solution but for now it doesn't matter because we have no real console at all. We should rework this as soon as we have switched this entire module to estream. */ - c = getchar(); + c = getchar(); #else - c = riscos_getchar(); + c = riscos_getchar(); #endif - if (c == 0xa || c == 0xd) { /* Return || Enter */ - c = (int) '\n'; - } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */ - if (i>0) { - i--; - if (!hidden) { - last_prompt_len--; - fputc(8, ttyfp); - fputc(32, ttyfp); - fputc(8, ttyfp); - fflush(ttyfp); + if (c == 0xa || c == 0xd) /* Return || Enter */ + { + c = (int) '\n'; + } + else if (c == 0x8 || c == 0x7f) /* Backspace || Delete */ + { + if (i>0) + { + i--; + if (!hidden) + { + last_prompt_len--; + fputc(8, ttyfp); + fputc(32, ttyfp); + fputc(8, ttyfp); + fflush(ttyfp); } - } else { - fputc(7, ttyfp); - fflush(ttyfp); } - continue; - } else if (c == (int) '\t') { /* Tab */ - c = ' '; - } else if (c > 0xa0) { - ; /* we don't allow 0xa0, as this is a protected blank which may - * confuse the user */ - } else if (iscntrl(c)) { - continue; + else + { + fputc(7, ttyfp); + fflush(ttyfp); + } + continue; + } + else if (c == (int) '\t') /* Tab */ + { + c = ' '; + } + else if (c > 0xa0) + { + ; /* we don't allow 0xa0, as this is a protected blank which may + * confuse the user */ + } + else if (iscntrl(c)) + { + continue; } - if(!(i < n-1)) { - n += 50; - buf = xrealloc (buf, n); + if (!(i < n-1)) + { + n += 50; + buf = xrealloc (buf, n); } - buf[i++] = c; - if (!hidden) { - last_prompt_len++; - fputc(c, ttyfp); - fflush(ttyfp); + buf[i++] = c; + if (!hidden) + { + last_prompt_len++; + fputc(c, ttyfp); + fflush(ttyfp); } - } while (c != '\n'); - i = (i>0) ? i-1 : 0; + } + while (c != '\n'); + i = (i>0) ? i-1 : 0; + #else /* Other systems. */ - if( hidden ) { + + if (hidden) + { #ifdef HAVE_TCGETATTR - struct termios term; - - if( tcgetattr(fileno(ttyfp), &termsave) ) - log_fatal("tcgetattr() failed: %s\n", strerror(errno) ); - restore_termios = 1; - term = termsave; - term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); - if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) ) - log_fatal("tcsetattr() failed: %s\n", strerror(errno) ); + struct termios term; + + if (tcgetattr(fileno(ttyfp), &termsave)) + log_fatal("tcgetattr() failed: %s\n", strerror(errno)); + restore_termios = 1; + term = termsave; + term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + if (tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) ) + log_fatal("tcsetattr() failed: %s\n", strerror(errno)); #endif } - /* fixme: How can we avoid that the \n is echoed w/o disabling - * canonical mode - w/o this kill_prompt can't work */ - while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) { - if( !hidden ) - last_prompt_len++; - c = *cbuf; - if( c == CONTROL_D ) - log_info("control d found\n"); - if( c == '\t' ) - c = ' '; - else if( c > 0xa0 ) - ; /* we don't allow 0xa0, as this is a protected blank which may - * confuse the user */ - else if( iscntrl(c) ) - continue; - if( !(i < n-1) ) { - n += 50; - buf = xrealloc (buf, n ); - } - buf[i++] = c; + /* fixme: How can we avoid that the \n is echoed w/o disabling + * canonical mode - w/o this kill_prompt can't work */ + while (read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n') + { + if (!hidden) + last_prompt_len++; + c = *cbuf; + if (c == CONTROL_D) + log_info ("Control-D detected\n"); + + if (c == '\t') /* Map tab to a space. */ + c = ' '; + else if ( (c >= 0 && c <= 0x1f) || c == 0x7f) + continue; /* Skip all other ASCII control characters. */ + if (!(i < n-1)) + { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; } - if( *cbuf != '\n' ) { - buf[0] = CONTROL_D; - i = 1; + if (*cbuf != '\n') + { + buf[0] = CONTROL_D; + i = 1; } - if( hidden ) { + if (hidden) + { #ifdef HAVE_TCGETATTR - if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) ) - log_error("tcsetattr() failed: %s\n", strerror(errno) ); - restore_termios = 0; + if (tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave)) + log_error ("tcsetattr() failed: %s\n", strerror(errno)); + restore_termios = 0; #endif } #endif /* end unix version */ - buf[i] = 0; - return buf; + + buf[i] = 0; + return buf; } + +/* Note: This function never returns NULL. */ char * tty_get( const char *prompt ) { diff --git a/common/userids.c b/common/userids.c index 01f2cd84b..181b48866 100644 --- a/common/userids.c +++ b/common/userids.c @@ -226,14 +226,15 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) goto out; } } - if (i != 32 && i != 40) + if (i != 32 && i != 40 && i != 64) { rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */ goto out; } for (i=0,si=s; si < se; i++, si +=2) desc->u.fpr[i] = hextobyte(si); - for (; i < 20; i++) + desc->fprlen = i; + for (; i < 32; i++) desc->u.fpr[i]= 0; mode = KEYDB_SEARCH_MODE_FPR; } @@ -326,14 +327,17 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) } desc->u.fpr[i] = c; } - mode = KEYDB_SEARCH_MODE_FPR16; + desc->fprlen = 16; + for (; i < 32; i++) + desc->u.fpr[i]= 0; + mode = KEYDB_SEARCH_MODE_FPR; } else if ((hexlength == 40 && (s[hexlength] == 0 || (s[hexlength] == '!' && s[hexlength + 1] == 0))) || (!hexprefix && hexlength == 41 && *s == '0')) { - /* SHA1/RMD160 fingerprint. */ + /* SHA1 fingerprint. */ int i; if (hexlength == 41) s++; @@ -347,7 +351,32 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) } desc->u.fpr[i] = c; } - mode = KEYDB_SEARCH_MODE_FPR20; + desc->fprlen = 20; + for (; i < 32; i++) + desc->u.fpr[i]= 0; + mode = KEYDB_SEARCH_MODE_FPR; + } + else if ((hexlength == 64 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 65 && *s == '0')) + { + /* SHA256 fingerprint. */ + int i; + if (hexlength == 65) + s++; + for (i=0; i < 32; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.fpr[i] = c; + } + desc->fprlen = 32; + mode = KEYDB_SEARCH_MODE_FPR; } else if (!hexprefix) { @@ -367,15 +396,21 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) desc->u.fpr[i] = c; } if (i == 20) - mode = KEYDB_SEARCH_MODE_FPR20; + { + desc->fprlen = 20; + mode = KEYDB_SEARCH_MODE_FPR; + } + for (; i < 32; i++) + desc->u.fpr[i]= 0; } if (!mode) { /* Still not found. Now check for a space separated - OpenPGP v4 fingerprint like: - 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 - or - 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + * OpenPGP v4 fingerprint like: + * 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + * or + * 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + * FIXME: Support OpenPGP v5 fingerprint */ hexlength = strspn (s, " 0123456789abcdefABCDEF"); if (s[hexlength] && s[hexlength] != ' ') @@ -409,7 +444,12 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) s += 2; } if (i == 20) - mode = KEYDB_SEARCH_MODE_FPR20; + { + desc->fprlen = 20; + mode = KEYDB_SEARCH_MODE_FPR; + } + for (; i < 32; i++) + desc->u.fpr[i]= 0; } } if (!mode) /* Default to substring search. */ diff --git a/common/util.h b/common/util.h index 682415d92..8895137ec 100644 --- a/common/util.h +++ b/common/util.h @@ -39,7 +39,10 @@ * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ - +#if GPG_ERROR_VERSION_NUMBER < 0x012400 /* 1.36 */ +#define GPG_ERR_NO_AUTH 314 +#define GPG_ERR_BAD_AUTH 315 +#endif /*GPG_ERROR_VERSION_NUMBER*/ /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) @@ -189,6 +192,7 @@ gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, int get_pk_algo_from_key (gcry_sexp_t key); int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen); +char *pubkey_algo_string (gcry_sexp_t s_pkey); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); @@ -201,7 +205,8 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); -char *percent_data_escape (const void *data, size_t datalen); +char *percent_data_escape (int plus, const char *prefix, + const void *data, size_t datalen); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); @@ -210,8 +215,11 @@ size_t percent_unescape_inplace (char *string, int nulrepl); /*-- openpgp-oid.c --*/ gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); +char *openpgp_oidbuf_to_str (const unsigned char *buf, size_t len); char *openpgp_oid_to_str (gcry_mpi_t a); +int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len); int openpgp_oid_is_ed25519 (gcry_mpi_t a); +int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len); int openpgp_oid_is_cv25519 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits); const char *openpgp_oid_to_curve (const char *oid, int canon); @@ -258,6 +266,13 @@ void gnupg_module_name_flush_some (void); void gnupg_set_builddir (const char *newdir); +/* A list of constants to identify protocols. This is used by tools + * which need to distinguish between the different protocols + * implemented by GnuPG. May be used as bit flags. */ +#define GNUPG_PROTOCOL_OPENPGP 1 /* The one and only (gpg). */ +#define GNUPG_PROTOCOL_CMS 2 /* The core of S/MIME (gpgsm) */ +#define GNUPG_PROTOCOL_SSH_AGENT 4 /* Out ssh-agent implementation */ + /*-- gpgrlhelp.c --*/ void gnupg_rl_initialize (void); @@ -299,6 +314,7 @@ void print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved); char *try_make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim); +char *decode_c_string (const char *src); int is_file_compressed (const char *s, int *ret_rc); |