From b841a883a2a66807aa427e65d49067584bedfbe2 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 23 Jun 2016 11:55:46 +0200 Subject: common: Rename private-keys.c to name-value.c * common/private-keys.c: Rename to name-value.c. * common/private-keys.h: Rename to name-value.h. Chage all users. * common/t-private-keys.c: Rename to t-name-value.c. * common/Makefile.am: Adjust accordingly. -- The module is cool enough to be used for other purposes as well. Thus we better change the name. Signed-off-by: Werner Koch --- agent/findkey.c | 2 +- common/Makefile.am | 6 +- common/name-value.c | 755 ++++++++++++++++++++++++++++++++++++++++++++++++ common/name-value.h | 109 +++++++ common/private-keys.c | 750 ----------------------------------------------- common/private-keys.h | 109 ------- common/t-name-value.c | 541 ++++++++++++++++++++++++++++++++++ common/t-private-keys.c | 541 ---------------------------------- 8 files changed, 1409 insertions(+), 1404 deletions(-) create mode 100644 common/name-value.c create mode 100644 common/name-value.h delete mode 100644 common/private-keys.c delete mode 100644 common/private-keys.h create mode 100644 common/t-name-value.c delete mode 100644 common/t-private-keys.c diff --git a/agent/findkey.c b/agent/findkey.c index d3780b930..b2215101a 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -35,7 +35,7 @@ #include "agent.h" #include "i18n.h" #include "../common/ssh-utils.h" -#include "../common/private-keys.h" +#include "../common/name-value.h" #ifndef O_BINARY #define O_BINARY 0 diff --git a/common/Makefile.am b/common/Makefile.am index 884c966a3..24516894b 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -91,7 +91,7 @@ common_sources = \ call-gpg.c call-gpg.h \ exectool.c exectool.h \ server-help.c server-help.h \ - private-keys.c private-keys.h + name-value.c name-value.h if HAVE_W32_SYSTEM common_sources += w32-reg.c w32-afunix.c w32-afunix.h @@ -157,7 +157,7 @@ module_tests = t-stringhelp t-timestuff \ t-convert t-percent t-gettime t-sysutils t-sexputil \ t-session-env t-openpgp-oid t-ssh-utils \ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ - t-private-keys t-ccparray + t-name-value t-ccparray if !HAVE_W32CE_SYSTEM module_tests += t-exechelp endif @@ -206,7 +206,7 @@ t_zb32_LDADD = $(t_common_ldadd) t_mbox_util_LDADD = $(t_common_ldadd) t_iobuf_LDADD = $(t_common_ldadd) t_strlist_LDADD = $(t_common_ldadd) -t_private_keys_LDADD = $(t_common_ldadd) +t_name_value_LDADD = $(t_common_ldadd) t_ccparray_LDADD = $(t_common_ldadd) # System specific test diff --git a/common/name-value.c b/common/name-value.c new file mode 100644 index 000000000..601ddd43b --- /dev/null +++ b/common/name-value.c @@ -0,0 +1,755 @@ +/* name-value.c - Parser and writer for a name-value format. + * Copyright (C) 2016 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. + * + * 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 . + */ + +/* + * This module aso provides features for the extended private key + * format of gpg-agent. + */ + +#include +#include +#include +#include +#include + +#include "mischelp.h" +#include "strlist.h" +#include "util.h" +#include "name-value.h" + +struct private_key_container +{ + struct private_key_entry *first; + struct private_key_entry *last; +}; + + +struct private_key_entry +{ + struct private_key_entry *prev; + struct private_key_entry *next; + + /* The name. Comments and blank lines have NAME set to NULL. */ + char *name; + + /* The value as stored in the file. We store it when when we parse + a file so that we can reproduce it. */ + strlist_t raw_value; + + /* The decoded value. */ + char *value; +}; + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + + + + +/* Allocation and deallocation. */ + +/* Allocate a private key container structure. */ +pkc_t +pkc_new (void) +{ + return xtrycalloc (1, sizeof (struct private_key_container)); +} + + +static void +pke_release (pke_t entry) +{ + if (entry == NULL) + return; + + xfree (entry->name); + if (entry->value) + wipememory (entry->value, strlen (entry->value)); + xfree (entry->value); + free_strlist_wipe (entry->raw_value); + xfree (entry); +} + + +/* Release a private key container structure. */ +void +pkc_release (pkc_t pk) +{ + pke_t e, next; + + if (pk == NULL) + return; + + for (e = pk->first; e; e = next) + { + next = e->next; + pke_release (e); + } + + xfree (pk); +} + + + +/* Dealing with names and values. */ + +/* Check whether the given name is valid. Valid names start with a + letter, end with a colon, and contain only alphanumeric characters + and the hyphen. */ +static int +valid_name (const char *name) +{ + size_t i, len = strlen (name); + + if (! alphap (name) || len == 0 || name[len - 1] != ':') + return 0; + + for (i = 1; i < len - 1; i++) + if (! alnump (&name[i]) && name[i] != '-') + return 0; + + return 1; +} + + +/* Makes sure that ENTRY has a RAW_VALUE. */ +static gpg_error_t +assert_raw_value (pke_t entry) +{ + gpg_error_t err = 0; + size_t len, offset; +#define LINELEN 70 + char buf[LINELEN+3]; + + if (entry->raw_value) + return 0; + + len = strlen (entry->value); + offset = 0; + while (len) + { + size_t amount, linelen = LINELEN; + + /* On the first line we need to subtract space for the name. */ + if (entry->raw_value == NULL && strlen (entry->name) < linelen) + linelen -= strlen (entry->name); + + /* See if the rest of the value fits in this line. */ + if (len <= linelen) + amount = len; + else + { + size_t i; + + /* Find a suitable space to break on. */ + for (i = linelen - 1; linelen - i < 30 && linelen - i > offset; i--) + if (ascii_isspace (entry->value[i])) + break; + + if (ascii_isspace (entry->value[i])) + { + /* Found one. */ + amount = i; + } + else + { + /* Just induce a hard break. */ + amount = linelen; + } + } + + snprintf (buf, sizeof buf, " %.*s\n", (int) amount, + &entry->value[offset]); + if (append_to_strlist_try (&entry->raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + offset += amount; + len -= amount; + } + + leave: + if (err) + { + free_strlist_wipe (entry->raw_value); + entry->raw_value = NULL; + } + + return err; +#undef LINELEN +} + + +/* Computes the length of the value encoded as continuation. If + *SWALLOW_WS is set, all whitespace at the beginning of S is + swallowed. If START is given, a pointer to the beginning of the + value is stored there. */ +static size_t +continuation_length (const char *s, int *swallow_ws, const char **start) +{ + size_t len; + + if (*swallow_ws) + { + /* The previous line was a blank line and we inserted a newline. + Swallow all whitespace at the beginning of this line. */ + while (ascii_isspace (*s)) + s++; + } + else + { + /* Iff a continuation starts with more than one space, it + encodes a space. */ + if (ascii_isspace (*s)) + s++; + } + + /* Strip whitespace at the end. */ + len = strlen (s); + while (len > 0 && ascii_isspace (s[len-1])) + len--; + + if (len == 0) + { + /* Blank lines encode newlines. */ + len = 1; + s = "\n"; + *swallow_ws = 1; + } + else + *swallow_ws = 0; + + if (start) + *start = s; + + return len; +} + + +/* Makes sure that ENTRY has a VALUE. */ +static gpg_error_t +assert_value (pke_t entry) +{ + size_t len; + int swallow_ws; + strlist_t s; + char *p; + + if (entry->value) + return 0; + + len = 0; + swallow_ws = 0; + for (s = entry->raw_value; s; s = s->next) + len += continuation_length (s->d, &swallow_ws, NULL); + + /* Add one for the terminating zero. */ + len += 1; + + entry->value = p = xtrymalloc (len); + if (entry->value == NULL) + return my_error_from_syserror (); + + swallow_ws = 0; + for (s = entry->raw_value; s; s = s->next) + { + const char *start; + size_t l = continuation_length (s->d, &swallow_ws, &start); + + memcpy (p, start, l); + p += l; + } + + *p++ = 0; + assert (p - entry->value == len); + + return 0; +} + + +/* Get the name. */ +char * +pke_name (pke_t pke) +{ + return pke->name; +} + + +/* Get the value. */ +char * +pke_value (pke_t pke) +{ + if (assert_value (pke)) + return NULL; + return pke->value; +} + + + +/* Adding and modifying values. */ + +/* Add (NAME, VALUE, RAW_VALUE) to PK. NAME may be NULL for comments + and blank lines. At least one of VALUE and RAW_VALUE must be + given. If PRESERVE_ORDER is not given, entries with the same name + are grouped. NAME, VALUE and RAW_VALUE is consumed. */ +static gpg_error_t +_pkc_add (pkc_t pk, char *name, char *value, strlist_t raw_value, + int preserve_order) +{ + gpg_error_t err = 0; + pke_t e; + + assert (value || raw_value); + + if (name && ! valid_name (name)) + { + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + if (name && ascii_strcasecmp (name, "Key:") == 0 && pkc_lookup (pk, "Key:")) + { + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + e = xtrycalloc (1, sizeof *e); + if (e == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + e->name = name; + e->value = value; + e->raw_value = raw_value; + + if (pk->first) + { + pke_t last; + + if (preserve_order || name == NULL) + last = pk->last; + else + { + /* See if there is already an entry with NAME. */ + last = pkc_lookup (pk, name); + + /* If so, find the last in that block. */ + if (last) + { + while (last->next) + { + pke_t next = last->next; + + if (next->name && ascii_strcasecmp (next->name, name) == 0) + last = next; + else + break; + } + } + else /* Otherwise, just find the last entry. */ + last = pk->last; + } + + if (last->next) + { + e->prev = last; + e->next = last->next; + last->next = e; + e->next->prev = e; + } + else + { + e->prev = last; + last->next = e; + pk->last = e; + } + } + else + pk->first = pk->last = e; + + leave: + if (err) + { + xfree (name); + if (value) + wipememory (value, strlen (value)); + xfree (value); + free_strlist_wipe (raw_value); + } + + return err; +} + + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is not updated but the new entry is appended. */ +gpg_error_t +pkc_add (pkc_t pk, const char *name, const char *value) +{ + char *k, *v; + + k = xtrystrdup (name); + if (k == NULL) + return my_error_from_syserror (); + + v = xtrystrdup (value); + if (v == NULL) + { + xfree (k); + return my_error_from_syserror (); + } + + return _pkc_add (pk, k, v, NULL, 0); +} + + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is updated with VALUE. If multiple entries with NAME exist, the + first entry is updated. */ +gpg_error_t +pkc_set (pkc_t pk, const char *name, const char *value) +{ + pke_t e; + + if (! valid_name (name)) + return GPG_ERR_INV_NAME; + + e = pkc_lookup (pk, name); + if (e) + { + char *v; + + v = xtrystrdup (value); + if (v == NULL) + return my_error_from_syserror (); + + free_strlist_wipe (e->raw_value); + e->raw_value = NULL; + if (e->value) + wipememory (e->value, strlen (e->value)); + xfree (e->value); + e->value = v; + + return 0; + } + else + return pkc_add (pk, name, value); +} + + +/* Delete the given entry from PK. */ +void +pkc_delete (pkc_t pk, pke_t entry) +{ + if (entry->prev) + entry->prev->next = entry->next; + else + pk->first = entry->next; + + if (entry->next) + entry->next->prev = entry->prev; + else + pk->last = entry->prev; + + pke_release (entry); +} + + + +/* Lookup and iteration. */ + +/* Get the first non-comment entry. */ +pke_t +pkc_first (pkc_t pk) +{ + pke_t entry; + for (entry = pk->first; entry; entry = entry->next) + if (entry->name) + return entry; + return NULL; +} + + +/* Get the first entry with the given name. */ +pke_t +pkc_lookup (pkc_t pk, const char *name) +{ + pke_t entry; + for (entry = pk->first; entry; entry = entry->next) + if (entry->name && ascii_strcasecmp (entry->name, name) == 0) + return entry; + return NULL; +} + + +/* Get the next non-comment entry. */ +pke_t +pke_next (pke_t entry) +{ + for (entry = entry->next; entry; entry = entry->next) + if (entry->name) + return entry; + return NULL; +} + + +/* Get the next entry with the given name. */ +pke_t +pke_next_value (pke_t entry, const char *name) +{ + for (entry = entry->next; entry; entry = entry->next) + if (entry->name && ascii_strcasecmp (entry->name, name) == 0) + return entry; + return NULL; +} + + + +/* Private key handling. */ + +/* Get the private key. */ +gpg_error_t +pkc_get_private_key (pkc_t pk, gcry_sexp_t *retsexp) +{ + gpg_error_t err; + pke_t e; + + e = pkc_lookup (pk, "Key:"); + if (e == NULL) + return gpg_error (GPG_ERR_MISSING_KEY); + + err = assert_value (e); + if (err) + return err; + + return gcry_sexp_sscan (retsexp, NULL, e->value, strlen (e->value)); +} + + +/* Set the private key. */ +gpg_error_t +pkc_set_private_key (pkc_t pk, gcry_sexp_t sexp) +{ + gpg_error_t err; + char *raw, *clean, *p; + size_t len, i; + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + + raw = xtrymalloc (len); + if (raw == NULL) + return my_error_from_syserror (); + + clean = xtrymalloc (len); + if (clean == NULL) + { + xfree (raw); + return my_error_from_syserror (); + } + + gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, raw, len); + + /* Strip any whitespace at the end. */ + i = strlen (raw) - 1; + while (i && ascii_isspace (raw[i])) + { + raw[i] = 0; + i--; + } + + /* Replace any newlines with spaces, remove superfluous whitespace. */ + len = strlen (raw); + for (p = clean, i = 0; i < len; i++) + { + char c = raw[i]; + + /* Collapse contiguous and superfluous spaces. */ + if (ascii_isspace (c) && i > 0 + && (ascii_isspace (raw[i-1]) || raw[i-1] == '(' || raw[i-1] == ')')) + continue; + + if (c == '\n') + c = ' '; + + *p++ = c; + } + *p = 0; + + err = pkc_set (pk, "Key:", clean); + xfree (raw); + xfree (clean); + return err; +} + + + +/* Parsing and serialization. */ + +/* Parse STREAM and return a newly allocated private key container + structure in RESULT. If ERRLINEP is given, the line number the + parser was last considering is stored there. */ +gpg_error_t +pkc_parse (pkc_t *result, int *errlinep, estream_t stream) +{ + gpg_error_t err = 0; + gpgrt_ssize_t len; + char *buf = NULL; + size_t buf_len = 0; + char *name = NULL; + strlist_t raw_value = NULL; + + + *result = pkc_new (); + if (*result == NULL) + return my_error_from_syserror (); + + if (errlinep) + *errlinep = 0; + while ((len = es_read_line (stream, &buf, &buf_len, NULL))) + { + char *p; + if (errlinep) + *errlinep += 1; + + /* Skip any whitespace. */ + for (p = buf; *p && ascii_isspace (*p); p++) + /* Do nothing. */; + + if (name && (spacep (buf) || *p == 0)) + { + /* A continuation. */ + if (append_to_strlist_try (&raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + continue; + } + + /* No continuation. Add the current entry if any. */ + if (raw_value) + { + err = _pkc_add (*result, name, NULL, raw_value, 1); + if (err) + goto leave; + } + + /* And prepare for the next one. */ + name = NULL; + raw_value = NULL; + + if (*p != 0 && *p != '#') + { + char *colon, *value, tmp; + + colon = strchr (buf, ':'); + if (colon == NULL) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + + value = colon + 1; + tmp = *value; + *value = 0; + name = xtrystrdup (p); + *value = tmp; + + if (name == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + if (append_to_strlist_try (&raw_value, value) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + continue; + } + + if (append_to_strlist_try (&raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + } + + /* Add the final entry. */ + if (raw_value) + err = _pkc_add (*result, name, NULL, raw_value, 1); + + leave: + gpgrt_free (buf); + if (err) + { + pkc_release (*result); + *result = NULL; + } + + return err; +} + + +/* Write a representation of PK to STREAM. */ +gpg_error_t +pkc_write (pkc_t pk, estream_t stream) +{ + gpg_error_t err; + pke_t entry; + strlist_t s; + + for (entry = pk->first; entry; entry = entry->next) + { + if (entry->name) + es_fputs (entry->name, stream); + + err = assert_raw_value (entry); + if (err) + return err; + + for (s = entry->raw_value; s; s = s->next) + es_fputs (s->d, stream); + + if (es_ferror (stream)) + return my_error_from_syserror (); + } + + return 0; +} diff --git a/common/name-value.h b/common/name-value.h new file mode 100644 index 000000000..0a8694a57 --- /dev/null +++ b/common/name-value.h @@ -0,0 +1,109 @@ +/* name-value.h - Parser and writer for a name-value format. + * Copyright (C) 2016 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. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef GNUPG_COMMON_NAME_VALUE_H +#define GNUPG_COMMON_NAME_VALUE_H + +struct private_key_container; +typedef struct private_key_container *pkc_t; + +struct private_key_entry; +typedef struct private_key_entry *pke_t; + + + +/* Memory management, and dealing with entries. */ + +/* Allocate a private key container structure. */ +pkc_t pkc_new (void); + +/* Release a private key container structure. */ +void pkc_release (pkc_t pk); + +/* Get the name. */ +char *pke_name (pke_t pke); + +/* Get the value. */ +char *pke_value (pke_t pke); + + + +/* Lookup and iteration. */ + +/* Get the first non-comment entry. */ +pke_t pkc_first (pkc_t pk); + +/* Get the first entry with the given name. */ +pke_t pkc_lookup (pkc_t pk, const char *name); + +/* Get the next non-comment entry. */ +pke_t pke_next (pke_t entry); + +/* Get the next entry with the given name. */ +pke_t pke_next_value (pke_t entry, const char *name); + + + +/* Adding and modifying values. */ + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is not updated but the new entry is appended. */ +gpg_error_t pkc_add (pkc_t pk, const char *name, const char *value); + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is updated with VALUE. If multiple entries with NAME exist, the + first entry is updated. */ +gpg_error_t pkc_set (pkc_t pk, const char *name, const char *value); + +/* Delete the given entry from PK. */ +void pkc_delete (pkc_t pk, pke_t pke); + + + +/* Private key handling. */ + +/* Get the private key. */ +gpg_error_t pkc_get_private_key (pkc_t pk, gcry_sexp_t *retsexp); + +/* Set the private key. */ +gpg_error_t pkc_set_private_key (pkc_t pk, gcry_sexp_t sexp); + + + +/* Parsing and serialization. */ + +/* Parse STREAM and return a newly allocated private key container + structure in RESULT. If ERRLINEP is given, the line number the + parser was last considering is stored there. */ +gpg_error_t pkc_parse (pkc_t *result, int *errlinep, estream_t stream); + +/* Write a representation of PK to STREAM. */ +gpg_error_t pkc_write (pkc_t pk, estream_t stream); + +#endif /* GNUPG_COMMON_NAME_VALUE_H */ diff --git a/common/private-keys.c b/common/private-keys.c deleted file mode 100644 index 4cf7d227c..000000000 --- a/common/private-keys.c +++ /dev/null @@ -1,750 +0,0 @@ -/* private-keys.c - Parser and writer for the extended private key format. - * Copyright (C) 2016 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. - * - * 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 "private-keys.h" -#include "mischelp.h" -#include "strlist.h" -#include "util.h" - -struct private_key_container -{ - struct private_key_entry *first; - struct private_key_entry *last; -}; - - -struct private_key_entry -{ - struct private_key_entry *prev; - struct private_key_entry *next; - - /* The name. Comments and blank lines have NAME set to NULL. */ - char *name; - - /* The value as stored in the file. We store it when when we parse - a file so that we can reproduce it. */ - strlist_t raw_value; - - /* The decoded value. */ - char *value; -}; - - -/* Helper */ -static inline gpg_error_t -my_error_from_syserror (void) -{ - return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); -} - - - - -/* Allocation and deallocation. */ - -/* Allocate a private key container structure. */ -pkc_t -pkc_new (void) -{ - return xtrycalloc (1, sizeof (struct private_key_container)); -} - - -static void -pke_release (pke_t entry) -{ - if (entry == NULL) - return; - - xfree (entry->name); - if (entry->value) - wipememory (entry->value, strlen (entry->value)); - xfree (entry->value); - free_strlist_wipe (entry->raw_value); - xfree (entry); -} - - -/* Release a private key container structure. */ -void -pkc_release (pkc_t pk) -{ - pke_t e, next; - - if (pk == NULL) - return; - - for (e = pk->first; e; e = next) - { - next = e->next; - pke_release (e); - } - - xfree (pk); -} - - - -/* Dealing with names and values. */ - -/* Check whether the given name is valid. Valid names start with a - letter, end with a colon, and contain only alphanumeric characters - and the hyphen. */ -static int -valid_name (const char *name) -{ - size_t i, len = strlen (name); - - if (! alphap (name) || len == 0 || name[len - 1] != ':') - return 0; - - for (i = 1; i < len - 1; i++) - if (! alnump (&name[i]) && name[i] != '-') - return 0; - - return 1; -} - - -/* Makes sure that ENTRY has a RAW_VALUE. */ -static gpg_error_t -assert_raw_value (pke_t entry) -{ - gpg_error_t err = 0; - size_t len, offset; -#define LINELEN 70 - char buf[LINELEN+3]; - - if (entry->raw_value) - return 0; - - len = strlen (entry->value); - offset = 0; - while (len) - { - size_t amount, linelen = LINELEN; - - /* On the first line we need to subtract space for the name. */ - if (entry->raw_value == NULL && strlen (entry->name) < linelen) - linelen -= strlen (entry->name); - - /* See if the rest of the value fits in this line. */ - if (len <= linelen) - amount = len; - else - { - size_t i; - - /* Find a suitable space to break on. */ - for (i = linelen - 1; linelen - i < 30 && linelen - i > offset; i--) - if (ascii_isspace (entry->value[i])) - break; - - if (ascii_isspace (entry->value[i])) - { - /* Found one. */ - amount = i; - } - else - { - /* Just induce a hard break. */ - amount = linelen; - } - } - - snprintf (buf, sizeof buf, " %.*s\n", (int) amount, - &entry->value[offset]); - if (append_to_strlist_try (&entry->raw_value, buf) == NULL) - { - err = my_error_from_syserror (); - goto leave; - } - - offset += amount; - len -= amount; - } - - leave: - if (err) - { - free_strlist_wipe (entry->raw_value); - entry->raw_value = NULL; - } - - return err; -#undef LINELEN -} - - -/* Computes the length of the value encoded as continuation. If - *SWALLOW_WS is set, all whitespace at the beginning of S is - swallowed. If START is given, a pointer to the beginning of the - value is stored there. */ -static size_t -continuation_length (const char *s, int *swallow_ws, const char **start) -{ - size_t len; - - if (*swallow_ws) - { - /* The previous line was a blank line and we inserted a newline. - Swallow all whitespace at the beginning of this line. */ - while (ascii_isspace (*s)) - s++; - } - else - { - /* Iff a continuation starts with more than one space, it - encodes a space. */ - if (ascii_isspace (*s)) - s++; - } - - /* Strip whitespace at the end. */ - len = strlen (s); - while (len > 0 && ascii_isspace (s[len-1])) - len--; - - if (len == 0) - { - /* Blank lines encode newlines. */ - len = 1; - s = "\n"; - *swallow_ws = 1; - } - else - *swallow_ws = 0; - - if (start) - *start = s; - - return len; -} - - -/* Makes sure that ENTRY has a VALUE. */ -static gpg_error_t -assert_value (pke_t entry) -{ - size_t len; - int swallow_ws; - strlist_t s; - char *p; - - if (entry->value) - return 0; - - len = 0; - swallow_ws = 0; - for (s = entry->raw_value; s; s = s->next) - len += continuation_length (s->d, &swallow_ws, NULL); - - /* Add one for the terminating zero. */ - len += 1; - - entry->value = p = xtrymalloc (len); - if (entry->value == NULL) - return my_error_from_syserror (); - - swallow_ws = 0; - for (s = entry->raw_value; s; s = s->next) - { - const char *start; - size_t l = continuation_length (s->d, &swallow_ws, &start); - - memcpy (p, start, l); - p += l; - } - - *p++ = 0; - assert (p - entry->value == len); - - return 0; -} - - -/* Get the name. */ -char * -pke_name (pke_t pke) -{ - return pke->name; -} - - -/* Get the value. */ -char * -pke_value (pke_t pke) -{ - if (assert_value (pke)) - return NULL; - return pke->value; -} - - - -/* Adding and modifying values. */ - -/* Add (NAME, VALUE, RAW_VALUE) to PK. NAME may be NULL for comments - and blank lines. At least one of VALUE and RAW_VALUE must be - given. If PRESERVE_ORDER is not given, entries with the same name - are grouped. NAME, VALUE and RAW_VALUE is consumed. */ -static gpg_error_t -_pkc_add (pkc_t pk, char *name, char *value, strlist_t raw_value, - int preserve_order) -{ - gpg_error_t err = 0; - pke_t e; - - assert (value || raw_value); - - if (name && ! valid_name (name)) - { - err = gpg_error (GPG_ERR_INV_NAME); - goto leave; - } - - if (name && ascii_strcasecmp (name, "Key:") == 0 && pkc_lookup (pk, "Key:")) - { - err = gpg_error (GPG_ERR_INV_NAME); - goto leave; - } - - e = xtrycalloc (1, sizeof *e); - if (e == NULL) - { - err = my_error_from_syserror (); - goto leave; - } - - e->name = name; - e->value = value; - e->raw_value = raw_value; - - if (pk->first) - { - pke_t last; - - if (preserve_order || name == NULL) - last = pk->last; - else - { - /* See if there is already an entry with NAME. */ - last = pkc_lookup (pk, name); - - /* If so, find the last in that block. */ - if (last) - { - while (last->next) - { - pke_t next = last->next; - - if (next->name && ascii_strcasecmp (next->name, name) == 0) - last = next; - else - break; - } - } - else /* Otherwise, just find the last entry. */ - last = pk->last; - } - - if (last->next) - { - e->prev = last; - e->next = last->next; - last->next = e; - e->next->prev = e; - } - else - { - e->prev = last; - last->next = e; - pk->last = e; - } - } - else - pk->first = pk->last = e; - - leave: - if (err) - { - xfree (name); - if (value) - wipememory (value, strlen (value)); - xfree (value); - free_strlist_wipe (raw_value); - } - - return err; -} - - -/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it - is not updated but the new entry is appended. */ -gpg_error_t -pkc_add (pkc_t pk, const char *name, const char *value) -{ - char *k, *v; - - k = xtrystrdup (name); - if (k == NULL) - return my_error_from_syserror (); - - v = xtrystrdup (value); - if (v == NULL) - { - xfree (k); - return my_error_from_syserror (); - } - - return _pkc_add (pk, k, v, NULL, 0); -} - - -/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it - is updated with VALUE. If multiple entries with NAME exist, the - first entry is updated. */ -gpg_error_t -pkc_set (pkc_t pk, const char *name, const char *value) -{ - pke_t e; - - if (! valid_name (name)) - return GPG_ERR_INV_NAME; - - e = pkc_lookup (pk, name); - if (e) - { - char *v; - - v = xtrystrdup (value); - if (v == NULL) - return my_error_from_syserror (); - - free_strlist_wipe (e->raw_value); - e->raw_value = NULL; - if (e->value) - wipememory (e->value, strlen (e->value)); - xfree (e->value); - e->value = v; - - return 0; - } - else - return pkc_add (pk, name, value); -} - - -/* Delete the given entry from PK. */ -void -pkc_delete (pkc_t pk, pke_t entry) -{ - if (entry->prev) - entry->prev->next = entry->next; - else - pk->first = entry->next; - - if (entry->next) - entry->next->prev = entry->prev; - else - pk->last = entry->prev; - - pke_release (entry); -} - - - -/* Lookup and iteration. */ - -/* Get the first non-comment entry. */ -pke_t -pkc_first (pkc_t pk) -{ - pke_t entry; - for (entry = pk->first; entry; entry = entry->next) - if (entry->name) - return entry; - return NULL; -} - - -/* Get the first entry with the given name. */ -pke_t -pkc_lookup (pkc_t pk, const char *name) -{ - pke_t entry; - for (entry = pk->first; entry; entry = entry->next) - if (entry->name && ascii_strcasecmp (entry->name, name) == 0) - return entry; - return NULL; -} - - -/* Get the next non-comment entry. */ -pke_t -pke_next (pke_t entry) -{ - for (entry = entry->next; entry; entry = entry->next) - if (entry->name) - return entry; - return NULL; -} - - -/* Get the next entry with the given name. */ -pke_t -pke_next_value (pke_t entry, const char *name) -{ - for (entry = entry->next; entry; entry = entry->next) - if (entry->name && ascii_strcasecmp (entry->name, name) == 0) - return entry; - return NULL; -} - - - -/* Private key handling. */ - -/* Get the private key. */ -gpg_error_t -pkc_get_private_key (pkc_t pk, gcry_sexp_t *retsexp) -{ - gpg_error_t err; - pke_t e; - - e = pkc_lookup (pk, "Key:"); - if (e == NULL) - return gpg_error (GPG_ERR_MISSING_KEY); - - err = assert_value (e); - if (err) - return err; - - return gcry_sexp_sscan (retsexp, NULL, e->value, strlen (e->value)); -} - - -/* Set the private key. */ -gpg_error_t -pkc_set_private_key (pkc_t pk, gcry_sexp_t sexp) -{ - gpg_error_t err; - char *raw, *clean, *p; - size_t len, i; - - len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); - - raw = xtrymalloc (len); - if (raw == NULL) - return my_error_from_syserror (); - - clean = xtrymalloc (len); - if (clean == NULL) - { - xfree (raw); - return my_error_from_syserror (); - } - - gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, raw, len); - - /* Strip any whitespace at the end. */ - i = strlen (raw) - 1; - while (i && ascii_isspace (raw[i])) - { - raw[i] = 0; - i--; - } - - /* Replace any newlines with spaces, remove superfluous whitespace. */ - len = strlen (raw); - for (p = clean, i = 0; i < len; i++) - { - char c = raw[i]; - - /* Collapse contiguous and superfluous spaces. */ - if (ascii_isspace (c) && i > 0 - && (ascii_isspace (raw[i-1]) || raw[i-1] == '(' || raw[i-1] == ')')) - continue; - - if (c == '\n') - c = ' '; - - *p++ = c; - } - *p = 0; - - err = pkc_set (pk, "Key:", clean); - xfree (raw); - xfree (clean); - return err; -} - - - -/* Parsing and serialization. */ - -/* Parse STREAM and return a newly allocated private key container - structure in RESULT. If ERRLINEP is given, the line number the - parser was last considering is stored there. */ -gpg_error_t -pkc_parse (pkc_t *result, int *errlinep, estream_t stream) -{ - gpg_error_t err = 0; - gpgrt_ssize_t len; - char *buf = NULL; - size_t buf_len = 0; - char *name = NULL; - strlist_t raw_value = NULL; - - - *result = pkc_new (); - if (*result == NULL) - return my_error_from_syserror (); - - if (errlinep) - *errlinep = 0; - while ((len = es_read_line (stream, &buf, &buf_len, NULL))) - { - char *p; - if (errlinep) - *errlinep += 1; - - /* Skip any whitespace. */ - for (p = buf; *p && ascii_isspace (*p); p++) - /* Do nothing. */; - - if (name && (spacep (buf) || *p == 0)) - { - /* A continuation. */ - if (append_to_strlist_try (&raw_value, buf) == NULL) - { - err = my_error_from_syserror (); - goto leave; - } - continue; - } - - /* No continuation. Add the current entry if any. */ - if (raw_value) - { - err = _pkc_add (*result, name, NULL, raw_value, 1); - if (err) - goto leave; - } - - /* And prepare for the next one. */ - name = NULL; - raw_value = NULL; - - if (*p != 0 && *p != '#') - { - char *colon, *value, tmp; - - colon = strchr (buf, ':'); - if (colon == NULL) - { - err = gpg_error (GPG_ERR_INV_VALUE); - goto leave; - } - - value = colon + 1; - tmp = *value; - *value = 0; - name = xtrystrdup (p); - *value = tmp; - - if (name == NULL) - { - err = my_error_from_syserror (); - goto leave; - } - - if (append_to_strlist_try (&raw_value, value) == NULL) - { - err = my_error_from_syserror (); - goto leave; - } - continue; - } - - if (append_to_strlist_try (&raw_value, buf) == NULL) - { - err = my_error_from_syserror (); - goto leave; - } - } - - /* Add the final entry. */ - if (raw_value) - err = _pkc_add (*result, name, NULL, raw_value, 1); - - leave: - gpgrt_free (buf); - if (err) - { - pkc_release (*result); - *result = NULL; - } - - return err; -} - - -/* Write a representation of PK to STREAM. */ -gpg_error_t -pkc_write (pkc_t pk, estream_t stream) -{ - gpg_error_t err; - pke_t entry; - strlist_t s; - - for (entry = pk->first; entry; entry = entry->next) - { - if (entry->name) - es_fputs (entry->name, stream); - - err = assert_raw_value (entry); - if (err) - return err; - - for (s = entry->raw_value; s; s = s->next) - es_fputs (s->d, stream); - - if (es_ferror (stream)) - return my_error_from_syserror (); - } - - return 0; -} diff --git a/common/private-keys.h b/common/private-keys.h deleted file mode 100644 index d21e94f7c..000000000 --- a/common/private-keys.h +++ /dev/null @@ -1,109 +0,0 @@ -/* private-keys.h - Parser and writer for the extended private key format. - * Copyright (C) 2016 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. - * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef GNUPG_COMMON_PRIVATE_KEYS_H -#define GNUPG_COMMON_PRIVATE_KEYS_H - -struct private_key_container; -typedef struct private_key_container *pkc_t; - -struct private_key_entry; -typedef struct private_key_entry *pke_t; - - - -/* Memory management, and dealing with entries. */ - -/* Allocate a private key container structure. */ -pkc_t pkc_new (void); - -/* Release a private key container structure. */ -void pkc_release (pkc_t pk); - -/* Get the name. */ -char *pke_name (pke_t pke); - -/* Get the value. */ -char *pke_value (pke_t pke); - - - -/* Lookup and iteration. */ - -/* Get the first non-comment entry. */ -pke_t pkc_first (pkc_t pk); - -/* Get the first entry with the given name. */ -pke_t pkc_lookup (pkc_t pk, const char *name); - -/* Get the next non-comment entry. */ -pke_t pke_next (pke_t entry); - -/* Get the next entry with the given name. */ -pke_t pke_next_value (pke_t entry, const char *name); - - - -/* Adding and modifying values. */ - -/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it - is not updated but the new entry is appended. */ -gpg_error_t pkc_add (pkc_t pk, const char *name, const char *value); - -/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it - is updated with VALUE. If multiple entries with NAME exist, the - first entry is updated. */ -gpg_error_t pkc_set (pkc_t pk, const char *name, const char *value); - -/* Delete the given entry from PK. */ -void pkc_delete (pkc_t pk, pke_t pke); - - - -/* Private key handling. */ - -/* Get the private key. */ -gpg_error_t pkc_get_private_key (pkc_t pk, gcry_sexp_t *retsexp); - -/* Set the private key. */ -gpg_error_t pkc_set_private_key (pkc_t pk, gcry_sexp_t sexp); - - - -/* Parsing and serialization. */ - -/* Parse STREAM and return a newly allocated private key container - structure in RESULT. If ERRLINEP is given, the line number the - parser was last considering is stored there. */ -gpg_error_t pkc_parse (pkc_t *result, int *errlinep, estream_t stream); - -/* Write a representation of PK to STREAM. */ -gpg_error_t pkc_write (pkc_t pk, estream_t stream); - -#endif /* GNUPG_COMMON_PRIVATE_KEYS_H */ diff --git a/common/t-name-value.c b/common/t-name-value.c new file mode 100644 index 000000000..d6cef3503 --- /dev/null +++ b/common/t-name-value.c @@ -0,0 +1,541 @@ +/* t-name-value.c - Module test for name-value.c + * Copyright (C) 2016 g10 Code GmbH + * + * 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 "util.h" +#include "name-value.h" + +static int verbose; + +void +test_getting_values (pkc_t pk) +{ + pke_t e; + + e = pkc_lookup (pk, "Comment:"); + assert (e); + + /* Names are case-insensitive. */ + e = pkc_lookup (pk, "comment:"); + assert (e); + e = pkc_lookup (pk, "COMMENT:"); + assert (e); + + e = pkc_lookup (pk, "SomeOtherName:"); + assert (e); +} + + +void +test_key_extraction (pkc_t pk) +{ + gpg_error_t err; + gcry_sexp_t key; + + err = pkc_get_private_key (pk, &key); + assert (err == 0); + assert (key); + + if (verbose) + gcry_sexp_dump (key); + + gcry_sexp_release (key); +} + + +void +test_iteration (pkc_t pk) +{ + int i; + pke_t e; + + i = 0; + for (e = pkc_first (pk); e; e = pke_next (e)) + i++; + assert (i == 4); + + i = 0; + for (e = pkc_lookup (pk, "Comment:"); + e; + e = pke_next_value (e, "Comment:")) + i++; + assert (i == 3); +} + + +void +test_whitespace (pkc_t pk) +{ + pke_t e; + + e = pkc_lookup (pk, "One:"); + assert (e); + assert (strcmp (pke_value (e), "WithoutWhitespace") == 0); + + e = pkc_lookup (pk, "Two:"); + assert (e); + assert (strcmp (pke_value (e), "With Whitespace") == 0); + + e = pkc_lookup (pk, "Three:"); + assert (e); + assert (strcmp (pke_value (e), + "Blank lines in continuations encode newlines.\n" + "Next paragraph.") == 0); +} + + +struct +{ + char *value; + void (*test_func) (pkc_t); +} tests[] = + { + { + "# This is a comment followed by an empty line\n" + "\n", + NULL, + }, + { + "# This is a comment followed by two empty lines, Windows style\r\n" + "\r\n" + "\r\n", + NULL, + }, + { + "# Some name,value pairs\n" + "Comment: Some comment.\n" + "SomeOtherName: Some value.\n", + test_getting_values, + }, + { + " # Whitespace is preserved as much as possible\r\n" + "Comment:Some comment.\n" + "SomeOtherName: Some value. \n", + test_getting_values, + }, + { + "# Values may be continued in the next line as indicated by leading\n" + "# space\n" + "Comment: Some rather long\n" + " comment that is continued in the next line.\n" + "\n" + " Blank lines with or without whitespace are allowed within\n" + " continuations to allow paragraphs.\n" + "SomeOtherName: Some value.\n", + test_getting_values, + }, + { + "# Names may be given multiple times forming an array of values\n" + "Comment: Some comment, element 0.\n" + "Comment: Some comment, element 1.\n" + "Comment: Some comment, element 2.\n" + "SomeOtherName: Some value.\n", + test_iteration, + }, + { + "# One whitespace at the beginning of a continuation is swallowed.\n" + "One: Without\n" + " Whitespace\n" + "Two: With\n" + " Whitespace\n" + "Three: Blank lines in continuations encode newlines.\n" + "\n" + " Next paragraph.\n", + test_whitespace, + }, + { + "Description: Key to sign all GnuPG released tarballs.\n" + " The key is actually stored on a smart card.\n" + "Use-for-ssh: yes\n" + "OpenSSH-cert: long base64 encoded string wrapped so that this\n" + " key file can be easily edited with a standard editor.\n" + "Key: (shadowed-private-key\n" + " (rsa\n" + " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n" + " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n" + " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n" + " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n" + " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n" + " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n" + " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n" + " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n" + " E186A02BA2497FDC5D1221#)\n" + " (e #00010001#)\n" + " (shadowed t1-v1\n" + " (#D2760001240102000005000011730000# OPENPGP.1)\n" + " )))\n", + test_key_extraction, + }, + }; + + +static char * +pkc_to_string (pkc_t pk) +{ + gpg_error_t err; + char *buf; + size_t len; + estream_t sink; + + sink = es_fopenmem (0, "rw"); + assert (sink); + + err = pkc_write (pk, sink); + assert (err == 0); + + len = es_ftell (sink); + buf = xmalloc (len+1); + assert (buf); + + es_fseek (sink, 0, SEEK_SET); + es_read (sink, buf, len, NULL); + buf[len] = 0; + + es_fclose (sink); + return buf; +} + + +void dummy_free (void *p) { (void) p; } +void *dummy_realloc (void *p, size_t s) { (void) s; return p; } + +void +run_tests (void) +{ + gpg_error_t err; + pkc_t pk; + + int i; + for (i = 0; i < DIM (tests); i++) + { + estream_t source; + char *buf; + size_t len; + + len = strlen (tests[i].value); + source = es_mopen (tests[i].value, len, len, + 0, dummy_realloc, dummy_free, "r"); + assert (source); + + err = pkc_parse (&pk, NULL, source); + assert (err == 0); + assert (pk); + + if (verbose) + { + err = pkc_write (pk, es_stderr); + assert (err == 0); + } + + buf = pkc_to_string (pk); + assert (memcmp (tests[i].value, buf, len) == 0); + + es_fclose (source); + xfree (buf); + + if (tests[i].test_func) + tests[i].test_func (pk); + + pkc_release (pk); + } +} + + +void +run_modification_tests (void) +{ + gpg_error_t err; + pkc_t pk; + gcry_sexp_t key; + char *buf; + + pk = pkc_new (); + assert (pk); + + pkc_set (pk, "Foo:", "Bar"); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Bar\n") == 0); + xfree (buf); + + pkc_set (pk, "Foo:", "Baz"); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\n") == 0); + xfree (buf); + + pkc_set (pk, "Bar:", "Bazzel"); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + pkc_add (pk, "Foo:", "Bar"); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + pkc_add (pk, "DontExistYet:", "Bar"); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n") + == 0); + xfree (buf); + + pkc_delete (pk, pkc_lookup (pk, "DontExistYet:")); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + pkc_delete (pk, pke_next_value (pkc_lookup (pk, "Foo:"), "Foo:")); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + pkc_delete (pk, pkc_lookup (pk, "Foo:")); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Bar: Bazzel\n") == 0); + xfree (buf); + + pkc_delete (pk, pkc_first (pk)); + buf = pkc_to_string (pk); + assert (strcmp (buf, "") == 0); + xfree (buf); + + pkc_set (pk, "Foo:", "A really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: A really long value spanning across multiple" + " lines that has to be\n wrapped at a convenient space.\n") + == 0); + xfree (buf); + + pkc_set (pk, "Foo:", "XA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: XA really long value spanning across multiple" + " lines that has to\n be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + pkc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple" + " lines that has\n to be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + pkc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines" + "thathastobewrappedataconvenientspacethatisnotthere."); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat" + "hastobewrappedataco\n nvenientspacethatisnotthere.\n") + == 0); + xfree (buf); + pkc_release (pk); + + pk = pkc_new (); + assert (pk); + + err = gcry_sexp_build (&key, NULL, "(hello world)"); + assert (err == 0); + assert (key); + + err = pkc_set_private_key (pk, key); + gcry_sexp_release (key); + assert (err == 0); + buf = pkc_to_string (pk); + assert (strcmp (buf, "Key: (hello world)\n") == 0); + xfree (buf); + pkc_release (pk); +} + + +void +convert (const char *fname) +{ + gpg_error_t err; + estream_t source; + gcry_sexp_t key; + char *buf; + size_t buflen; + struct stat st; + pkc_t pk; + + source = es_fopen (fname, "rb"); + if (source == NULL) + goto leave; + + if (fstat (es_fileno (source), &st)) + goto leave; + + buflen = st.st_size; + buf = xtrymalloc (buflen+1); + assert (buf); + + if (es_fread (buf, buflen, 1, source) != 1) + goto leave; + + err = gcry_sexp_sscan (&key, NULL, buf, buflen); + if (err) + { + fprintf (stderr, "malformed s-expression in %s\n", fname); + exit (1); + } + + pk = pkc_new (); + assert (pk); + + err = pkc_set_private_key (pk, key); + assert (err == 0); + + err = pkc_write (pk, es_stdout); + assert (err == 0); + + return; + + leave: + perror (fname); + exit (1); +} + + +void +parse (const char *fname) +{ + gpg_error_t err; + estream_t source; + char *buf; + pkc_t pk_a, pk_b; + pke_t e; + int line; + + source = es_fopen (fname, "rb"); + if (source == NULL) + { + perror (fname); + exit (1); + } + + err = pkc_parse (&pk_a, &line, source); + if (err) + { + fprintf (stderr, "failed to parse %s line %d: %s\n", + fname, line, gpg_strerror (err)); + exit (1); + } + + buf = pkc_to_string (pk_a); + xfree (buf); + + pk_b = pkc_new (); + assert (pk_b); + + for (e = pkc_first (pk_a); e; e = pke_next (e)) + { + gcry_sexp_t key = NULL; + + if (strcasecmp (pke_name (e), "Key:") == 0) + { + err = pkc_get_private_key (pk_a, &key); + if (err) + key = NULL; + } + + if (key) + { + err = pkc_set_private_key (pk_b, key); + assert (err == 0); + } + else + { + err = pkc_add (pk_b, pke_name (e), pke_value (e)); + assert (err == 0); + } + } + + buf = pkc_to_string (pk_b); + if (verbose) + fprintf (stdout, "%s", buf); + xfree (buf); +} + + +void +print_usage (void) +{ + fprintf (stderr, + "usage: t-private-keys [--verbose]" + " [--convert " + " || --parse ]\n"); + exit (2); +} + + +int +main (int argc, char **argv) +{ + enum { TEST, CONVERT, PARSE } command = TEST; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + if (argc && !strcmp (argv[0], "--convert")) + { + command = CONVERT; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + if (argc && !strcmp (argv[0], "--parse")) + { + command = PARSE; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + switch (command) + { + case TEST: + run_tests (); + run_modification_tests (); + break; + + case CONVERT: + convert (*argv); + break; + + case PARSE: + parse (*argv); + break; + } + + return 0; +} diff --git a/common/t-private-keys.c b/common/t-private-keys.c deleted file mode 100644 index 1027e70cf..000000000 --- a/common/t-private-keys.c +++ /dev/null @@ -1,541 +0,0 @@ -/* t-private-keys.c - Module test for private-keys.c - * Copyright (C) 2016 g10 Code GmbH - * - * 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 "util.h" -#include "private-keys.h" - -static int verbose; - -void -test_getting_values (pkc_t pk) -{ - pke_t e; - - e = pkc_lookup (pk, "Comment:"); - assert (e); - - /* Names are case-insensitive. */ - e = pkc_lookup (pk, "comment:"); - assert (e); - e = pkc_lookup (pk, "COMMENT:"); - assert (e); - - e = pkc_lookup (pk, "SomeOtherName:"); - assert (e); -} - - -void -test_key_extraction (pkc_t pk) -{ - gpg_error_t err; - gcry_sexp_t key; - - err = pkc_get_private_key (pk, &key); - assert (err == 0); - assert (key); - - if (verbose) - gcry_sexp_dump (key); - - gcry_sexp_release (key); -} - - -void -test_iteration (pkc_t pk) -{ - int i; - pke_t e; - - i = 0; - for (e = pkc_first (pk); e; e = pke_next (e)) - i++; - assert (i == 4); - - i = 0; - for (e = pkc_lookup (pk, "Comment:"); - e; - e = pke_next_value (e, "Comment:")) - i++; - assert (i == 3); -} - - -void -test_whitespace (pkc_t pk) -{ - pke_t e; - - e = pkc_lookup (pk, "One:"); - assert (e); - assert (strcmp (pke_value (e), "WithoutWhitespace") == 0); - - e = pkc_lookup (pk, "Two:"); - assert (e); - assert (strcmp (pke_value (e), "With Whitespace") == 0); - - e = pkc_lookup (pk, "Three:"); - assert (e); - assert (strcmp (pke_value (e), - "Blank lines in continuations encode newlines.\n" - "Next paragraph.") == 0); -} - - -struct -{ - char *value; - void (*test_func) (pkc_t); -} tests[] = - { - { - "# This is a comment followed by an empty line\n" - "\n", - NULL, - }, - { - "# This is a comment followed by two empty lines, Windows style\r\n" - "\r\n" - "\r\n", - NULL, - }, - { - "# Some name,value pairs\n" - "Comment: Some comment.\n" - "SomeOtherName: Some value.\n", - test_getting_values, - }, - { - " # Whitespace is preserved as much as possible\r\n" - "Comment:Some comment.\n" - "SomeOtherName: Some value. \n", - test_getting_values, - }, - { - "# Values may be continued in the next line as indicated by leading\n" - "# space\n" - "Comment: Some rather long\n" - " comment that is continued in the next line.\n" - "\n" - " Blank lines with or without whitespace are allowed within\n" - " continuations to allow paragraphs.\n" - "SomeOtherName: Some value.\n", - test_getting_values, - }, - { - "# Names may be given multiple times forming an array of values\n" - "Comment: Some comment, element 0.\n" - "Comment: Some comment, element 1.\n" - "Comment: Some comment, element 2.\n" - "SomeOtherName: Some value.\n", - test_iteration, - }, - { - "# One whitespace at the beginning of a continuation is swallowed.\n" - "One: Without\n" - " Whitespace\n" - "Two: With\n" - " Whitespace\n" - "Three: Blank lines in continuations encode newlines.\n" - "\n" - " Next paragraph.\n", - test_whitespace, - }, - { - "Description: Key to sign all GnuPG released tarballs.\n" - " The key is actually stored on a smart card.\n" - "Use-for-ssh: yes\n" - "OpenSSH-cert: long base64 encoded string wrapped so that this\n" - " key file can be easily edited with a standard editor.\n" - "Key: (shadowed-private-key\n" - " (rsa\n" - " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n" - " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n" - " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n" - " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n" - " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n" - " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n" - " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n" - " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n" - " E186A02BA2497FDC5D1221#)\n" - " (e #00010001#)\n" - " (shadowed t1-v1\n" - " (#D2760001240102000005000011730000# OPENPGP.1)\n" - " )))\n", - test_key_extraction, - }, - }; - - -static char * -pkc_to_string (pkc_t pk) -{ - gpg_error_t err; - char *buf; - size_t len; - estream_t sink; - - sink = es_fopenmem (0, "rw"); - assert (sink); - - err = pkc_write (pk, sink); - assert (err == 0); - - len = es_ftell (sink); - buf = xmalloc (len+1); - assert (buf); - - es_fseek (sink, 0, SEEK_SET); - es_read (sink, buf, len, NULL); - buf[len] = 0; - - es_fclose (sink); - return buf; -} - - -void dummy_free (void *p) { (void) p; } -void *dummy_realloc (void *p, size_t s) { (void) s; return p; } - -void -run_tests (void) -{ - gpg_error_t err; - pkc_t pk; - - int i; - for (i = 0; i < DIM (tests); i++) - { - estream_t source; - char *buf; - size_t len; - - len = strlen (tests[i].value); - source = es_mopen (tests[i].value, len, len, - 0, dummy_realloc, dummy_free, "r"); - assert (source); - - err = pkc_parse (&pk, NULL, source); - assert (err == 0); - assert (pk); - - if (verbose) - { - err = pkc_write (pk, es_stderr); - assert (err == 0); - } - - buf = pkc_to_string (pk); - assert (memcmp (tests[i].value, buf, len) == 0); - - es_fclose (source); - xfree (buf); - - if (tests[i].test_func) - tests[i].test_func (pk); - - pkc_release (pk); - } -} - - -void -run_modification_tests (void) -{ - gpg_error_t err; - pkc_t pk; - gcry_sexp_t key; - char *buf; - - pk = pkc_new (); - assert (pk); - - pkc_set (pk, "Foo:", "Bar"); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Bar\n") == 0); - xfree (buf); - - pkc_set (pk, "Foo:", "Baz"); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Baz\n") == 0); - xfree (buf); - - pkc_set (pk, "Bar:", "Bazzel"); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); - xfree (buf); - - pkc_add (pk, "Foo:", "Bar"); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); - xfree (buf); - - pkc_add (pk, "DontExistYet:", "Bar"); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n") - == 0); - xfree (buf); - - pkc_delete (pk, pkc_lookup (pk, "DontExistYet:")); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); - xfree (buf); - - pkc_delete (pk, pke_next_value (pkc_lookup (pk, "Foo:"), "Foo:")); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); - xfree (buf); - - pkc_delete (pk, pkc_lookup (pk, "Foo:")); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Bar: Bazzel\n") == 0); - xfree (buf); - - pkc_delete (pk, pkc_first (pk)); - buf = pkc_to_string (pk); - assert (strcmp (buf, "") == 0); - xfree (buf); - - pkc_set (pk, "Foo:", "A really long value spanning across multiple lines" - " that has to be wrapped at a convenient space."); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: A really long value spanning across multiple" - " lines that has to be\n wrapped at a convenient space.\n") - == 0); - xfree (buf); - - pkc_set (pk, "Foo:", "XA really long value spanning across multiple lines" - " that has to be wrapped at a convenient space."); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: XA really long value spanning across multiple" - " lines that has to\n be wrapped at a convenient space.\n") - == 0); - xfree (buf); - - pkc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines" - " that has to be wrapped at a convenient space."); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple" - " lines that has\n to be wrapped at a convenient space.\n") - == 0); - xfree (buf); - - pkc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines" - "thathastobewrappedataconvenientspacethatisnotthere."); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat" - "hastobewrappedataco\n nvenientspacethatisnotthere.\n") - == 0); - xfree (buf); - pkc_release (pk); - - pk = pkc_new (); - assert (pk); - - err = gcry_sexp_build (&key, NULL, "(hello world)"); - assert (err == 0); - assert (key); - - err = pkc_set_private_key (pk, key); - gcry_sexp_release (key); - assert (err == 0); - buf = pkc_to_string (pk); - assert (strcmp (buf, "Key: (hello world)\n") == 0); - xfree (buf); - pkc_release (pk); -} - - -void -convert (const char *fname) -{ - gpg_error_t err; - estream_t source; - gcry_sexp_t key; - char *buf; - size_t buflen; - struct stat st; - pkc_t pk; - - source = es_fopen (fname, "rb"); - if (source == NULL) - goto leave; - - if (fstat (es_fileno (source), &st)) - goto leave; - - buflen = st.st_size; - buf = xtrymalloc (buflen+1); - assert (buf); - - if (es_fread (buf, buflen, 1, source) != 1) - goto leave; - - err = gcry_sexp_sscan (&key, NULL, buf, buflen); - if (err) - { - fprintf (stderr, "malformed s-expression in %s\n", fname); - exit (1); - } - - pk = pkc_new (); - assert (pk); - - err = pkc_set_private_key (pk, key); - assert (err == 0); - - err = pkc_write (pk, es_stdout); - assert (err == 0); - - return; - - leave: - perror (fname); - exit (1); -} - - -void -parse (const char *fname) -{ - gpg_error_t err; - estream_t source; - char *buf; - pkc_t pk_a, pk_b; - pke_t e; - int line; - - source = es_fopen (fname, "rb"); - if (source == NULL) - { - perror (fname); - exit (1); - } - - err = pkc_parse (&pk_a, &line, source); - if (err) - { - fprintf (stderr, "failed to parse %s line %d: %s\n", - fname, line, gpg_strerror (err)); - exit (1); - } - - buf = pkc_to_string (pk_a); - xfree (buf); - - pk_b = pkc_new (); - assert (pk_b); - - for (e = pkc_first (pk_a); e; e = pke_next (e)) - { - gcry_sexp_t key = NULL; - - if (strcasecmp (pke_name (e), "Key:") == 0) - { - err = pkc_get_private_key (pk_a, &key); - if (err) - key = NULL; - } - - if (key) - { - err = pkc_set_private_key (pk_b, key); - assert (err == 0); - } - else - { - err = pkc_add (pk_b, pke_name (e), pke_value (e)); - assert (err == 0); - } - } - - buf = pkc_to_string (pk_b); - if (verbose) - fprintf (stdout, "%s", buf); - xfree (buf); -} - - -void -print_usage (void) -{ - fprintf (stderr, - "usage: t-private-keys [--verbose]" - " [--convert " - " || --parse ]\n"); - exit (2); -} - - -int -main (int argc, char **argv) -{ - enum { TEST, CONVERT, PARSE } command = TEST; - - if (argc) - { argc--; argv++; } - if (argc && !strcmp (argv[0], "--verbose")) - { - verbose = 1; - argc--; argv++; - } - - if (argc && !strcmp (argv[0], "--convert")) - { - command = CONVERT; - argc--; argv++; - if (argc != 1) - print_usage (); - } - - if (argc && !strcmp (argv[0], "--parse")) - { - command = PARSE; - argc--; argv++; - if (argc != 1) - print_usage (); - } - - switch (command) - { - case TEST: - run_tests (); - run_modification_tests (); - break; - - case CONVERT: - convert (*argv); - break; - - case PARSE: - parse (*argv); - break; - } - - return 0; -} -- cgit v1.2.3