summaryrefslogtreecommitdiffstats
path: root/sm/misc.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sm/misc.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/sm/misc.c b/sm/misc.c
new file mode 100644
index 0000000..66d928c
--- /dev/null
+++ b/sm/misc.c
@@ -0,0 +1,308 @@
+/* misc.c - Miscellaneous functions
+ * Copyright (C) 2004, 2009, 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "gpgsm.h"
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/tlv.h"
+#include "../common/sexp-parse.h"
+
+
+/* Setup the environment so that the pinentry is able to get all
+ required information. This is used prior to an exec of the
+ protect-tool. */
+void
+setup_pinentry_env (void)
+{
+#ifndef HAVE_W32_SYSTEM
+ char *lc;
+ const char *name, *value;
+ int iterator;
+
+ /* Try to make sure that GPG_TTY has been set. This is needed if we
+ call for example the protect-tools with redirected stdin and thus
+ it won't be able to ge a default by itself. Try to do it here
+ but print a warning. */
+ value = session_env_getenv (opt.session_env, "GPG_TTY");
+ if (value)
+ gnupg_setenv ("GPG_TTY", value, 1);
+ else if (!(lc=getenv ("GPG_TTY")) || !*lc)
+ {
+ log_error (_("GPG_TTY has not been set - "
+ "using maybe bogus default\n"));
+ lc = gnupg_ttyname (0);
+ if (!lc)
+ lc = "/dev/tty";
+ gnupg_setenv ("GPG_TTY", lc, 1);
+ }
+
+ if (opt.lc_ctype)
+ gnupg_setenv ("LC_CTYPE", opt.lc_ctype, 1);
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ else if ( (lc = setlocale (LC_CTYPE, "")) )
+ gnupg_setenv ("LC_CTYPE", lc, 1);
+#endif
+
+ if (opt.lc_messages)
+ gnupg_setenv ("LC_MESSAGES", opt.lc_messages, 1);
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ else if ( (lc = setlocale (LC_MESSAGES, "")) )
+ gnupg_setenv ("LC_MESSAGES", lc, 1);
+#endif
+
+ iterator = 0;
+ while ((name = session_env_list_stdenvnames (&iterator, NULL)))
+ {
+ if (!strcmp (name, "GPG_TTY"))
+ continue; /* Already set. */
+ value = session_env_getenv (opt.session_env, name);
+ if (value)
+ gnupg_setenv (name, value, 1);
+ }
+
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
+
+/* Transform a sig-val style s-expression as returned by Libgcrypt to
+ one which includes an algorithm identifier encoding the public key
+ and the hash algorithm. The public key algorithm is taken directly
+ from SIGVAL and the hash algorithm is given by MDALGO. This is
+ required because X.509 merges the public key algorithm and the hash
+ algorithm into one OID but Libgcrypt is not aware of that. The
+ function ignores missing parameters so that it can also be used to
+ create an siginfo value as expected by ksba_certreq_set_siginfo.
+ To create a siginfo s-expression a public-key s-expression may be
+ used instead of a sig-val. We only support RSA for now. */
+gpg_error_t
+transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
+ unsigned char **r_newsigval, size_t *r_newsigvallen)
+{
+ gpg_error_t err;
+ const unsigned char *buf, *tok;
+ size_t buflen, toklen;
+ int depth, last_depth1, last_depth2;
+ int is_pubkey = 0;
+ const unsigned char *rsa_s = NULL;
+ size_t rsa_s_len = 0;
+ const char *oid;
+ gcry_sexp_t sexp;
+
+ *r_newsigval = NULL;
+ if (r_newsigvallen)
+ *r_newsigvallen = 0;
+
+ buf = sigval;
+ buflen = sigvallen;
+ depth = 0;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ return err;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ return err;
+ if (tok && toklen == 7 && !memcmp ("sig-val", tok, toklen))
+ ;
+ else if (tok && toklen == 10 && !memcmp ("public-key", tok, toklen))
+ is_pubkey = 1;
+ else
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ return err;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ return err;
+ if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+ return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ return err;
+ if (tok && toklen == 1)
+ {
+ const unsigned char **mpi;
+ size_t *mpi_len;
+
+ switch (*tok)
+ {
+ case 's': mpi = &rsa_s; mpi_len = &rsa_s_len; break;
+ default: mpi = NULL; mpi_len = NULL; break;
+ }
+ if (mpi && *mpi)
+ return gpg_error (GPG_ERR_DUP_VALUE);
+
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ return err;
+ if (tok && mpi)
+ {
+ *mpi = tok;
+ *mpi_len = toklen;
+ }
+ }
+
+ /* Skip to the end of the list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ return err;
+ }
+ if (err)
+ return err;
+
+ /* Map the hash algorithm to an OID. */
+ switch (mdalgo)
+ {
+ case GCRY_MD_SHA1:
+ oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA256:
+ oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA384:
+ oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA512:
+ oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */
+ break;
+
+ default:
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+ }
+
+ if (rsa_s && !is_pubkey)
+ err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))",
+ oid, (int)rsa_s_len, rsa_s);
+ else
+ err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid);
+ if (err)
+ return err;
+ err = make_canon_sexp (sexp, r_newsigval, r_newsigvallen);
+ gcry_sexp_release (sexp);
+
+ return err;
+}
+
+
+/* Wrapper around ksba_cms_get_sig_val to return a gcrypt object
+ * instaed of ksba's canonical s-expression. On errror NULL is return
+ * and in some cases an error message is printed. */
+gcry_sexp_t
+gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx)
+{
+ gpg_error_t err;
+ ksba_sexp_t sigval;
+ gcry_sexp_t s_sigval;
+ size_t n;
+
+ sigval = ksba_cms_get_sig_val (cms, idx);
+ if (!sigval)
+ return NULL;
+ n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
+ if (!n)
+ {
+ log_error ("%s: libksba did not return a proper S-Exp\n", __func__);
+ ksba_free (sigval);
+ return NULL;
+ }
+ err = gcry_sexp_sscan (&s_sigval, NULL, (char*)sigval, n);
+ ksba_free (sigval);
+ if (err)
+ {
+ log_error ("%s: gcry_sexp_scan failed: %s\n",
+ __func__, gpg_strerror (err));
+ s_sigval = NULL;
+ }
+
+ return s_sigval;
+}
+
+
+/* Return the hash algorithm from the S-expression SIGVAL. Returns 0
+ * if the hash algorithm is not encoded in SIGVAL or it is not
+ * supported by libgcrypt. It further stores flag values for the
+ * public key algorithm at R_PKALGO_FLAGS; the only flag we currently
+ * support is PK_ALGO_FLAG_RSAPSS. */
+int
+gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval_arg,
+ unsigned int *r_pkalgo_flags)
+{
+ gcry_sexp_t sigval, l1;
+ size_t n;
+ const char *s;
+ char *string;
+ int hashalgo;
+ int i;
+
+ *r_pkalgo_flags = 0;
+
+ sigval = gcry_sexp_find_token (sigval_arg, "sig-val", 0);
+ if (!sigval)
+ return 0; /* Not a sig-val. */
+
+ /* First check whether this is a rsaPSS signature and return that as
+ * additional info. */
+ l1 = gcry_sexp_find_token (sigval, "flags", 0);
+ if (l1)
+ {
+ /* Note that the flag parser assumes that the list of flags
+ * contains only strings and in particular not a sub-list. This
+ * is always the case for the current libksba. */
+ for (i=1; (s = gcry_sexp_nth_data (l1, i, &n)); i++)
+ if (n == 3 && !memcmp (s, "pss", 3))
+ {
+ *r_pkalgo_flags |= PK_ALGO_FLAG_RSAPSS;
+ break;
+ }
+ gcry_sexp_release (l1);
+ }
+
+ l1 = gcry_sexp_find_token (sigval, "hash", 0);
+ if (!l1)
+ {
+ gcry_sexp_release (sigval);
+ return 0; /* hash algorithm not given in sigval. */
+ }
+ string = gcry_sexp_nth_string (l1, 1);
+ gcry_sexp_release (sigval);
+ if (!string)
+ return 0; /* hash algorithm has no value. */
+ hashalgo = gcry_md_map_name (string);
+ gcry_free (string);
+
+ return hashalgo;
+}