aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am12
-rw-r--r--common/asshelp.c43
-rw-r--r--common/asshelp.h11
-rw-r--r--common/audit.c4
-rw-r--r--common/b64dec.c299
-rw-r--r--common/b64enc.c423
-rw-r--r--common/compliance.c10
-rw-r--r--common/dynload.h11
-rw-r--r--common/exechelp-posix.c999
-rw-r--r--common/exechelp-w32.c1209
-rw-r--r--common/exechelp.h231
-rw-r--r--common/exectool.c40
-rw-r--r--common/get-passphrase.c3
-rw-r--r--common/homedir.c586
-rw-r--r--common/init.c1
-rw-r--r--common/iobuf.c72
-rw-r--r--common/iobuf.h6
-rw-r--r--common/kem.c220
-rw-r--r--common/miscellaneous.c72
-rw-r--r--common/openpgp-oid.c83
-rw-r--r--common/openpgpdefs.h8
-rw-r--r--common/sexputil.c46
-rw-r--r--common/ssh-utils.c24
-rw-r--r--common/sysutils.c206
-rw-r--r--common/sysutils.h16
-rw-r--r--common/t-b64.c283
-rw-r--r--common/t-exechelp.c152
-rw-r--r--common/t-iobuf.c56
-rw-r--r--common/util.h52
29 files changed, 2655 insertions, 2523 deletions
diff --git a/common/Makefile.am b/common/Makefile.am
index 91718f99e..a4840fbdd 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -65,7 +65,7 @@ common_sources = \
homedir.c \
gettime.c gettime.h \
yesno.c \
- b64enc.c b64dec.c zb32.c zb32.h \
+ zb32.c zb32.h \
convert.c \
percent.c \
mbox-util.c mbox-util.h \
@@ -97,8 +97,8 @@ common_sources = \
openpgp-fpr.c \
comopt.c comopt.h \
compliance.c compliance.h \
- pkscreening.c pkscreening.h
-
+ pkscreening.c pkscreening.h \
+ kem.c
if HAVE_W32_SYSTEM
common_sources += w32-reg.c w32-cmdline.c
@@ -161,11 +161,12 @@ 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-name-value t-ccparray t-recsel t-w32-cmdline t-b64
+ t-name-value t-ccparray t-recsel t-w32-cmdline t-exechelp
+
if HAVE_W32_SYSTEM
module_tests += t-w32-reg
else
-module_tests += t-exechelp t-exectool
+module_tests += t-exectool
endif
if MAINTAINER_MODE
@@ -196,7 +197,6 @@ t_gettime_LDADD = $(t_common_ldadd)
t_sysutils_LDADD = $(t_common_ldadd)
t_helpfile_LDADD = $(t_common_ldadd)
t_sexputil_LDADD = $(t_common_ldadd)
-t_b64_LDADD = $(t_common_ldadd)
t_exechelp_LDADD = $(t_common_ldadd)
t_exectool_LDADD = $(t_common_ldadd)
t_session_env_LDADD = $(t_common_ldadd)
diff --git a/common/asshelp.c b/common/asshelp.c
index eb3e41bf5..5a40e0380 100644
--- a/common/asshelp.c
+++ b/common/asshelp.c
@@ -386,7 +386,8 @@ start_new_service (assuan_context_t *r_ctx,
const char *opt_lc_ctype,
const char *opt_lc_messages,
session_env_t session_env,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg)
{
@@ -445,7 +446,7 @@ start_new_service (assuan_context_t *r_ctx,
}
err = assuan_socket_connect (ctx, sockname, 0, connect_flags);
- if (err && autostart)
+ if (err && (flags & ASSHELP_FLAG_AUTOSTART))
{
char *abs_homedir;
lock_spawn_t lock;
@@ -523,16 +524,12 @@ start_new_service (assuan_context_t *r_ctx,
&& assuan_socket_connect (ctx, sockname, 0, connect_flags))
{
#ifdef HAVE_W32_SYSTEM
- err = gnupg_spawn_process_detached (program? program : program_name,
- argv, NULL);
+ err = gnupg_process_spawn (program? program : program_name, argv,
+ GNUPG_PROCESS_DETACHED,
+ NULL, NULL, NULL);
#else /*!W32*/
- pid_t pid;
-
- err = gnupg_spawn_process_fd (program? program : program_name,
- argv, -1, -1, -1, &pid);
- if (!err)
- err = gnupg_wait_process (program? program : program_name,
- pid, 1, NULL);
+ err = gnupg_process_spawn (program? program : program_name, argv,
+ 0, NULL, NULL, NULL);
#endif /*!W32*/
if (err)
log_error ("failed to start %s '%s': %s\n",
@@ -551,7 +548,8 @@ start_new_service (assuan_context_t *r_ctx,
xfree (sockname);
if (err)
{
- if (autostart || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED)
+ if ((flags & ASSHELP_FLAG_AUTOSTART)
+ || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED)
log_error ("can't connect to the %s: %s\n",
printed_name, gpg_strerror (err));
assuan_release (ctx);
@@ -603,55 +601,58 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
const char *opt_lc_ctype,
const char *opt_lc_messages,
session_env_t session_env,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg)
{
return start_new_service (r_ctx, GNUPG_MODULE_NAME_AGENT,
errsource, agent_program,
opt_lc_ctype, opt_lc_messages, session_env,
- autostart, verbose, debug,
+ flags, verbose, debug,
status_cb, status_cb_arg);
}
/* Try to connect to the dirmngr via a socket. On platforms
- supporting it, start it up if needed and if AUTOSTART is true.
+ supporting it, start it up if needed and if ASSHELP_FLAG_AUTOSTART is set.
Returns a new assuan context at R_CTX or an error code. */
gpg_error_t
start_new_keyboxd (assuan_context_t *r_ctx,
gpg_err_source_t errsource,
const char *keyboxd_program,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg)
{
return start_new_service (r_ctx, GNUPG_MODULE_NAME_KEYBOXD,
errsource, keyboxd_program,
NULL, NULL, NULL,
- autostart, verbose, debug,
+ flags, verbose, debug,
status_cb, status_cb_arg);
}
/* Try to connect to the dirmngr via a socket. On platforms
- supporting it, start it up if needed and if AUTOSTART is true.
+ supporting it, start it up if needed and if ASSHELP_FLAG_AUTOSTART is set.
Returns a new assuan context at R_CTX or an error code. */
gpg_error_t
start_new_dirmngr (assuan_context_t *r_ctx,
gpg_err_source_t errsource,
const char *dirmngr_program,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg)
{
#ifndef USE_DIRMNGR_AUTO_START
- autostart = 0;
+ flags &= ~ASSHELP_FLAG_AUTOSTART; /* Clear flag. */
#endif
return start_new_service (r_ctx, GNUPG_MODULE_NAME_DIRMNGR,
errsource, dirmngr_program,
NULL, NULL, NULL,
- autostart, verbose, debug,
+ flags, verbose, debug,
status_cb, status_cb_arg);
}
diff --git a/common/asshelp.h b/common/asshelp.h
index e7e43bd1b..bca50759d 100644
--- a/common/asshelp.h
+++ b/common/asshelp.h
@@ -37,6 +37,8 @@
#include "util.h"
/*-- asshelp.c --*/
+#define ASSHELP_FLAG_AUTOSTART 1 /* Autostart the new service. */
+
void setup_libassuan_logging (unsigned int *debug_var_address,
int (*log_monitor)(assuan_context_t ctx,
@@ -61,7 +63,8 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
const char *opt_lc_ctype,
const char *opt_lc_messages,
session_env_t session_env,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg);
@@ -71,7 +74,8 @@ gpg_error_t
start_new_keyboxd (assuan_context_t *r_ctx,
gpg_err_source_t errsource,
const char *keyboxd_program,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg);
@@ -81,7 +85,8 @@ gpg_error_t
start_new_dirmngr (assuan_context_t *r_ctx,
gpg_err_source_t errsource,
const char *dirmngr_program,
- int autostart, int verbose, int debug,
+ unsigned int flags,
+ int verbose, int debug,
gpg_error_t (*status_cb)(ctrl_t, int, ...),
ctrl_t status_cb_arg);
diff --git a/common/audit.c b/common/audit.c
index ae0d45216..42a2cf6d6 100644
--- a/common/audit.c
+++ b/common/audit.c
@@ -45,8 +45,8 @@ struct log_item_s
int intvalue; /* A logged integer value. */
char *string; /* A malloced string or NULL. */
ksba_cert_t cert; /* A certifciate or NULL. */
- int have_err:1;
- int have_intvalue:1;
+ unsigned int have_err:1;
+ unsigned int have_intvalue:1;
};
typedef struct log_item_s *log_item_t;
diff --git a/common/b64dec.c b/common/b64dec.c
deleted file mode 100644
index 2904b0471..000000000
--- a/common/b64dec.c
+++ /dev/null
@@ -1,299 +0,0 @@
-/* b64dec.c - Simple Base64 decoder.
- * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
- * Copyright (C) 2008, 2011, 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 the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * 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 Lesser General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses/>.
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#include "i18n.h"
-#include "util.h"
-
-
-/* The reverse base-64 list used for base-64 decoding. */
-static unsigned char const asctobin[128] =
- {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
- 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
- 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
- 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
- 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
- 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
- };
-
-enum decoder_states
- {
- s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
- s_b64_0, s_b64_1, s_b64_2, s_b64_3,
- s_waitendtitle, s_waitend
- };
-
-
-
-/* Initialize the context for the base64 decoder. If TITLE is NULL a
- plain base64 decoding is done. If it is the empty string the
- decoder will skip everything until a "-----BEGIN " line has been
- seen, decoding ends at a "----END " line. */
-gpg_error_t
-b64dec_start (struct b64state *state, const char *title)
-{
- memset (state, 0, sizeof *state);
- if (title)
- {
- state->title = xtrystrdup (title);
- if (!state->title)
- state->lasterr = gpg_error_from_syserror ();
- else
- state->idx = s_init;
- }
- else
- state->idx = s_b64_0;
- return state->lasterr;
-}
-
-
-/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the
- new length of the buffer at R_NBYTES. */
-gpg_error_t
-b64dec_proc (struct b64state *state, void *buffer, size_t length,
- size_t *r_nbytes)
-{
- enum decoder_states ds = state->idx;
- unsigned char val = state->radbuf[0];
- int pos = state->quad_count;
- char *d, *s;
-
- if (state->lasterr)
- return state->lasterr;
-
- if (state->stop_seen)
- {
- *r_nbytes = 0;
- state->lasterr = gpg_error (GPG_ERR_EOF);
- xfree (state->title);
- state->title = NULL;
- return state->lasterr;
- }
-
- for (s=d=buffer; length && !state->stop_seen; length--, s++)
- {
- again:
- switch (ds)
- {
- case s_idle:
- if (*s == '\n')
- {
- ds = s_lfseen;
- pos = 0;
- }
- break;
- case s_init:
- ds = s_lfseen;
- /* fall through */
- case s_lfseen:
- if (*s != "-----BEGIN "[pos])
- {
- ds = s_idle;
- goto again;
- }
- else if (pos == 10)
- {
- pos = 0;
- ds = s_beginseen;
- }
- else
- pos++;
- break;
- case s_beginseen:
- if (*s != "PGP "[pos])
- ds = s_begin; /* Not a PGP armor. */
- else if (pos == 3)
- ds = s_waitheader;
- else
- pos++;
- break;
- case s_waitheader:
- if (*s == '\n')
- ds = s_waitblank;
- break;
- case s_waitblank:
- if (*s == '\n')
- ds = s_b64_0; /* blank line found. */
- else if (*s == ' ' || *s == '\r' || *s == '\t')
- ; /* Ignore spaces. */
- else
- {
- /* Armor header line. Note that we don't care that our
- * FSM accepts a header prefixed with spaces. */
- ds = s_waitheader; /* Wait for next header. */
- }
- break;
- case s_begin:
- if (*s == '\n')
- ds = s_b64_0;
- break;
- case s_b64_0:
- case s_b64_1:
- case s_b64_2:
- case s_b64_3:
- {
- int c;
-
- if (*s == '-' && state->title)
- {
- /* Not a valid Base64 character: assume end
- header. */
- ds = s_waitend;
- }
- else if (*s == '=')
- {
- /* Pad character: stop */
- if (ds == s_b64_1)
- *d++ = val;
- ds = state->title? s_waitendtitle : s_waitend;
- }
- else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
- ; /* Skip white spaces. */
- else if ( (*s & 0x80)
- || (c = asctobin[*(unsigned char *)s]) == 255)
- {
- /* Skip invalid encodings. */
- state->invalid_encoding = 1;
- }
- else if (ds == s_b64_0)
- {
- val = c << 2;
- ds = s_b64_1;
- }
- else if (ds == s_b64_1)
- {
- val |= (c>>4)&3;
- *d++ = val;
- val = (c<<4)&0xf0;
- ds = s_b64_2;
- }
- else if (ds == s_b64_2)
- {
- val |= (c>>2)&15;
- *d++ = val;
- val = (c<<6)&0xc0;
- ds = s_b64_3;
- }
- else
- {
- val |= c&0x3f;
- *d++ = val;
- ds = s_b64_0;
- }
- }
- break;
- case s_waitendtitle:
- if (*s == '-')
- ds = s_waitend;
- break;
- case s_waitend:
- if ( *s == '\n')
- state->stop_seen = 1;
- break;
- default:
- BUG();
- }
- }
-
-
- state->idx = ds;
- state->radbuf[0] = val;
- state->quad_count = pos;
- *r_nbytes = (d -(char*) buffer);
- return 0;
-}
-
-
-/* This function needs to be called before releasing the decoder
- state. It may return an error code in case an encoding error has
- been found during decoding. */
-gpg_error_t
-b64dec_finish (struct b64state *state)
-{
- xfree (state->title);
- state->title = NULL;
-
- if (state->lasterr)
- return state->lasterr;
-
- return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
-}
-
-
-/* Convert STRING consisting of base64 characters into its binary
- * representation and store the result in a newly allocated buffer at
- * R_BUFFER with its length at R_BUFLEN. If TITLE is NULL a plain
- * base64 decoding is done. If it is the empty string the decoder
- * will skip everything until a "-----BEGIN " line has been seen,
- * decoding then ends at a "----END " line. On failure the function
- * returns an error code and sets R_BUFFER to NULL. If the decoded
- * data has a length of 0 a dummy buffer will still be allocated and
- * the length is set to 0. */
-gpg_error_t
-b64decode (const char *string, const char *title,
- void **r_buffer, size_t *r_buflen)
-{
- gpg_error_t err;
- struct b64state state;
- size_t nbytes;
- char *buffer;
-
- *r_buffer = NULL;
- *r_buflen = 0;
-
- buffer = xtrystrdup (string);
- if (!buffer)
- return gpg_error_from_syserror();
-
- err = b64dec_start (&state, title);
- if (err)
- {
- xfree (buffer);
- return err;
- }
- b64dec_proc (&state, buffer, strlen (buffer), &nbytes);
- err = b64dec_finish (&state);
- if (err)
- xfree (buffer);
- else
- {
- *r_buffer = buffer;
- *r_buflen = nbytes;
- }
- return err;
-}
diff --git a/common/b64enc.c b/common/b64enc.c
deleted file mode 100644
index 7846dcb3e..000000000
--- a/common/b64enc.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/* b64enc.c - Simple Base64 encoder.
- * Copyright (C) 2001, 2003, 2004, 2008, 2010,
- * 2011 Free Software Foundation, Inc.
- * Copyright (C) 2001, 2003, 2004, 2008, 2010,
- * 2011 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 the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * 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 Lesser General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses/>.
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#include "i18n.h"
-#include "util.h"
-
-#define B64ENC_DID_HEADER 1
-#define B64ENC_DID_TRAILER 2
-#define B64ENC_NO_LINEFEEDS 16
-#define B64ENC_USE_PGPCRC 32
-
-/* The base-64 character list */
-static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
-
-/* Stuff required to create the OpenPGP CRC. This crc_table has been
- created using this code:
-
- #include <stdio.h>
- #include <stdint.h>
-
- #define CRCPOLY 0x864CFB
-
- int
- main (void)
- {
- int i, j;
- uint32_t t;
- uint32_t crc_table[256];
-
- crc_table[0] = 0;
- for (i=j=0; j < 128; j++ )
- {
- t = crc_table[j];
- if ( (t & 0x00800000) )
- {
- t <<= 1;
- crc_table[i++] = t ^ CRCPOLY;
- crc_table[i++] = t;
- }
- else
- {
- t <<= 1;
- crc_table[i++] = t;
- crc_table[i++] = t ^ CRCPOLY;
- }
- }
-
- puts ("static const u32 crc_table[256] = {");
- for (i=j=0; i < 256; i++)
- {
- printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]);
- if (i != 255)
- {
- putchar (',');
- if ( ++j > 5)
- {
- j = 0;
- putchar ('\n');
- }
- }
- }
- puts ("\n};");
- return 0;
- }
-*/
-#define CRCINIT 0xB704CE
-static const u32 crc_table[256] = {
- 0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a,
- 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf,
- 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272,
- 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e,
- 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa,
- 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f,
- 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b,
- 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7,
- 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a,
- 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af,
- 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29,
- 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5,
- 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1,
- 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad,
- 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099,
- 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375,
- 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821,
- 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4,
- 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049,
- 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5,
- 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791,
- 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52,
- 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66,
- 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a,
- 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337,
- 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2,
- 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6,
- 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a,
- 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e,
- 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132,
- 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506,
- 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea,
- 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c,
- 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9,
- 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604,
- 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8,
- 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc,
- 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69,
- 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d,
- 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1,
- 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c,
- 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9,
- 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538
-};
-
-
-static gpg_error_t
-enc_start (struct b64state *state, FILE *fp, estream_t stream,
- const char *title)
-{
- memset (state, 0, sizeof *state);
- state->fp = fp;
- state->stream = stream;
- state->lasterr = 0;
- if (title && !*title)
- state->flags |= B64ENC_NO_LINEFEEDS;
- else if (title)
- {
- if (!strncmp (title, "PGP ", 4))
- {
- state->flags |= B64ENC_USE_PGPCRC;
- state->crc = CRCINIT;
- }
- state->title = xtrystrdup (title);
- if (!state->title)
- state->lasterr = gpg_error_from_syserror ();
- }
- return state->lasterr;
-}
-
-
-/* Prepare for base-64 writing to the stream FP. If TITLE is not NULL
- and not an empty string, this string will be used as the title for
- the armor lines, with TITLE being an empty string, we don't write
- the header lines and furthermore even don't write any linefeeds.
- If TITLE starts with "PGP " the OpenPGP CRC checksum will be
- written as well. With TITLE being NULL, we merely don't write
- header but make sure that lines are not too long. Note, that we
- don't write any output unless at least one byte get written using
- b64enc_write. */
-gpg_error_t
-b64enc_start (struct b64state *state, FILE *fp, const char *title)
-{
- return enc_start (state, fp, NULL, title);
-}
-
-/* Same as b64enc_start but takes an estream. */
-gpg_error_t
-b64enc_start_es (struct b64state *state, estream_t fp, const char *title)
-{
- return enc_start (state, NULL, fp, title);
-}
-
-
-static int
-my_fputs (const char *string, struct b64state *state)
-{
- if (state->stream)
- return es_fputs (string, state->stream);
- else
- return fputs (string, state->fp);
-}
-
-
-/* Write NBYTES from BUFFER to the Base 64 stream identified by
- STATE. With BUFFER and NBYTES being 0, merely do a fflush on the
- stream. */
-gpg_error_t
-b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
-{
- unsigned char radbuf[4];
- int idx, quad_count;
- const unsigned char *p;
-
- if (state->lasterr)
- return state->lasterr;
-
- if (!nbytes)
- {
- if (buffer)
- if (state->stream? es_fflush (state->stream) : fflush (state->fp))
- goto write_error;
- return 0;
- }
-
- if (!(state->flags & B64ENC_DID_HEADER))
- {
- if (state->title)
- {
- if ( my_fputs ("-----BEGIN ", state) == EOF
- || my_fputs (state->title, state) == EOF
- || my_fputs ("-----\n", state) == EOF)
- goto write_error;
- if ( (state->flags & B64ENC_USE_PGPCRC)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
-
- state->flags |= B64ENC_DID_HEADER;
- }
-
- idx = state->idx;
- quad_count = state->quad_count;
- assert (idx < 4);
- memcpy (radbuf, state->radbuf, idx);
-
- if ( (state->flags & B64ENC_USE_PGPCRC) )
- {
- size_t n;
- u32 crc = state->crc;
-
- for (p=buffer, n=nbytes; n; p++, n-- )
- crc = ((u32)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p];
- state->crc = (crc & 0x00ffffff);
- }
-
- for (p=buffer; nbytes; p++, nbytes--)
- {
- radbuf[idx++] = *p;
- if (idx > 2)
- {
- char tmp[4];
-
- tmp[0] = bintoasc[(*radbuf >> 2) & 077];
- tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
- tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
- tmp[3] = bintoasc[radbuf[2]&077];
- if (state->stream)
- {
- for (idx=0; idx < 4; idx++)
- es_putc (tmp[idx], state->stream);
- idx = 0;
- if (es_ferror (state->stream))
- goto write_error;
- }
- else
- {
- for (idx=0; idx < 4; idx++)
- putc (tmp[idx], state->fp);
- idx = 0;
- if (ferror (state->fp))
- goto write_error;
- }
- if (++quad_count >= (64/4))
- {
- quad_count = 0;
- if (!(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
- }
- }
- memcpy (state->radbuf, radbuf, idx);
- state->idx = idx;
- state->quad_count = quad_count;
- return 0;
-
- write_error:
- state->lasterr = gpg_error_from_syserror ();
- if (state->title)
- {
- xfree (state->title);
- state->title = NULL;
- }
- return state->lasterr;
-}
-
-
-gpg_error_t
-b64enc_finish (struct b64state *state)
-{
- gpg_error_t err = 0;
- unsigned char radbuf[4];
- int idx, quad_count;
- char tmp[4];
-
- if (state->lasterr)
- return state->lasterr;
-
- if (!(state->flags & B64ENC_DID_HEADER))
- goto cleanup;
-
- /* Flush the base64 encoding */
- idx = state->idx;
- quad_count = state->quad_count;
- assert (idx < 4);
- memcpy (radbuf, state->radbuf, idx);
-
- if (idx)
- {
- tmp[0] = bintoasc[(*radbuf>>2)&077];
- if (idx == 1)
- {
- tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077];
- tmp[2] = '=';
- tmp[3] = '=';
- }
- else
- {
- tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
- tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077];
- tmp[3] = '=';
- }
- if (state->stream)
- {
- for (idx=0; idx < 4; idx++)
- es_putc (tmp[idx], state->stream);
- if (es_ferror (state->stream))
- goto write_error;
- }
- else
- {
- for (idx=0; idx < 4; idx++)
- putc (tmp[idx], state->fp);
- if (ferror (state->fp))
- goto write_error;
- }
-
- if (++quad_count >= (64/4))
- {
- quad_count = 0;
- if (!(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
- }
-
- /* Finish the last line and write the trailer. */
- if (quad_count
- && !(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
-
- if ( (state->flags & B64ENC_USE_PGPCRC) )
- {
- /* Write the CRC. */
- my_fputs ("=", state);
- radbuf[0] = state->crc >>16;
- radbuf[1] = state->crc >> 8;
- radbuf[2] = state->crc;
- tmp[0] = bintoasc[(*radbuf>>2)&077];
- tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
- tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
- tmp[3] = bintoasc[radbuf[2]&077];
- if (state->stream)
- {
- for (idx=0; idx < 4; idx++)
- es_putc (tmp[idx], state->stream);
- if (es_ferror (state->stream))
- goto write_error;
- }
- else
- {
- for (idx=0; idx < 4; idx++)
- putc (tmp[idx], state->fp);
- if (ferror (state->fp))
- goto write_error;
- }
- if (!(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
-
- if (state->title)
- {
- if ( my_fputs ("-----END ", state) == EOF
- || my_fputs (state->title, state) == EOF
- || my_fputs ("-----\n", state) == EOF)
- goto write_error;
- }
-
- goto cleanup;
-
- write_error:
- err = gpg_error_from_syserror ();
-
- cleanup:
- if (state->title)
- {
- xfree (state->title);
- state->title = NULL;
- }
- state->fp = NULL;
- state->stream = NULL;
- state->lasterr = err;
- return err;
-}
diff --git a/common/compliance.c b/common/compliance.c
index 84449af25..2df10d2e2 100644
--- a/common/compliance.c
+++ b/common/compliance.c
@@ -139,7 +139,7 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
gcry_mpi_t key[], unsigned int keylength,
const char *curvename)
{
- enum { is_rsa, is_dsa, is_elg, is_ecc } algotype;
+ enum { is_rsa, is_dsa, is_elg, is_ecc, is_kem } algotype;
int result = 0;
if (! initialized)
@@ -173,6 +173,10 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
case PUBKEY_ALGO_ELGAMAL:
return 0; /* Signing with Elgamal is not at all supported. */
+ case PUBKEY_ALGO_KYBER:
+ algotype = is_kem;
+ break;
+
default: /* Unknown. */
return 0;
}
@@ -227,6 +231,10 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo,
|| !strcmp (curvename, "brainpoolP512r1")));
break;
+ case is_kem:
+ result = 0;
+ break;
+
default:
result = 0;
}
diff --git a/common/dynload.h b/common/dynload.h
index 6ac7b4e17..af3906c81 100644
--- a/common/dynload.h
+++ b/common/dynload.h
@@ -34,12 +34,15 @@
#ifndef __MINGW32__
# include <dlfcn.h>
#else
-# include <errhandlingapi.h>
-# include <handleapi.h>
-# include <libloaderapi.h>
+# ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+# endif
+# include <windows.h>
# include "utf8conv.h"
# include "mischelp.h"
-# define RTLD_LAZY 0
+# ifndef RTLD_LAZY
+# define RTLD_LAZY 0
+# endif
static inline void *
dlopen (const char *name, int flag)
diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c
index fa613449d..d90b4e8c7 100644
--- a/common/exechelp-posix.c
+++ b/common/exechelp-posix.c
@@ -84,12 +84,6 @@ my_error_from_syserror (void)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
}
-static inline gpg_error_t
-my_error (int errcode)
-{
- return gpg_err_make (default_errsource, errcode);
-}
-
/* Return the maximum number of currently allowed open file
descriptors. Only useful on POSIX systems but returns a value on
@@ -273,73 +267,6 @@ get_all_open_fds (void)
}
-/* The exec core used right after the fork. This will never return. */
-static void
-do_exec (const char *pgmname, const char *argv[],
- int fd_in, int fd_out, int fd_err,
- int *except, unsigned int flags)
-{
- char **arg_list;
- int i, j;
- int fds[3];
- int nodevnull[3];
-
- fds[0] = fd_in;
- fds[1] = fd_out;
- fds[2] = fd_err;
-
- nodevnull[0] = !!(flags & GNUPG_SPAWN_KEEP_STDIN);
- nodevnull[1] = !!(flags & GNUPG_SPAWN_KEEP_STDOUT);
- nodevnull[2] = !!(flags & GNUPG_SPAWN_KEEP_STDERR);
-
- /* Create the command line argument array. */
- i = 0;
- if (argv)
- while (argv[i])
- i++;
- arg_list = xcalloc (i+2, sizeof *arg_list);
- arg_list[0] = strrchr (pgmname, '/');
- if (arg_list[0])
- arg_list[0]++;
- else
- arg_list[0] = xstrdup (pgmname);
- if (argv)
- for (i=0,j=1; argv[i]; i++, j++)
- arg_list[j] = (char*)argv[i];
-
- /* Assign /dev/null to unused FDs. */
- for (i=0; i <= 2; i++)
- {
- if (nodevnull[i])
- continue;
- if (fds[i] == -1)
- {
- fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY);
- if (fds[i] == -1)
- log_fatal ("failed to open '%s': %s\n",
- "/dev/null", strerror (errno));
- }
- }
-
- /* Connect the standard files. */
- for (i=0; i <= 2; i++)
- {
- if (nodevnull[i])
- continue;
- if (fds[i] != i && dup2 (fds[i], i) == -1)
- log_fatal ("dup2 std%s failed: %s\n",
- i==0?"in":i==1?"out":"err", strerror (errno));
- }
-
- /* Close all other files. */
- close_all_fds (3, except);
-
- execv (pgmname, arg_list);
- /* No way to print anything, as we have closed all streams. */
- _exit (127);
-}
-
-
static gpg_error_t
do_create_pipe (int filedes[2])
{
@@ -431,487 +358,669 @@ gnupg_close_pipe (int fd)
close (fd);
}
+#include <sys/socket.h>
-/* Fork and exec the PGMNAME, see exechelp.h for details. */
-gpg_error_t
-gnupg_spawn_process (const char *pgmname, const char *argv[],
- int *except, unsigned int flags,
- estream_t *r_infp,
- estream_t *r_outfp,
- estream_t *r_errfp,
- pid_t *pid)
-{
- gpg_error_t err;
- int inpipe[2] = {-1, -1};
- int outpipe[2] = {-1, -1};
- int errpipe[2] = {-1, -1};
- estream_t infp = NULL;
- estream_t outfp = NULL;
- estream_t errfp = NULL;
- int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+struct gnupg_process {
+ const char *pgmname;
+ unsigned int terminated :1; /* or detached */
+ unsigned int flags;
+ pid_t pid;
+ int fd_in;
+ int fd_out;
+ int fd_err;
+ int wstatus;
+};
- if (r_infp)
- *r_infp = NULL;
- if (r_outfp)
- *r_outfp = NULL;
- if (r_errfp)
- *r_errfp = NULL;
- *pid = (pid_t)(-1); /* Always required. */
+static int gnupg_process_syscall_func_initialized;
- if (r_infp)
- {
- err = create_pipe_and_estream (inpipe, &infp, 1, nonblock);
- if (err)
- return err;
- }
+/* Functions called before and after blocking syscalls. */
+static void (*pre_syscall_func) (void);
+static void (*post_syscall_func) (void);
- if (r_outfp)
+static void
+check_syscall_func (void)
+{
+ if (!gnupg_process_syscall_func_initialized)
{
- err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock);
- if (err)
- {
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != -1)
- close (inpipe[1]);
- if (inpipe[0] != -1)
- close (inpipe[0]);
-
- return err;
- }
+ gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func);
+ gnupg_process_syscall_func_initialized = 1;
}
+}
- if (r_errfp)
- {
- err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock);
- if (err)
- {
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != -1)
- close (inpipe[1]);
- if (inpipe[0] != -1)
- close (inpipe[0]);
-
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != -1)
- close (outpipe[0]);
- if (outpipe[1] != -1)
- close (outpipe[1]);
-
- return err;
- }
- }
+static void
+pre_syscall (void)
+{
+ if (pre_syscall_func)
+ pre_syscall_func ();
+}
+static void
+post_syscall (void)
+{
+ if (post_syscall_func)
+ post_syscall_func ();
+}
- *pid = fork ();
- if (*pid == (pid_t)(-1))
- {
- err = my_error_from_syserror ();
- log_error (_("error forking process: %s\n"), gpg_strerror (err));
-
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != -1)
- close (inpipe[1]);
- if (inpipe[0] != -1)
- close (inpipe[0]);
-
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != -1)
- close (outpipe[0]);
- if (outpipe[1] != -1)
- close (outpipe[1]);
-
- if (errfp)
- es_fclose (errfp);
- else if (errpipe[0] != -1)
- close (errpipe[0]);
- if (errpipe[1] != -1)
- close (errpipe[1]);
- return err;
- }
- if (!*pid)
+static gpg_err_code_t
+do_create_socketpair (int filedes[2])
+{
+ gpg_error_t err = 0;
+
+ pre_syscall ();
+ if (socketpair (AF_LOCAL, SOCK_STREAM, 0, filedes) == -1)
{
- /* This is the child. */
- gcry_control (GCRYCTL_TERM_SECMEM);
- es_fclose (infp);
- es_fclose (outfp);
- es_fclose (errfp);
- do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
- except, flags);
- /*NOTREACHED*/
+ err = gpg_err_code_from_syserror ();
+ filedes[0] = filedes[1] = -1;
}
+ post_syscall ();
- /* This is the parent. */
- if (inpipe[0] != -1)
- close (inpipe[0]);
- if (outpipe[1] != -1)
- close (outpipe[1]);
- if (errpipe[1] != -1)
- close (errpipe[1]);
+ return err;
+}
- if (r_infp)
- *r_infp = infp;
- if (r_outfp)
- *r_outfp = outfp;
- if (r_errfp)
- *r_errfp = errfp;
+static int
+posix_open_null (int for_write)
+{
+ int fd;
- return 0;
+ fd = open ("/dev/null", for_write? O_WRONLY : O_RDONLY);
+ if (fd == -1)
+ log_fatal ("failed to open '/dev/null': %s\n", strerror (errno));
+ return fd;
}
+static void
+call_spawn_cb (struct spawn_cb_arg *sca,
+ int fd_in, int fd_out, int fd_err,
+ void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg)
+{
+ sca->fds[0] = fd_in;
+ sca->fds[1] = fd_out;
+ sca->fds[2] = fd_err;
+ sca->except_fds = NULL;
+ sca->arg = spawn_cb_arg;
+ if (spawn_cb)
+ (*spawn_cb) (sca);
+}
+static void
+my_exec (const char *pgmname, const char *argv[], struct spawn_cb_arg *sca)
+{
+ int i;
-/* Simplified version of gnupg_spawn_process. This function forks and
- then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
- and ERRFD to stderr (any of them may be -1 to connect them to
- /dev/null). The arguments for the process are expected in the NULL
- terminated array ARGV. The program name itself should not be
- included there. Calling gnupg_wait_process is required.
+ /* Assign /dev/null to unused FDs. */
+ for (i = 0; i <= 2; i++)
+ if (sca->fds[i] == -1)
+ sca->fds[i] = posix_open_null (i);
- Returns 0 on success or an error code. */
-gpg_error_t
-gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
- int infd, int outfd, int errfd, pid_t *pid)
+ /* Connect the standard files. */
+ for (i = 0; i <= 2; i++)
+ if (sca->fds[i] != i)
+ {
+ if (dup2 (sca->fds[i], i) == -1)
+ log_fatal ("dup2 std%s failed: %s\n",
+ i==0?"in":i==1?"out":"err", strerror (errno));
+ /*
+ * We don't close sca.fds[i] here, but close them by
+ * close_all_fds. Note that there may be same one in three of
+ * sca->fds[i].
+ */
+ }
+
+ /* Close all other files. */
+ close_all_fds (3, sca->except_fds);
+
+ execv (pgmname, (char *const *)argv);
+ /* No way to print anything, as we have may have closed all streams. */
+ _exit (127);
+}
+
+static gpg_err_code_t
+spawn_detached (const char *pgmname, const char *argv[],
+ void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg)
{
- gpg_error_t err;
+ gpg_err_code_t ec;
+ pid_t pid;
- *pid = fork ();
- if (*pid == (pid_t)(-1))
+ /* FIXME: Is this GnuPG specific or should we keep it. */
+ if (getuid() != geteuid())
{
- err = my_error_from_syserror ();
- log_error (_("error forking process: %s\n"), strerror (errno));
- return err;
+ xfree (argv);
+ return GPG_ERR_BUG;
}
- if (!*pid)
+ if (access (pgmname, X_OK))
{
- gcry_control (GCRYCTL_TERM_SECMEM);
- /* Run child. */
- do_exec (pgmname, argv, infd, outfd, errfd, NULL, 0);
- /*NOTREACHED*/
+ ec = gpg_err_code_from_syserror ();
+ xfree (argv);
+ return ec;
}
- return 0;
-}
-
-
-
+ pre_syscall ();
+ pid = fork ();
+ post_syscall ();
+ if (pid == (pid_t)(-1))
+ {
+ ec = gpg_err_code_from_syserror ();
+ log_error (_("error forking process: %s\n"), gpg_strerror (ec));
+ xfree (argv);
+ return ec;
+ }
-/* Waiting for child processes.
+ if (!pid)
+ {
+ pid_t pid2;
+ struct spawn_cb_arg sca;
- waitpid(2) may return information about terminated children that we
- did not yet request, and there is no portable way to wait for a
- specific set of children.
+ if (setsid() == -1 || chdir ("/"))
+ _exit (1);
- As a workaround, we store the results of children for later use.
+ pid2 = fork (); /* Double fork to let init take over the new child. */
+ if (pid2 == (pid_t)(-1))
+ _exit (1);
+ if (pid2)
+ _exit (0); /* Let the parent exit immediately. */
- XXX: This assumes that PIDs are not reused too quickly. */
+ call_spawn_cb (&sca, -1, -1, -1, spawn_cb, spawn_cb_arg);
-struct terminated_child
-{
- pid_t pid;
- int exitcode;
- struct terminated_child *next;
-};
+ my_exec (pgmname, argv, &sca);
+ /*NOTREACHED*/
+ }
-struct terminated_child *terminated_children;
+ pre_syscall ();
+ if (waitpid (pid, NULL, 0) == -1)
+ {
+ post_syscall ();
+ ec = gpg_err_code_from_syserror ();
+ log_error ("waitpid failed in gpgrt_spawn_process_detached: %s",
+ gpg_strerror (ec));
+ return ec;
+ }
+ else
+ post_syscall ();
+ return 0;
+}
-static gpg_error_t
-store_result (pid_t pid, int exitcode)
+void
+gnupg_spawn_helper (struct spawn_cb_arg *sca)
{
- struct terminated_child *c;
+ int *user_except = sca->arg;
+ sca->except_fds = user_except;
+}
- c = xtrymalloc (sizeof *c);
- if (c == NULL)
- return gpg_err_code_from_syserror ();
+gpg_err_code_t
+gnupg_process_spawn (const char *pgmname, const char *argv1[],
+ unsigned int flags,
+ void (*spawn_cb) (struct spawn_cb_arg *),
+ void *spawn_cb_arg,
+ gnupg_process_t *r_process)
+{
+ gpg_err_code_t ec;
+ gnupg_process_t process;
+ int fd_in[2];
+ int fd_out[2];
+ int fd_err[2];
+ pid_t pid;
+ const char **argv;
+ int i, j;
- c->pid = pid;
- c->exitcode = exitcode;
- c->next = terminated_children;
- terminated_children = c;
+ check_syscall_func ();
- return 0;
-}
+ if (r_process)
+ *r_process = NULL;
+ /* Create the command line argument array. */
+ i = 0;
+ if (argv1)
+ while (argv1[i])
+ i++;
+ argv = xtrycalloc (i+2, sizeof *argv);
+ if (!argv)
+ return gpg_err_code_from_syserror ();
+ argv[0] = strrchr (pgmname, '/');
+ if (argv[0])
+ argv[0]++;
+ else
+ argv[0] = pgmname;
-static int
-get_result (pid_t pid, int *r_exitcode)
-{
- struct terminated_child *c, **prevp;
+ if (argv1)
+ for (i=0, j=1; argv1[i]; i++, j++)
+ argv[j] = argv1[i];
- for (prevp = &terminated_children, c = terminated_children;
- c;
- prevp = &c->next, c = c->next)
- if (c->pid == pid)
- {
- *prevp = c->next;
- *r_exitcode = c->exitcode;
- xfree (c);
- return 1;
- }
+ if ((flags & GNUPG_PROCESS_DETACHED))
+ {
+ if ((flags & GNUPG_PROCESS_STDFDS_SETTING))
+ {
+ xfree (argv);
+ return GPG_ERR_INV_FLAG;
+ }
- return 0;
-}
+ /* In detached case, it must be no R_PROCESS. */
+ if (r_process)
+ {
+ xfree (argv);
+ return GPG_ERR_INV_ARG;
+ }
+ return spawn_detached (pgmname, argv, spawn_cb, spawn_cb_arg);
+ }
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
-{
- gpg_err_code_t ec;
- int i, status;
+ process = xtrycalloc (1, sizeof (struct gnupg_process));
+ if (process == NULL)
+ {
+ xfree (argv);
+ return gpg_err_code_from_syserror ();
+ }
- if (r_exitcode)
- *r_exitcode = -1;
+ process->pgmname = pgmname;
+ process->flags = flags;
- if (pid == (pid_t)(-1))
- return gpg_error (GPG_ERR_INV_VALUE);
+ if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR))
+ {
+ ec = do_create_socketpair (fd_in);
+ if (ec)
+ {
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
+ fd_out[0] = dup (fd_in[0]);
+ fd_out[1] = dup (fd_in[1]);
+ }
+ else
+ {
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ {
+ ec = do_create_pipe (fd_in);
+ if (ec)
+ {
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDIN_KEEP))
+ {
+ fd_in[0] = 0;
+ fd_in[1] = -1;
+ }
+ else
+ {
+ fd_in[0] = -1;
+ fd_in[1] = -1;
+ }
-#ifdef USE_NPTH
- i = npth_waitpid (pid, &status, hang? 0:WNOHANG);
-#else
- while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
- && errno == EINTR);
-#endif
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ {
+ ec = do_create_pipe (fd_out);
+ if (ec)
+ {
+ if (fd_in[0] >= 0 && fd_in[0] != 0)
+ close (fd_in[0]);
+ if (fd_in[1] >= 0)
+ close (fd_in[1]);
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDOUT_KEEP))
+ {
+ fd_out[0] = -1;
+ fd_out[1] = 1;
+ }
+ else
+ {
+ fd_out[0] = -1;
+ fd_out[1] = -1;
+ }
+ }
- if (i == (pid_t)(-1))
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
{
- ec = gpg_err_code_from_errno (errno);
- log_error (_("waiting for process %d to terminate failed: %s\n"),
- (int)pid, strerror (errno));
+ ec = do_create_pipe (fd_err);
+ if (ec)
+ {
+ if (fd_in[0] >= 0 && fd_in[0] != 0)
+ close (fd_in[0]);
+ if (fd_in[1] >= 0)
+ close (fd_in[1]);
+ if (fd_out[0] >= 0)
+ close (fd_out[0]);
+ if (fd_out[1] >= 0 && fd_out[1] != 1)
+ close (fd_out[1]);
+ xfree (process);
+ xfree (argv);
+ return ec;
+ }
}
- else if (!i)
+ else if ((flags & GNUPG_PROCESS_STDERR_KEEP))
{
- ec = GPG_ERR_TIMEOUT; /* Still running. */
+ fd_err[0] = -1;
+ fd_err[1] = 2;
}
- else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
+ else
{
- log_error (_("error running '%s': probably not installed\n"), pgmname);
- ec = GPG_ERR_CONFIGURATION;
+ fd_err[0] = -1;
+ fd_err[1] = -1;
}
- else if (WIFEXITED (status) && WEXITSTATUS (status))
+
+ pre_syscall ();
+ pid = fork ();
+ post_syscall ();
+ if (pid == (pid_t)(-1))
{
- if (!r_exitcode)
- log_error (_("error running '%s': exit status %d\n"), pgmname,
- WEXITSTATUS (status));
- else
- *r_exitcode = WEXITSTATUS (status);
- ec = GPG_ERR_GENERAL;
+ ec = gpg_err_code_from_syserror ();
+ log_error (_("error forking process: %s\n"), gpg_strerror (ec));
+ if (fd_in[0] >= 0 && fd_in[0] != 0)
+ close (fd_in[0]);
+ if (fd_in[1] >= 0)
+ close (fd_in[1]);
+ if (fd_out[0] >= 0)
+ close (fd_out[0]);
+ if (fd_out[1] >= 0 && fd_out[1] != 1)
+ close (fd_out[1]);
+ if (fd_err[0] >= 0)
+ close (fd_err[0]);
+ if (fd_err[1] >= 0 && fd_err[1] != 2)
+ close (fd_err[1]);
+ xfree (process);
+ xfree (argv);
+ return ec;
}
- else if (!WIFEXITED (status))
+
+ if (!pid)
{
- log_error (_("error running '%s': terminated\n"), pgmname);
- ec = GPG_ERR_GENERAL;
+ struct spawn_cb_arg sca;
+
+ if (fd_in[1] >= 0)
+ close (fd_in[1]);
+ if (fd_out[0] >= 0)
+ close (fd_out[0]);
+ if (fd_err[0] >= 0)
+ close (fd_err[0]);
+
+ call_spawn_cb (&sca, fd_in[0], fd_out[1], fd_err[1],
+ spawn_cb, spawn_cb_arg);
+
+ /* Run child. */
+ my_exec (pgmname, argv, &sca);
+ /*NOTREACHED*/
}
- else
+
+ xfree (argv);
+ process->pid = pid;
+
+ if (fd_in[0] >= 0 && fd_in[0] != 0)
+ close (fd_in[0]);
+ if (fd_out[1] >= 0 && fd_out[1] != 1)
+ close (fd_out[1]);
+ if (fd_err[1] >= 0 && fd_err[1] != 2)
+ close (fd_err[1]);
+ process->fd_in = fd_in[1];
+ process->fd_out = fd_out[0];
+ process->fd_err = fd_err[0];
+ process->wstatus = -1;
+ process->terminated = 0;
+
+ if (r_process == NULL)
{
- if (r_exitcode)
- *r_exitcode = 0;
- ec = 0;
+ ec = gnupg_process_wait (process, 1);
+ gnupg_process_release (process);
+ return ec;
}
- return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+ *r_process = process;
+ return 0;
}
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
- int hang, int *r_exitcodes)
+static gpg_err_code_t
+process_kill (gnupg_process_t process, int sig)
{
gpg_err_code_t ec = 0;
- size_t i, left;
- int *dummy = NULL;
+ pid_t pid = process->pid;
+
+ pre_syscall ();
+ if (kill (pid, sig) < 0)
+ ec = gpg_err_code_from_syserror ();
+ post_syscall ();
+ return ec;
+}
+
+gpg_err_code_t
+gnupg_process_terminate (gnupg_process_t process)
+{
+ return process_kill (process, SIGTERM);
+}
- if (r_exitcodes == NULL)
+gpg_err_code_t
+gnupg_process_get_fds (gnupg_process_t process, unsigned int flags,
+ int *r_fd_in, int *r_fd_out, int *r_fd_err)
+{
+ (void)flags;
+ if (r_fd_in)
{
- dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count);
- if (dummy == NULL)
- return gpg_err_code_from_syserror ();
+ *r_fd_in = process->fd_in;
+ process->fd_in = -1;
}
-
- for (i = 0, left = count; i < count; i++)
+ if (r_fd_out)
+ {
+ *r_fd_out = process->fd_out;
+ process->fd_out = -1;
+ }
+ if (r_fd_err)
{
- int status = -1;
+ *r_fd_err = process->fd_err;
+ process->fd_err = -1;
+ }
- /* Skip invalid PID. */
- if (pids[i] == (pid_t)(-1))
- {
- r_exitcodes[i] = -1;
- left -= 1;
- continue;
- }
+ return 0;
+}
- /* See if there was a previously stored result for this pid. */
- if (get_result (pids[i], &status))
- left -= 1;
+gpg_err_code_t
+gnupg_process_get_streams (gnupg_process_t process, unsigned int flags,
+ gpgrt_stream_t *r_fp_in, gpgrt_stream_t *r_fp_out,
+ gpgrt_stream_t *r_fp_err)
+{
+ int nonblock = (flags & GNUPG_PROCESS_STREAM_NONBLOCK)? 1: 0;
- r_exitcodes[i] = status;
+ if (r_fp_in)
+ {
+ *r_fp_in = es_fdopen (process->fd_in, nonblock? "w,nonblock" : "w");
+ process->fd_in = -1;
}
+ if (r_fp_out)
+ {
+ *r_fp_out = es_fdopen (process->fd_out, nonblock? "r,nonblock" : "r");
+ process->fd_out = -1;
+ }
+ if (r_fp_err)
+ {
+ *r_fp_err = es_fdopen (process->fd_err, nonblock? "r,nonblock" : "r");
+ process->fd_err = -1;
+ }
+ return 0;
+}
- while (left > 0)
+static gpg_err_code_t
+process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr)
+{
+ switch (request)
{
- pid_t pid;
- int status;
+ case GNUPG_PROCESS_NOP:
+ return 0;
-#ifdef USE_NPTH
- pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG);
-#else
- while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1)
- && errno == EINTR);
-#endif
+ case GNUPG_PROCESS_GET_PROC_ID:
+ {
+ int *r_id = va_arg (arg_ptr, int *);
- if (pid == (pid_t)(-1))
- {
- ec = gpg_err_code_from_errno (errno);
- log_error (_("waiting for processes to terminate failed: %s\n"),
- strerror (errno));
- break;
- }
- else if (!pid)
- {
- ec = GPG_ERR_TIMEOUT; /* Still running. */
- break;
- }
- else
- {
- for (i = 0; i < count; i++)
- if (pid == pids[i])
- break;
+ if (r_id == NULL)
+ return GPG_ERR_INV_VALUE;
- if (i == count)
- {
- /* No match, store this result. */
- ec = store_result (pid, status);
- if (ec)
- break;
- continue;
- }
+ *r_id = (int)process->pid;
+ return 0;
+ }
- /* Process PIDS[i] died. */
- if (r_exitcodes[i] != (pid_t) -1)
- {
- log_error ("PID %d was reused", pid);
- ec = GPG_ERR_GENERAL;
- break;
- }
+ case GNUPG_PROCESS_GET_EXIT_ID:
+ {
+ int status = process->wstatus;
+ int *r_exit_status = va_arg (arg_ptr, int *);
- left -= 1;
- r_exitcodes[i] = status;
- }
- }
+ if (!process->terminated)
+ return GPG_ERR_UNFINISHED;
- for (i = 0; i < count; i++)
- {
- if (r_exitcodes[i] == -1)
- continue;
+ if (WIFEXITED (status))
+ {
+ if (r_exit_status)
+ *r_exit_status = WEXITSTATUS (status);
+ }
+ else
+ *r_exit_status = -1;
- if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
- {
- log_error (_("error running '%s': probably not installed\n"),
- pgmnames[i]);
- ec = GPG_ERR_CONFIGURATION;
- }
- else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
- {
- if (dummy)
- log_error (_("error running '%s': exit status %d\n"),
- pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
- else
- r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
- ec = GPG_ERR_GENERAL;
- }
- else if (!WIFEXITED (r_exitcodes[i]))
- {
- log_error (_("error running '%s': terminated\n"), pgmnames[i]);
- ec = GPG_ERR_GENERAL;
- }
+ return 0;
+ }
+
+ case GNUPG_PROCESS_GET_PID:
+ {
+ pid_t *r_pid = va_arg (arg_ptr, pid_t *);
+
+ if (r_pid == NULL)
+ return GPG_ERR_INV_VALUE;
+
+ *r_pid = process->pid;
+ return 0;
+ }
+
+ case GNUPG_PROCESS_GET_WSTATUS:
+ {
+ int status = process->wstatus;
+ int *r_if_exited = va_arg (arg_ptr, int *);
+ int *r_if_signaled = va_arg (arg_ptr, int *);
+ int *r_exit_status = va_arg (arg_ptr, int *);
+ int *r_termsig = va_arg (arg_ptr, int *);
+
+ if (!process->terminated)
+ return GPG_ERR_UNFINISHED;
+
+ if (WIFEXITED (status))
+ {
+ if (r_if_exited)
+ *r_if_exited = 1;
+ if (r_if_signaled)
+ *r_if_signaled = 0;
+ if (r_exit_status)
+ *r_exit_status = WEXITSTATUS (status);
+ if (r_termsig)
+ *r_termsig = 0;
+ }
+ else if (WIFSIGNALED (status))
+ {
+ if (r_if_exited)
+ *r_if_exited = 0;
+ if (r_if_signaled)
+ *r_if_signaled = 1;
+ if (r_exit_status)
+ *r_exit_status = 0;
+ if (r_termsig)
+ *r_termsig = WTERMSIG (status);
+ }
+
+ return 0;
+ }
+
+ case GNUPG_PROCESS_KILL:
+ {
+ int sig = va_arg (arg_ptr, int);
+
+ return process_kill (process, sig);
+ }
+
+ default:
+ break;
}
- xfree (dummy);
- return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+ return GPG_ERR_UNKNOWN_COMMAND;
}
-
-
-void
-gnupg_release_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_ctl (gnupg_process_t process, unsigned int request, ...)
{
- (void)pid;
-}
+ va_list arg_ptr;
+ gpg_err_code_t ec;
+ va_start (arg_ptr, request);
+ ec = process_vctl (process, request, arg_ptr);
+ va_end (arg_ptr);
+ return ec;
+}
-/* Spawn a new process and immediately detach from it. The name of
- the program to exec is PGMNAME and its arguments are in ARGV (the
- programname is automatically passed as first argument).
- Environment strings in ENVP are set. An error is returned if
- pgmname is not executable; to make this work it is necessary to
- provide an absolute file name. All standard file descriptors are
- connected to /dev/null. */
-gpg_error_t
-gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
- const char *envp[] )
+gpg_err_code_t
+gnupg_process_wait (gnupg_process_t process, int hang)
{
gpg_err_code_t ec;
+ int status;
pid_t pid;
- int i;
- if (getuid() != geteuid())
- return my_error (GPG_ERR_BUG);
+ if (process->terminated)
+ /* Already terminated. */
+ return 0;
- if ((ec = gnupg_access (pgmname, X_OK)))
- return gpg_err_make (default_errsource, ec);
+ pre_syscall ();
+ while ((pid = waitpid (process->pid, &status, hang? 0: WNOHANG))
+ == (pid_t)(-1) && errno == EINTR);
+ post_syscall ();
- pid = fork ();
if (pid == (pid_t)(-1))
{
- log_error (_("error forking process: %s\n"), strerror (errno));
- return my_error_from_syserror ();
+ ec = gpg_err_code_from_syserror ();
+ log_error (_("waiting for process %d to terminate failed: %s\n"),
+ (int)pid, gpg_strerror (ec));
}
- if (!pid)
+ else if (!pid)
{
- pid_t pid2;
-
- gcry_control (GCRYCTL_TERM_SECMEM);
- if (setsid() == -1 || chdir ("/"))
- _exit (1);
-
- pid2 = fork (); /* Double fork to let init take over the new child. */
- if (pid2 == (pid_t)(-1))
- _exit (1);
- if (pid2)
- _exit (0); /* Let the parent exit immediately. */
+ ec = GPG_ERR_TIMEOUT; /* Still running. */
+ }
+ else
+ {
+ process->terminated = 1;
+ process->wstatus = status;
+ ec = 0;
+ }
- if (envp)
- for (i=0; envp[i]; i++)
- putenv (xstrdup (envp[i]));
+ return ec;
+}
- do_exec (pgmname, argv, -1, -1, -1, NULL, 0);
+void
+gnupg_process_release (gnupg_process_t process)
+{
+ if (!process)
+ return;
- /*NOTREACHED*/
+ if (process->terminated)
+ {
+ gnupg_process_terminate (process);
+ gnupg_process_wait (process, 1);
}
- if (waitpid (pid, NULL, 0) == -1)
- log_error ("waitpid failed in gnupg_spawn_process_detached: %s",
- strerror (errno));
-
- return 0;
+ xfree (process);
}
-
-/* Kill a process; that is send an appropriate signal to the process.
- gnupg_wait_process must be called to actually remove the process
- from the system. An invalid PID is ignored. */
-void
-gnupg_kill_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang)
{
- if (pid != (pid_t)(-1))
+ gpg_err_code_t ec = 0;
+ int i;
+
+ for (i = 0; i < count; i++)
{
- kill (pid, SIGTERM);
+ if (process_list[i]->terminated)
+ continue;
+
+ ec = gnupg_process_wait (process_list[i], hang);
+ if (ec)
+ break;
}
+
+ return ec;
}
diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c
index 2093f0d8b..08290e442 100644
--- a/common/exechelp-w32.c
+++ b/common/exechelp-w32.c
@@ -33,6 +33,8 @@
#if !defined(HAVE_W32_SYSTEM)
#error This code is only used on W32.
+#else
+#define _WIN32_WINNT 0x600
#endif
#include <stdio.h>
@@ -63,9 +65,11 @@
#include "util.h"
#include "i18n.h"
#include "sysutils.h"
+#define NEED_STRUCT_SPAWN_CB_ARG
#include "exechelp.h"
#include <windows.h>
+#include <processthreadsapi.h>
/* Define to 1 do enable debugging. */
#define DEBUG_W32_SPAWN 0
@@ -405,143 +409,147 @@ gnupg_close_pipe (int fd)
if (fd != -1)
close (fd);
}
+
+struct gnupg_process {
+ const char *pgmname;
+ unsigned int terminated :1; /* or detached */
+ unsigned int flags;
+ HANDLE hProcess;
+ HANDLE hd_in;
+ HANDLE hd_out;
+ HANDLE hd_err;
+ int exitcode;
+};
+
+static int gnupg_process_syscall_func_initialized;
+
+/* Functions called before and after blocking syscalls. */
+static void (*pre_syscall_func) (void);
+static void (*post_syscall_func) (void);
+
+static void
+check_syscall_func (void)
+{
+ if (!gnupg_process_syscall_func_initialized)
+ {
+ gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func);
+ gnupg_process_syscall_func_initialized = 1;
+ }
+}
-/* Fork and exec the PGMNAME, see exechelp.h for details. */
-gpg_error_t
-gnupg_spawn_process (const char *pgmname, const char *argv[],
- int *except, unsigned int flags,
- estream_t *r_infp,
- estream_t *r_outfp,
- estream_t *r_errfp,
- pid_t *pid)
+static void
+pre_syscall (void)
{
- gpg_error_t err;
- SECURITY_ATTRIBUTES sec_attr;
- PROCESS_INFORMATION pi =
- {
- NULL, /* Returns process handle. */
- 0, /* Returns primary thread handle. */
- 0, /* Returns pid. */
- 0 /* Returns tid. */
- };
- STARTUPINFOW si;
- int cr_flags;
- char *cmdline;
- wchar_t *wcmdline = NULL;
- wchar_t *wpgmname = NULL;
- HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
- estream_t infp = NULL;
- estream_t outfp = NULL;
- estream_t errfp = NULL;
- HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
- INVALID_HANDLE_VALUE,
- INVALID_HANDLE_VALUE};
- int i, rc;
- es_syshd_t syshd;
- gpg_err_source_t errsource = default_errsource;
- int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+ if (pre_syscall_func)
+ pre_syscall_func ();
+}
+
- (void)except; /* Not yet used. */
+static void
+post_syscall (void)
+{
+ if (post_syscall_func)
+ post_syscall_func ();
+}
- if (r_infp)
- *r_infp = NULL;
- if (r_outfp)
- *r_outfp = NULL;
- if (r_errfp)
- *r_errfp = NULL;
- *pid = (pid_t)(-1); /* Always required. */
- if (r_infp)
+/*
+ * Check if STARTUPINFOEXW supports PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ */
+static int
+check_windows_version (void)
+{
+ static int is_vista_or_later = -1;
+
+ OSVERSIONINFO osvi;
+
+ if (is_vista_or_later == -1)
{
- if (create_inheritable_pipe (inpipe, INHERIT_READ))
- {
- err = gpg_err_make (errsource, GPG_ERR_GENERAL);
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- return err;
- }
+ memset (&osvi,0,sizeof(osvi));
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ GetVersionEx (&osvi);
- syshd.type = ES_SYSHD_HANDLE;
- syshd.u.handle = inpipe[1];
- infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
- if (!infp)
- {
- err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
- log_error (_("error creating a stream for a pipe: %s\n"),
- gpg_strerror (err));
- CloseHandle (inpipe[0]);
- CloseHandle (inpipe[1]);
- inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
- return err;
- }
+ /* The feature is available on Vista or later. */
+ is_vista_or_later = (osvi.dwMajorVersion >= 6);
}
- if (r_outfp)
- {
- if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
- {
- err = gpg_err_make (errsource, GPG_ERR_GENERAL);
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- return err;
- }
+ return is_vista_or_later;
+}
- syshd.type = ES_SYSHD_HANDLE;
- syshd.u.handle = outpipe[0];
- outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
- if (!outfp)
- {
- err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
- log_error (_("error creating a stream for a pipe: %s\n"),
- gpg_strerror (err));
- CloseHandle (outpipe[0]);
- CloseHandle (outpipe[1]);
- outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[1]);
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- return err;
- }
+
+static gpg_err_code_t
+spawn_detached (const char *pgmname, char *cmdline,
+ void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg)
+{
+ SECURITY_ATTRIBUTES sec_attr;
+ PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
+ STARTUPINFOEXW si;
+ int cr_flags;
+ wchar_t *wcmdline = NULL;
+ wchar_t *wpgmname = NULL;
+ gpg_err_code_t ec;
+ int ret;
+ struct spawn_cb_arg sca;
+ BOOL ask_inherit = FALSE;
+
+ ec = gnupg_access (pgmname, X_OK);
+ if (ec)
+ {
+ xfree (cmdline);
+ return ec;
}
- if (r_errfp)
+ memset (&si, 0, sizeof si);
+
+ sca.allow_foreground_window = FALSE;
+ sca.hd[0] = INVALID_HANDLE_VALUE;
+ sca.hd[1] = INVALID_HANDLE_VALUE;
+ sca.hd[2] = INVALID_HANDLE_VALUE;
+ sca.inherit_hds = NULL;
+ sca.arg = spawn_cb_arg;
+ if (spawn_cb)
+ (*spawn_cb) (&sca);
+
+ if (sca.inherit_hds)
{
- if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
- {
- err = gpg_err_make (errsource, GPG_ERR_GENERAL);
- log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
- return err;
- }
+ SIZE_T attr_list_size = 0;
+ HANDLE hd[16];
+ HANDLE *hd_p = sca.inherit_hds;
+ int j = 0;
- syshd.type = ES_SYSHD_HANDLE;
- syshd.u.handle = errpipe[0];
- errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
- if (!errfp)
+ if (hd_p)
{
- err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
- log_error (_("error creating a stream for a pipe: %s\n"),
- gpg_strerror (err));
- CloseHandle (errpipe[0]);
- CloseHandle (errpipe[1]);
- errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[0]);
- if (outpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[1]);
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- return err;
+ while (*hd_p != INVALID_HANDLE_VALUE)
+ if (j < DIM (hd))
+ hd[j++] = *hd_p++;
+ else
+ {
+ log_error ("Too much handles\n");
+ break;
+ }
}
+
+ if (j)
+ {
+ if (check_windows_version ())
+ {
+ InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size);
+ si.lpAttributeList = xtrymalloc (attr_list_size);
+ if (si.lpAttributeList == NULL)
+ {
+ xfree (cmdline);
+ return gpg_err_code_from_syserror ();
+ }
+ InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0,
+ &attr_list_size);
+ UpdateProcThreadAttribute (si.lpAttributeList, 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ hd, sizeof (HANDLE) * j, NULL, NULL);
+ }
+
+ ask_inherit = TRUE;
+ }
}
/* Prepare security attributes. */
@@ -549,502 +557,683 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
- /* Build the command line. */
- err = build_w32_commandline (pgmname, argv, &cmdline);
- if (err)
- return err;
-
- if (inpipe[0] == INVALID_HANDLE_VALUE)
- nullhd[0] = ((flags & GNUPG_SPAWN_KEEP_STDIN)?
- GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0));
- if (outpipe[1] == INVALID_HANDLE_VALUE)
- nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)?
- GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1));
- if (errpipe[1] == INVALID_HANDLE_VALUE)
- nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDERR)?
- GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1));
-
- memset (&si, 0, sizeof si);
- si.cb = sizeof (si);
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE;
- si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
- si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
- si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
+ /* Start the process. */
+ si.StartupInfo.cb = sizeof (si);
+ si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
+ si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
cr_flags = (CREATE_DEFAULT_ERROR_MODE
- | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
| GetPriorityClass (GetCurrentProcess ())
- | CREATE_SUSPENDED);
- /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", */
- /* pgmname, cmdline); */
+ | CREATE_NEW_PROCESS_GROUP
+ | DETACHED_PROCESS);
+
/* Take care: CreateProcessW may modify wpgmname */
if (!(wpgmname = utf8_to_wchar (pgmname)))
- rc = 0;
+ ret = 0;
else if (!(wcmdline = utf8_to_wchar (cmdline)))
- rc = 0;
+ ret = 0;
else
- rc = CreateProcessW (wpgmname, /* Program to start. */
- wcmdline, /* Command line arguments. */
- &sec_attr, /* Process security attributes. */
- &sec_attr, /* Thread security attributes. */
- TRUE, /* Inherit handles. */
- cr_flags, /* Creation flags. */
- NULL, /* Environment. */
- NULL, /* Use current drive/directory. */
- &si, /* Startup information. */
- &pi /* Returns process information. */
- );
- if (!rc)
+ ret = CreateProcessW (wpgmname, /* Program to start. */
+ wcmdline, /* Command line arguments. */
+ &sec_attr, /* Process security attributes. */
+ &sec_attr, /* Thread security attributes. */
+ ask_inherit, /* Inherit handles. */
+ cr_flags, /* Creation flags. */
+ NULL, /* Environment. */
+ NULL, /* Use current drive/directory. */
+ (STARTUPINFOW *)&si, /* Startup information. */
+ &pi /* Returns process information. */
+ );
+ if (!ret)
{
if (!wpgmname || !wcmdline)
log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
strerror (errno));
else
- log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
+ log_error ("CreateProcess(detached) failed: %d\n",
+ (int)GetLastError ());
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
- if (infp)
- es_fclose (infp);
- else if (inpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- if (outfp)
- es_fclose (outfp);
- else if (outpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[0]);
- if (outpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (errfp)
- es_fclose (errfp);
- else if (errpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (errpipe[0]);
- if (errpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (errpipe[1]);
- return gpg_err_make (errsource, GPG_ERR_GENERAL);
+ return GPG_ERR_GENERAL;
}
+ if (si.lpAttributeList)
+ DeleteProcThreadAttributeList (si.lpAttributeList);
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
- cmdline = NULL;
- /* Close the inherited handles to /dev/null. */
- for (i=0; i < DIM (nullhd); i++)
- if (nullhd[i] != INVALID_HANDLE_VALUE)
- CloseHandle (nullhd[i]);
+ /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
+ /* " dwProcessID=%d dwThreadId=%d\n", */
+ /* pi.hProcess, pi.hThread, */
+ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- /* Close the inherited ends of the pipes. */
- if (inpipe[0] != INVALID_HANDLE_VALUE)
- CloseHandle (inpipe[0]);
- if (outpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (outpipe[1]);
- if (errpipe[1] != INVALID_HANDLE_VALUE)
- CloseHandle (errpipe[1]);
-
- /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
- /* " dwProcessID=%d dwThreadId=%d\n", */
- /* pi.hProcess, pi.hThread, */
- /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */
+ /* Note: AllowSetForegroundWindow doesn't make sense for background
+ process. */
- /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
- invalid argument error if we pass it the correct processID. As a
- workaround we use -1 (ASFW_ANY). */
- if ((flags & GNUPG_SPAWN_RUN_ASFW))
- gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
-
- /* Process has been created suspended; resume it now. */
- ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
-
- if (r_infp)
- *r_infp = infp;
- if (r_outfp)
- *r_outfp = outfp;
- if (r_errfp)
- *r_errfp = errfp;
-
- *pid = handle_to_pid (pi.hProcess);
+ CloseHandle (pi.hProcess);
return 0;
-
}
+void
+gnupg_spawn_helper (struct spawn_cb_arg *sca)
+{
+ HANDLE *user_except = sca->arg;
+ sca->inherit_hds = user_except;
+}
-/* Simplified version of gnupg_spawn_process. This function forks and
- then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
- and ERRFD to stderr (any of them may be -1 to connect them to
- /dev/null). The arguments for the process are expected in the NULL
- terminated array ARGV. The program name itself should not be
- included there. Calling gnupg_wait_process is required.
-
- Returns 0 on success or an error code. */
-gpg_error_t
-gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
- int infd, int outfd, int errfd, pid_t *pid)
+gpg_err_code_t
+gnupg_process_spawn (const char *pgmname, const char *argv[],
+ unsigned int flags,
+ void (*spawn_cb) (struct spawn_cb_arg *),
+ void *spawn_cb_arg,
+ gnupg_process_t *r_process)
{
- gpg_error_t err;
+ gpg_err_code_t ec;
+ gnupg_process_t process;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
- STARTUPINFOW si;
+ STARTUPINFOEXW si;
+ int cr_flags;
char *cmdline;
wchar_t *wcmdline = NULL;
wchar_t *wpgmname = NULL;
- int i, rc;
- HANDLE stdhd[3];
+ int ret;
+ HANDLE hd_in[2];
+ HANDLE hd_out[2];
+ HANDLE hd_err[2];
+ struct spawn_cb_arg sca;
+ int i;
+ BOOL ask_inherit = FALSE;
+
+ check_syscall_func ();
+
+ /* Build the command line. */
+ ec = build_w32_commandline (pgmname, argv, &cmdline);
+ if (ec)
+ return ec;
+
+ if ((flags & GNUPG_PROCESS_DETACHED))
+ {
+ if ((flags & GNUPG_PROCESS_STDFDS_SETTING))
+ {
+ xfree (cmdline);
+ return GPG_ERR_INV_FLAG;
+ }
- /* Setup return values. */
- *pid = (pid_t)(-1);
+ /* In detached case, it must be no R_PROCESS. */
+ if (r_process)
+ {
+ xfree (cmdline);
+ return GPG_ERR_INV_ARG;
+ }
+
+ return spawn_detached (pgmname, cmdline, spawn_cb, spawn_cb_arg);
+ }
+
+ if (r_process)
+ *r_process = NULL;
+
+ process = xtrymalloc (sizeof (struct gnupg_process));
+ if (process == NULL)
+ {
+ xfree (cmdline);
+ return gpg_err_code_from_syserror ();
+ }
+
+ process->pgmname = pgmname;
+ process->flags = flags;
+
+ if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR))
+ {
+ xfree (process);
+ xfree (cmdline);
+ return GPG_ERR_NOT_SUPPORTED;
+ }
+
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ {
+ ec = create_inheritable_pipe (hd_in, INHERIT_READ);
+ if (ec)
+ {
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDIN_KEEP))
+ {
+ hd_in[0] = GetStdHandle (STD_INPUT_HANDLE);
+ hd_in[1] = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ hd_in[0] = w32_open_null (0);
+ hd_in[1] = INVALID_HANDLE_VALUE;
+ }
+
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ {
+ ec = create_inheritable_pipe (hd_out, INHERIT_WRITE);
+ if (ec)
+ {
+ if (hd_in[0] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[0]);
+ if (hd_in[1] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[1]);
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDOUT_KEEP))
+ {
+ hd_out[0] = INVALID_HANDLE_VALUE;
+ hd_out[1] = GetStdHandle (STD_OUTPUT_HANDLE);
+ }
+ else
+ {
+ hd_out[0] = INVALID_HANDLE_VALUE;
+ hd_out[1] = w32_open_null (1);
+ }
+
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
+ {
+ ec = create_inheritable_pipe (hd_err, INHERIT_WRITE);
+ if (ec)
+ {
+ if (hd_in[0] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[0]);
+ if (hd_in[1] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_in[1]);
+ if (hd_out[0] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_out[0]);
+ if (hd_out[1] != INVALID_HANDLE_VALUE)
+ CloseHandle (hd_out[1]);
+ xfree (process);
+ xfree (cmdline);
+ return ec;
+ }
+ }
+ else if ((flags & GNUPG_PROCESS_STDERR_KEEP))
+ {
+ hd_err[0] = INVALID_HANDLE_VALUE;
+ hd_err[1] = GetStdHandle (STD_ERROR_HANDLE);
+ }
+ else
+ {
+ hd_err[0] = INVALID_HANDLE_VALUE;
+ hd_err[1] = w32_open_null (1);
+ }
+
+ memset (&si, 0, sizeof si);
+
+ sca.allow_foreground_window = FALSE;
+ sca.hd[0] = hd_in[0];
+ sca.hd[1] = hd_out[1];
+ sca.hd[2] = hd_err[1];
+ sca.inherit_hds = NULL;
+ sca.arg = spawn_cb_arg;
+ if (spawn_cb)
+ (*spawn_cb) (&sca);
+
+ i = 0;
+ if (sca.hd[0] != INVALID_HANDLE_VALUE)
+ i++;
+ if (sca.hd[1] != INVALID_HANDLE_VALUE)
+ i++;
+ if (sca.hd[2] != INVALID_HANDLE_VALUE)
+ i++;
+
+ if (i != 0 || sca.inherit_hds)
+ {
+ SIZE_T attr_list_size = 0;
+ HANDLE hd[16];
+ HANDLE *hd_p = sca.inherit_hds;
+ int j = 0;
+
+ if (sca.hd[0] != INVALID_HANDLE_VALUE)
+ hd[j++] = sca.hd[0];
+ if (sca.hd[1] != INVALID_HANDLE_VALUE)
+ hd[j++] = sca.hd[1];
+ if (sca.hd[1] != INVALID_HANDLE_VALUE)
+ hd[j++] = sca.hd[2];
+ if (hd_p)
+ {
+ while (*hd_p != INVALID_HANDLE_VALUE)
+ if (j < DIM (hd))
+ hd[j++] = *hd_p++;
+ else
+ {
+ log_error ("Too much handles\n");
+ break;
+ }
+ }
+
+ if (j)
+ {
+ if (check_windows_version ())
+ {
+ InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size);
+ si.lpAttributeList = xtrymalloc (attr_list_size);
+ if (si.lpAttributeList == NULL)
+ {
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE)
+ || !(flags & GNUPG_PROCESS_STDIN_KEEP))
+ CloseHandle (hd_in[0]);
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ CloseHandle (hd_in[1]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ CloseHandle (hd_out[0]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE)
+ || !(flags & GNUPG_PROCESS_STDOUT_KEEP))
+ CloseHandle (hd_out[1]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
+ CloseHandle (hd_err[0]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE)
+ || !(flags & GNUPG_PROCESS_STDERR_KEEP))
+ CloseHandle (hd_err[1]);
+ xfree (process);
+ xfree (cmdline);
+ return gpg_err_code_from_syserror ();
+ }
+ InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0,
+ &attr_list_size);
+ UpdateProcThreadAttribute (si.lpAttributeList, 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ hd, sizeof (HANDLE) * j, NULL, NULL);
+ }
+ ask_inherit = TRUE;
+ }
+ }
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
- /* Build the command line. */
- err = build_w32_commandline (pgmname, argv, &cmdline);
- if (err)
- return err;
-
- memset (&si, 0, sizeof si);
- si.cb = sizeof (si);
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
- stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
- stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
- stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
- si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
- si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
- si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
-
-/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
- /* Take care: CreateProcessW may modify wpgmname */
+ /* Start the process. */
+ si.StartupInfo.cb = sizeof (si);
+ si.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE;
+ si.StartupInfo.hStdInput = sca.hd[0];
+ si.StartupInfo.hStdOutput = sca.hd[1];
+ si.StartupInfo.hStdError = sca.hd[2];
+
+ /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
+ cr_flags = (CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ())
+ | CREATE_SUSPENDED);
if (!(wpgmname = utf8_to_wchar (pgmname)))
- rc = 0;
+ ret = 0;
else if (!(wcmdline = utf8_to_wchar (cmdline)))
- rc = 0;
+ ret = 0;
else
- rc = CreateProcessW (wpgmname, /* Program to start. */
- wcmdline, /* Command line arguments. */
- &sec_attr, /* Process security attributes. */
- &sec_attr, /* Thread security attributes. */
- TRUE, /* Inherit handles. */
- (CREATE_DEFAULT_ERROR_MODE
- | GetPriorityClass (GetCurrentProcess ())
- | CREATE_SUSPENDED | DETACHED_PROCESS),
- NULL, /* Environment. */
- NULL, /* Use current drive/directory. */
- &si, /* Startup information. */
- &pi /* Returns process information. */
- );
- if (!rc)
+ ret = CreateProcessW (wpgmname, /* Program to start. */
+ wcmdline, /* Command line arguments. */
+ &sec_attr, /* Process security attributes. */
+ &sec_attr, /* Thread security attributes. */
+ ask_inherit, /* Inherit handles. */
+ cr_flags, /* Creation flags. */
+ NULL, /* Environment. */
+ NULL, /* Use current drive/directory. */
+ (STARTUPINFOW *)&si, /* Startup information. */
+ &pi /* Returns process information. */
+ );
+ if (!ret)
{
if (!wpgmname || !wcmdline)
log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
- strerror (errno));
+ strerror (errno));
else
- log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
- err = my_error (GPG_ERR_GENERAL);
+ log_error ("CreateProcess failed: ec=%d\n",
+ (int)GetLastError ());
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE)
+ || !(flags & GNUPG_PROCESS_STDIN_KEEP))
+ CloseHandle (hd_in[0]);
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE))
+ CloseHandle (hd_in[1]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE))
+ CloseHandle (hd_out[0]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE)
+ || !(flags & GNUPG_PROCESS_STDOUT_KEEP))
+ CloseHandle (hd_out[1]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE))
+ CloseHandle (hd_err[0]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE)
+ || !(flags & GNUPG_PROCESS_STDERR_KEEP))
+ CloseHandle (hd_err[1]);
+ xfree (wpgmname);
+ xfree (wcmdline);
+ xfree (process);
+ xfree (cmdline);
+ return GPG_ERR_GENERAL;
}
- else
- err = 0;
+
+ if (si.lpAttributeList)
+ DeleteProcThreadAttributeList (si.lpAttributeList);
xfree (wpgmname);
xfree (wcmdline);
xfree (cmdline);
- for (i=0; i < 3; i++)
- if (stdhd[i] != INVALID_HANDLE_VALUE)
- CloseHandle (stdhd[i]);
- if (err)
- return err;
-/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
-/* " dwProcessID=%d dwThreadId=%d\n", */
-/* pi.hProcess, pi.hThread, */
-/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
+ if ((flags & GNUPG_PROCESS_STDIN_PIPE)
+ || !(flags & GNUPG_PROCESS_STDIN_KEEP))
+ CloseHandle (hd_in[0]);
+ if ((flags & GNUPG_PROCESS_STDOUT_PIPE)
+ || !(flags & GNUPG_PROCESS_STDOUT_KEEP))
+ CloseHandle (hd_out[1]);
+ if ((flags & GNUPG_PROCESS_STDERR_PIPE)
+ || !(flags & GNUPG_PROCESS_STDERR_KEEP))
+ CloseHandle (hd_err[1]);
+
+ /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
+ /* " dwProcessID=%d dwThreadId=%d\n", */
+ /* pi.hProcess, pi.hThread, */
+ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
+
+ if (sca.allow_foreground_window)
+ {
+ /* Fixme: For unknown reasons AllowSetForegroundWindow returns
+ * an invalid argument error if we pass it the correct
+ * processID. As a workaround we use -1 (ASFW_ANY). */
+ if (!AllowSetForegroundWindow (ASFW_ANY /*pi.dwProcessId*/))
+ log_info ("AllowSetForegroundWindow() failed: ec=%d\n",
+ (int)GetLastError ());
+ }
/* Process has been created suspended; resume it now. */
+ pre_syscall ();
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
+ post_syscall ();
- *pid = handle_to_pid (pi.hProcess);
- return 0;
+ process->hProcess = pi.hProcess;
+ process->hd_in = hd_in[1];
+ process->hd_out = hd_out[0];
+ process->hd_err = hd_err[0];
+ process->exitcode = -1;
+ process->terminated = 0;
+ if (r_process == NULL)
+ {
+ ec = gnupg_process_wait (process, 1);
+ gnupg_process_release (process);
+ return ec;
+ }
+
+ *r_process = process;
+ return 0;
}
+gpg_err_code_t
+gnupg_process_get_fds (gnupg_process_t process, unsigned int flags,
+ int *r_fd_in, int *r_fd_out, int *r_fd_err)
+{
+ (void)flags;
+ if (r_fd_in)
+ {
+ *r_fd_in = _open_osfhandle ((intptr_t)process->hd_in, O_APPEND);
+ process->hd_in = INVALID_HANDLE_VALUE;
+ }
+ if (r_fd_out)
+ {
+ *r_fd_out = _open_osfhandle ((intptr_t)process->hd_out, O_RDONLY);
+ process->hd_out = INVALID_HANDLE_VALUE;
+ }
+ if (r_fd_err)
+ {
+ *r_fd_err = _open_osfhandle ((intptr_t)process->hd_err, O_RDONLY);
+ process->hd_err = INVALID_HANDLE_VALUE;
+ }
+
+ return 0;
+}
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
+gpg_err_code_t
+gnupg_process_get_streams (gnupg_process_t process, unsigned int flags,
+ estream_t *r_fp_in, estream_t *r_fp_out,
+ estream_t *r_fp_err)
{
- return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
+ int nonblock = (flags & GNUPG_PROCESS_STREAM_NONBLOCK)? 1: 0;
+ es_syshd_t syshd;
+
+ syshd.type = ES_SYSHD_HANDLE;
+ if (r_fp_in)
+ {
+ syshd.u.handle = process->hd_in;
+ *r_fp_in = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
+ process->hd_in = INVALID_HANDLE_VALUE;
+ }
+ if (r_fp_out)
+ {
+ syshd.u.handle = process->hd_out;
+ *r_fp_out = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
+ process->hd_out = INVALID_HANDLE_VALUE;
+ }
+ if (r_fp_err)
+ {
+ syshd.u.handle = process->hd_err;
+ *r_fp_err = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
+ process->hd_err = INVALID_HANDLE_VALUE;
+ }
+ return 0;
}
-/* See exechelp.h for a description. */
-gpg_error_t
-gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
- int hang, int *r_exitcodes)
+static gpg_err_code_t
+process_kill (gnupg_process_t process, unsigned int exitcode)
{
gpg_err_code_t ec = 0;
- size_t i;
- HANDLE *procs;
- int code;
- procs = xtrycalloc (count, sizeof *procs);
- if (procs == NULL)
- return my_error_from_syserror ();
+ pre_syscall ();
+ if (TerminateProcess (process->hProcess, exitcode))
+ ec = gpg_err_code_from_syserror ();
+ post_syscall ();
+ return ec;
+}
- for (i = 0; i < count; i++)
+static gpg_err_code_t
+process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr)
+{
+ switch (request)
{
- if (r_exitcodes)
- r_exitcodes[i] = -1;
+ case GNUPG_PROCESS_NOP:
+ return 0;
- if (pids[i] == (pid_t)(-1))
- return my_error (GPG_ERR_INV_VALUE);
+ case GNUPG_PROCESS_GET_PROC_ID:
+ {
+ int *r_id = va_arg (arg_ptr, int *);
- procs[i] = pid_to_handle (pids[i]);
- }
+ if (r_id == NULL)
+ return GPG_ERR_INV_VALUE;
- /* FIXME: We should do a pth_waitpid here. However this has not yet
- been implemented. A special W32 pth system call would even be
- better. */
- code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
- switch (code)
- {
- case WAIT_TIMEOUT:
- ec = GPG_ERR_TIMEOUT;
- goto leave;
+ *r_id = (int)GetProcessId (process->hProcess);
+ return 0;
+ }
- case WAIT_FAILED:
- log_error (_("waiting for processes to terminate failed: %s\n"),
- w32_strerror (-1));
- ec = GPG_ERR_GENERAL;
- goto leave;
+ case GNUPG_PROCESS_GET_EXIT_ID:
+ {
+ int *r_exit_status = va_arg (arg_ptr, int *);
+ unsigned long exit_code;
- case WAIT_OBJECT_0:
- for (i = 0; i < count; i++)
- {
- DWORD exc;
+ *r_exit_status = -1;
- if (! GetExitCodeProcess (procs[i], &exc))
- {
- log_error (_("error getting exit code of process %d: %s\n"),
- (int) pids[i], w32_strerror (-1) );
- ec = GPG_ERR_GENERAL;
- }
- else if (exc)
- {
- if (!r_exitcodes)
- log_error (_("error running '%s': exit status %d\n"),
- pgmnames[i], (int)exc);
- else
- r_exitcodes[i] = (int)exc;
- ec = GPG_ERR_GENERAL;
- }
- else
- {
- if (r_exitcodes)
- r_exitcodes[i] = 0;
- }
- }
- break;
+ if (!process->terminated)
+ return GPG_ERR_UNFINISHED;
+
+ if (process->hProcess == INVALID_HANDLE_VALUE)
+ return 0;
+
+ if (GetExitCodeProcess (process->hProcess, &exit_code) == 0)
+ return gpg_err_code_from_syserror ();
+
+ *r_exit_status = (int)exit_code;
+ return 0;
+ }
+
+ case GNUPG_PROCESS_GET_P_HANDLE:
+ {
+ HANDLE *r_hProcess = va_arg (arg_ptr, HANDLE *);
+
+ if (r_hProcess == NULL)
+ return GPG_ERR_INV_VALUE;
+
+ *r_hProcess = process->hProcess;
+ process->hProcess = INVALID_HANDLE_VALUE;
+ return 0;
+ }
+
+ case GNUPG_PROCESS_GET_HANDLES:
+ {
+ HANDLE *r_hd_in = va_arg (arg_ptr, HANDLE *);
+ HANDLE *r_hd_out = va_arg (arg_ptr, HANDLE *);
+ HANDLE *r_hd_err = va_arg (arg_ptr, HANDLE *);
+
+ if (r_hd_in)
+ {
+ *r_hd_in = process->hd_in;
+ process->hd_in = INVALID_HANDLE_VALUE;
+ }
+ if (r_hd_out)
+ {
+ *r_hd_out = process->hd_out;
+ process->hd_out = INVALID_HANDLE_VALUE;
+ }
+ if (r_hd_err)
+ {
+ *r_hd_err = process->hd_err;
+ process->hd_err = INVALID_HANDLE_VALUE;
+ }
+ return 0;
+ }
+
+ case GNUPG_PROCESS_GET_EXIT_CODE:
+ {
+ unsigned long *r_exitcode = va_arg (arg_ptr, unsigned long *);
+
+ if (!process->terminated)
+ return GPG_ERR_UNFINISHED;
+
+ if (process->hProcess == INVALID_HANDLE_VALUE)
+ {
+ *r_exitcode = (unsigned long)-1;
+ return 0;
+ }
+
+ if (GetExitCodeProcess (process->hProcess, r_exitcode) == 0)
+ return gpg_err_code_from_syserror ();
+ return 0;
+ }
+
+ case GNUPG_PROCESS_KILL_WITH_EC:
+ {
+ unsigned int exitcode = va_arg (arg_ptr, unsigned int);
+
+ if (process->terminated)
+ return 0;
+
+ if (process->hProcess == INVALID_HANDLE_VALUE)
+ return 0;
+
+ return process_kill (process, exitcode);
+ }
default:
- log_error ("WaitForMultipleObjects returned unexpected "
- "code %d\n", code);
- ec = GPG_ERR_GENERAL;
break;
}
- leave:
- return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+ return GPG_ERR_UNKNOWN_COMMAND;
}
-
-
-void
-gnupg_release_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_ctl (gnupg_process_t process, unsigned int request, ...)
{
- if (pid != (pid_t)INVALID_HANDLE_VALUE)
- {
- HANDLE process = (HANDLE)pid;
+ va_list arg_ptr;
+ gpg_err_code_t ec;
- CloseHandle (process);
- }
+ va_start (arg_ptr, request);
+ ec = process_vctl (process, request, arg_ptr);
+ va_end (arg_ptr);
+ return ec;
}
-
-/* Spawn a new process and immediately detach from it. The name of
- the program to exec is PGMNAME and its arguments are in ARGV (the
- programname is automatically passed as first argument).
- Environment strings in ENVP are set. An error is returned if
- pgmname is not executable; to make this work it is necessary to
- provide an absolute file name. All standard file descriptors are
- connected to /dev/null. */
-gpg_error_t
-gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
- const char *envp[] )
+gpg_err_code_t
+gnupg_process_wait (gnupg_process_t process, int hang)
{
- gpg_error_t err;
- SECURITY_ATTRIBUTES sec_attr;
- PROCESS_INFORMATION pi =
- {
- NULL, /* Returns process handle. */
- 0, /* Returns primary thread handle. */
- 0, /* Returns pid. */
- 0 /* Returns tid. */
- };
- STARTUPINFOW si;
- int cr_flags;
- char *cmdline;
- wchar_t *wcmdline = NULL;
- wchar_t *wpgmname = NULL;
- BOOL in_job = FALSE;
gpg_err_code_t ec;
- int rc;
- int jobdebug;
+ int code;
- /* We don't use ENVP. */
- (void)envp;
+ if (process->hProcess == INVALID_HANDLE_VALUE)
+ return 0;
- cmdline = getenv ("GNUPG_EXEC_DEBUG_FLAGS");
- jobdebug = (cmdline && (atoi (cmdline) & 1));
+ pre_syscall ();
+ code = WaitForSingleObject (process->hProcess, hang? INFINITE : 0);
+ post_syscall ();
- if ((ec = gnupg_access (pgmname, X_OK)))
- return gpg_err_make (default_errsource, ec);
+ switch (code)
+ {
+ case WAIT_TIMEOUT:
+ ec = GPG_ERR_TIMEOUT; /* Still running. */
+ break;
- /* Prepare security attributes. */
- memset (&sec_attr, 0, sizeof sec_attr );
- sec_attr.nLength = sizeof sec_attr;
- sec_attr.bInheritHandle = FALSE;
+ case WAIT_FAILED:
+ log_error (_("waiting for process to terminate failed: ec=%d\n"),
+ (int)GetLastError ());
+ ec = GPG_ERR_GENERAL;
+ break;
- /* Build the command line. */
- err = build_w32_commandline (pgmname, argv, &cmdline);
- if (err)
- return err;
+ case WAIT_OBJECT_0:
+ process->terminated = 1;
+ ec = 0;
+ break;
- /* Start the process. */
- memset (&si, 0, sizeof si);
- si.cb = sizeof (si);
- si.dwFlags = STARTF_USESHOWWINDOW;
- si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
+ default:
+ log_debug ("WaitForSingleObject returned unexpected code %d\n", code);
+ ec = GPG_ERR_GENERAL;
+ break;
+ }
- cr_flags = (CREATE_DEFAULT_ERROR_MODE
- | GetPriorityClass (GetCurrentProcess ())
- | CREATE_NEW_PROCESS_GROUP
- | DETACHED_PROCESS);
+ return ec;
+}
- /* Check if we were spawned as part of a Job.
- * In a job we need to add CREATE_BREAKAWAY_FROM_JOB
- * to the cr_flags, otherwise our child processes
- * are killed when we terminate. */
- if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job))
- {
- log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1));
- in_job = FALSE;
- }
+gpg_err_code_t
+gnupg_process_terminate (gnupg_process_t process)
+{
+ return process_kill (process, 1);
+}
- if (in_job)
- {
- /* Only try to break away from job if it is allowed, otherwise
- * CreateProcess() would fail with an "Access is denied" error. */
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
- if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation,
- &info, sizeof info, NULL))
- {
- log_error ("QueryInformationJobObject() failed: %s\n",
- w32_strerror (-1));
- }
- else if ((info.BasicLimitInformation.LimitFlags &
- JOB_OBJECT_LIMIT_BREAKAWAY_OK))
- {
- if (jobdebug)
- log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n");
- cr_flags |= CREATE_BREAKAWAY_FROM_JOB;
- }
- else if ((info.BasicLimitInformation.LimitFlags &
- JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))
- {
- /* The child process should automatically detach from the job. */
- if (jobdebug)
- log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; "
- "JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n");
- }
- else
- {
- /* It seems that the child process must remain in the job.
- * This is not necessarily an error, although it can cause premature
- * termination of the child process when the job is closed. */
- if (jobdebug)
- log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n");
- }
- }
- else
- {
- if (jobdebug)
- log_debug ("Process is not in a Job\n");
- }
+void
+gnupg_process_release (gnupg_process_t process)
+{
+ if (!process)
+ return;
- /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
- /* pgmname, cmdline); */
- /* Take care: CreateProcessW may modify wpgmname */
- if (!(wpgmname = utf8_to_wchar (pgmname)))
- rc = 0;
- else if (!(wcmdline = utf8_to_wchar (cmdline)))
- rc = 0;
- else
- rc = CreateProcessW (wpgmname, /* Program to start. */
- wcmdline, /* Command line arguments. */
- &sec_attr, /* Process security attributes. */
- &sec_attr, /* Thread security attributes. */
- FALSE, /* Inherit handles. */
- cr_flags, /* Creation flags. */
- NULL, /* Environment. */
- NULL, /* Use current drive/directory. */
- &si, /* Startup information. */
- &pi /* Returns process information. */
- );
- if (!rc)
+ if (process->terminated)
{
- if (!wpgmname || !wcmdline)
- log_error ("CreateProcess failed (utf8_to_wchar): %s\n",
- strerror (errno));
- else
- log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
- xfree (wpgmname);
- xfree (wcmdline);
- xfree (cmdline);
- return my_error (GPG_ERR_GENERAL);
+ gnupg_process_terminate (process);
+ gnupg_process_wait (process, 1);
}
- xfree (wpgmname);
- xfree (wcmdline);
- xfree (cmdline);
- cmdline = NULL;
-
-/* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
-/* " dwProcessID=%d dwThreadId=%d\n", */
-/* pi.hProcess, pi.hThread, */
-/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- CloseHandle (pi.hThread);
- CloseHandle (pi.hProcess);
-
- return 0;
+ CloseHandle (process->hProcess);
+ xfree (process);
}
-
-/* Kill a process; that is send an appropriate signal to the process.
- gnupg_wait_process must be called to actually remove the process
- from the system. An invalid PID is ignored. */
-void
-gnupg_kill_process (pid_t pid)
+gpg_err_code_t
+gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang)
{
- if (pid != (pid_t) INVALID_HANDLE_VALUE)
+ gpg_err_code_t ec = 0;
+ int i;
+
+ for (i = 0; i < count; i++)
{
- HANDLE process = (HANDLE) pid;
+ if (process_list[i]->terminated)
+ continue;
- /* Arbitrary error code. */
- TerminateProcess (process, 1);
+ ec = gnupg_process_wait (process_list[i], hang);
+ if (ec)
+ break;
}
+
+ return ec;
}
diff --git a/common/exechelp.h b/common/exechelp.h
index 3343fe598..0370b23a4 100644
--- a/common/exechelp.h
+++ b/common/exechelp.h
@@ -73,140 +73,103 @@ gpg_error_t gnupg_create_pipe (int filedes[2]);
void gnupg_close_pipe (int fd);
-#define GNUPG_SPAWN_NONBLOCK 16
-#define GNUPG_SPAWN_RUN_ASFW 64
-#define GNUPG_SPAWN_DETACHED 128
-#define GNUPG_SPAWN_KEEP_STDIN 256
-#define GNUPG_SPAWN_KEEP_STDOUT 512
-#define GNUPG_SPAWN_KEEP_STDERR 1024
-
-/* Fork and exec the program PGMNAME.
-
- If R_INFP is NULL connect stdin of the new process to /dev/null; if
- it is not NULL store the address of a pointer to a new estream
- there. If R_OUTFP is NULL connect stdout of the new process to
- /dev/null; if it is not NULL store the address of a pointer to a
- new estream there. If R_ERRFP is NULL connect stderr of the new
- process to /dev/null; if it is not NULL store the address of a
- pointer to a new estream there. On success the pid of the new
- process is stored at PID. On error -1 is stored at PID and if
- R_OUTFP or R_ERRFP are not NULL, NULL is stored there.
-
- The arguments for the process are expected in the NULL terminated
- array ARGV. The program name itself should not be included there.
- If PREEXEC is not NULL, the given function will be called right
- before the exec.
-
- IF EXCEPT is not NULL, it is expected to be an ordered list of file
- descriptors, terminated by an entry with the value (-1). These
- file descriptors won't be closed before spawning a new program.
-
- Returns 0 on success or an error code. Calling gnupg_wait_process
- and gnupg_release_process is required if the function succeeded.
-
- FLAGS is a bit vector:
-
- GNUPG_SPAWN_NONBLOCK
- If set the two output streams are created in non-blocking
- mode and the input stream is switched to non-blocking mode.
- This is merely a convenience feature because the caller
- could do the same with gpgrt_set_nonblock. Does not yet
- work for Windows.
-
- GNUPG_SPAWN_DETACHED
- If set the process will be started as a background process.
- This flag is only useful under W32 (but not W32CE) systems,
- so that no new console is created and pops up a console
- window when starting the server. Does not work on W32CE.
-
- GNUPG_SPAWN_RUN_ASFW
- On W32 (but not on W32CE) run AllowSetForegroundWindow for
- the child. Note that due to unknown problems this actually
- allows SetForegroundWindow for all children of this process.
-
- GNUPG_SPAWN_KEEP_STDIN
- GNUPG_SPAWN_KEEP_STDOUT
- GNUPG_SPAWN_KEEP_STDERR
- Do not assign /dev/null to a non-required standard file
- descriptor.
-
- */
-gpg_error_t
-gnupg_spawn_process (const char *pgmname, const char *argv[],
- int *execpt, unsigned int flags,
- estream_t *r_infp,
- estream_t *r_outfp,
- estream_t *r_errfp,
- pid_t *pid);
-
-
-/* Simplified version of gnupg_spawn_process. This function forks and
- then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
- and ERRFD to stderr (any of them may be -1 to connect them to
- /dev/null). The arguments for the process are expected in the NULL
- terminated array ARGV. The program name itself should not be
- included there. Calling gnupg_wait_process and
- gnupg_release_process is required. Returns 0 on success or an
- error code. */
-gpg_error_t gnupg_spawn_process_fd (const char *pgmname,
- const char *argv[],
- int infd, int outfd, int errfd,
- pid_t *pid);
-
-
-/* If HANG is true, waits for the process identified by PID to exit;
- if HANG is false, checks whether the process has terminated.
- PGMNAME should be the same as supplied to the spawn function and is
- only used for diagnostics. Return values:
-
- 0
- The process exited successful. 0 is stored at R_EXITCODE.
-
- GPG_ERR_GENERAL
- The process exited without success. The exit code of process
- is then stored at R_EXITCODE. An exit code of -1 indicates
- that the process terminated abnormally (e.g. due to a signal).
-
- GPG_ERR_TIMEOUT
- The process is still running (returned only if HANG is false).
-
- GPG_ERR_INV_VALUE
- An invalid PID has been specified.
-
- Other error codes may be returned as well. Unless otherwise noted,
- -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL
- if the exit code is not required (in that case an error message will
- be printed). Note that under Windows PID is not the process id but
- the handle of the process. */
-gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang,
- int *r_exitcode);
-
-/* Like gnupg_wait_process, but for COUNT processes. */
-gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids,
- size_t count, int hang, int *r_exitcodes);
-
-
-/* Kill a process; that is send an appropriate signal to the process.
- gnupg_wait_process must be called to actually remove the process
- from the system. An invalid PID is ignored. */
-void gnupg_kill_process (pid_t pid);
-
-/* Release the process identified by PID. This function is actually
- only required for Windows but it does not harm to always call it.
- It is a nop if PID is invalid. */
-void gnupg_release_process (pid_t pid);
-
-
-/* Spawn a new process and immediately detach from it. The name of
- the program to exec is PGMNAME and its arguments are in ARGV (the
- programname is automatically passed as first argument).
- Environment strings in ENVP are set. An error is returned if
- pgmname is not executable; to make this work it is necessary to
- provide an absolute file name. */
-gpg_error_t gnupg_spawn_process_detached (const char *pgmname,
- const char *argv[],
- const char *envp[] );
-
+/* The opaque type for a subprocess. */
+typedef struct gnupg_process *gnupg_process_t;
+#ifdef HAVE_W32_SYSTEM
+struct spawn_cb_arg;
+#ifdef NEED_STRUCT_SPAWN_CB_ARG
+struct spawn_cb_arg {
+ HANDLE hd[3];
+ HANDLE *inherit_hds;
+ BOOL allow_foreground_window;
+ void *arg;
+};
+#endif
+#else
+struct spawn_cb_arg {
+ int fds[3];
+ int *except_fds;
+ void *arg;
+};
+#endif
+
+#define GNUPG_PROCESS_DETACHED (1 << 1)
+
+/* Specify how to keep/connect standard fds. */
+#define GNUPG_PROCESS_STDIN_PIPE (1 << 8)
+#define GNUPG_PROCESS_STDOUT_PIPE (1 << 9)
+#define GNUPG_PROCESS_STDERR_PIPE (1 << 10)
+#define GNUPG_PROCESS_STDINOUT_SOCKETPAIR (1 << 11)
+#define GNUPG_PROCESS_STDIN_KEEP (1 << 12)
+#define GNUPG_PROCESS_STDOUT_KEEP (1 << 13)
+#define GNUPG_PROCESS_STDERR_KEEP (1 << 14)
+#define GNUPG_PROCESS_STDFDS_SETTING ( GNUPG_PROCESS_STDIN_PIPE \
+ | GNUPG_PROCESS_STDOUT_PIPE | GNUPG_PROCESS_STDERR_PIPE \
+ | GNUPG_PROCESS_STDINOUT_SOCKETPAIR | GNUPG_PROCESS_STDIN_KEEP \
+ | GNUPG_PROCESS_STDOUT_KEEP | GNUPG_PROCESS_STDERR_KEEP)
+
+#define GNUPG_PROCESS_STREAM_NONBLOCK (1 << 16)
+
+/* Spawn helper. */
+void gnupg_spawn_helper (struct spawn_cb_arg *sca);
+
+/* Spawn PGMNAME. */
+gpg_err_code_t gnupg_process_spawn (const char *pgmname, const char *argv[],
+ unsigned int flags,
+ void (*spawn_cb) (struct spawn_cb_arg *),
+ void *spawn_cb_arg,
+ gnupg_process_t *r_process);
+
+/* Get FDs for subprocess I/O. It is the caller which should care
+ FDs (closing FDs). */
+gpg_err_code_t gnupg_process_get_fds (gnupg_process_t process,
+ unsigned int flags,
+ int *r_fd_in, int *r_fd_out,
+ int *r_fd_err);
+
+/* Get STREAMs for subprocess I/O. It is the caller which should care
+ STREAMs (closing STREAMs). */
+gpg_err_code_t gnupg_process_get_streams (gnupg_process_t process,
+ unsigned int flags,
+ gpgrt_stream_t *r_fp_in,
+ gpgrt_stream_t *r_fp_out,
+ gpgrt_stream_t *r_fp_err);
+
+enum gnupg_process_requests
+ {
+ /* Portable requests */
+ GNUPG_PROCESS_NOP = 0,
+ GNUPG_PROCESS_GET_PROC_ID = 1,
+ GNUPG_PROCESS_GET_EXIT_ID = 2,
+
+ /* POSIX only */
+ GNUPG_PROCESS_GET_PID = 16,
+ GNUPG_PROCESS_GET_WSTATUS = 17,
+ GNUPG_PROCESS_KILL = 18,
+
+ /* Windows only */
+ GNUPG_PROCESS_GET_P_HANDLE = 32,
+ GNUPG_PROCESS_GET_HANDLES = 33,
+ GNUPG_PROCESS_GET_EXIT_CODE = 34,
+ GNUPG_PROCESS_KILL_WITH_EC = 35
+ };
+
+/* Control of a process. */
+gpg_err_code_t gnupg_process_ctl (gnupg_process_t process,
+ unsigned int request, ...);
+
+/* Wait for a single PROCESS. */
+gpg_err_code_t gnupg_process_wait (gnupg_process_t process, int hang);
+
+/* Terminate a PROCESS. */
+gpg_err_code_t gnupg_process_terminate (gnupg_process_t process);
+
+/* Release PROCESS resources. */
+void gnupg_process_release (gnupg_process_t process);
+
+/* Wait for a multiple processes. */
+gpg_err_code_t gnupg_process_wait_list (gnupg_process_t *process_list,
+ int count, int hang);
#endif /*GNUPG_COMMON_EXECHELP_H*/
diff --git a/common/exectool.c b/common/exectool.c
index aaf5898d0..3505c25f1 100644
--- a/common/exectool.c
+++ b/common/exectool.c
@@ -38,10 +38,14 @@
#include <gpg-error.h>
#include <assuan.h>
+
#include "i18n.h"
#include "logging.h"
#include "membuf.h"
#include "mischelp.h"
+#ifdef HAVE_W32_SYSTEM
+#define NEED_STRUCT_SPAWN_CB_ARG 1
+#endif
#include "exechelp.h"
#include "sysutils.h"
#include "util.h"
@@ -301,7 +305,6 @@ copy_buffer_flush (struct copy_buffer *c, estream_t sink)
}
-
/* Run the program PGMNAME with the command line arguments given in
* the NULL terminates array ARGV. If INPUT is not NULL it will be
* fed to stdin of the process. stderr is logged using log_info and
@@ -321,7 +324,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
void *status_cb_value)
{
gpg_error_t err;
- pid_t pid = (pid_t) -1;
+ gnupg_process_t proc = NULL;
estream_t infp = NULL;
estream_t extrafp = NULL;
estream_t outfp = NULL, errfp = NULL;
@@ -335,7 +338,6 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
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);
@@ -411,10 +413,15 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
else
exceptclose[0] = -1;
- err = gnupg_spawn_process (pgmname, argv,
- exceptclose, GNUPG_SPAWN_NONBLOCK,
- input? &infp : NULL,
- &outfp, &errfp, &pid);
+ err = gnupg_process_spawn (pgmname, argv,
+ ((input
+ ? GNUPG_PROCESS_STDIN_PIPE
+ : 0)
+ | GNUPG_PROCESS_STDOUT_PIPE
+ | GNUPG_PROCESS_STDERR_PIPE),
+ gnupg_spawn_helper, exceptclose, &proc);
+ gnupg_process_get_streams (proc, GNUPG_PROCESS_STREAM_NONBLOCK,
+ input? &infp : NULL, &outfp, &errfp);
if (extrapipe[0] != -1)
close (extrapipe[0]);
if (argsave)
@@ -546,20 +553,25 @@ 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, quiet? &dummy_exitcode : NULL);
- pid = (pid_t)(-1);
+ err = gnupg_process_wait (proc, 1);
+ if (!err)
+ { /* To be compatible to old wait_process. */
+ int status;
+
+ gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status);
+ if (status)
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
leave:
- if (err && pid != (pid_t) -1)
- gnupg_kill_process (pid);
+ if (err && proc)
+ gnupg_process_terminate (proc);
es_fclose (infp);
es_fclose (extrafp);
es_fclose (outfp);
es_fclose (errfp);
- if (pid != (pid_t)(-1))
- gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL);
- gnupg_release_process (pid);
+ gnupg_process_release (proc);
copy_buffer_shred (cpbuf_in);
xfree (cpbuf_in);
diff --git a/common/get-passphrase.c b/common/get-passphrase.c
index c24b40e88..8ea822710 100644
--- a/common/get-passphrase.c
+++ b/common/get-passphrase.c
@@ -94,7 +94,8 @@ start_agent (void)
agentargs.lc_ctype,
agentargs.lc_messages,
agentargs.session_env,
- 1, agentargs.verbosity, 0, NULL, NULL);
+ ASSHELP_FLAG_AUTOSTART,
+ agentargs.verbosity, 0, NULL, NULL);
if (!err)
{
/* Tell the agent that we support Pinentry notifications. No
diff --git a/common/homedir.c b/common/homedir.c
index 6f99f3eab..deb6f3616 100644
--- a/common/homedir.c
+++ b/common/homedir.c
@@ -1,7 +1,7 @@
/* homedir.c - Setup the home directory.
* Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc.
* Copyright (C) 2013, 2016 Werner Koch
- * Copyright (C) 2021 g10 Code GmbH
+ * Copyright (C) 2021, 2024 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -93,6 +93,22 @@ static char *the_gnupg_homedir;
static byte non_default_homedir;
+/* An object to store information taken from a gpgconf.ctl file. This
+ * is parsed early at startup time and never changed later. */
+static struct
+{
+ unsigned int checked:1; /* True if we have checked for a gpgconf.ctl. */
+ unsigned int found:1; /* True if a gpgconf.ctl was found. */
+ unsigned int empty:1; /* The file is empty except for comments. */
+ unsigned int valid:1; /* The entries in gpgconf.ctl are valid. */
+ unsigned int portable:1;/* Windows portable installation. */
+ char *gnupg; /* The "gnupg" directory part. */
+ char *rootdir; /* rootdir or NULL */
+ char *sysconfdir; /* sysconfdir or NULL */
+ char *socketdir; /* socketdir or NULL */
+} gpgconf_ctl;
+
+
#ifdef HAVE_W32_SYSTEM
/* A flag used to indicate that a control file for gpgconf has been
* detected. Under Windows the presence of this file indicates a
@@ -119,6 +135,87 @@ static byte w32_bin_is_bin;
static const char *w32_rootdir (void);
#endif
+/* Return the name of the gnupg dir. This is usually "gnupg". */
+static const char *
+my_gnupg_dirname (void)
+{
+ if (gpgconf_ctl.valid && gpgconf_ctl.gnupg)
+ return gpgconf_ctl.gnupg;
+ return "gnupg";
+}
+
+/* Return the hardwired home directory which is not anymore so
+ * hardwired because it may now be modified using the gpgconf.ctl
+ * "gnupg" keyword. */
+static const char *
+my_fixed_default_homedir (void)
+{
+ if (gpgconf_ctl.valid && gpgconf_ctl.gnupg)
+ {
+ static char *name;
+ char *p;
+
+ if (!name)
+ {
+ name = xmalloc (strlen (GNUPG_DEFAULT_HOMEDIR)
+ + strlen (gpgconf_ctl.gnupg) + 1);
+ strcpy (name, GNUPG_DEFAULT_HOMEDIR);
+ p = strrchr (name, '/');
+ if (p)
+ p++;
+ else
+ p = name;
+ if (*p == '.')
+ p++; /* Keep a leading dot. */
+ strcpy (p, gpgconf_ctl.gnupg);
+ gpgrt_annotate_leaked_object (name);
+ }
+ return name;
+ }
+ return GNUPG_DEFAULT_HOMEDIR;
+}
+
+
+
+/* Under Windows we need to modify the standard registry key with the
+ * "gnupg" keyword from a gpgconf.ctl. */
+#ifdef HAVE_W32_SYSTEM
+const char *
+gnupg_registry_dir (void)
+{
+ if (gpgconf_ctl.valid && gpgconf_ctl.gnupg)
+ {
+ static char *name;
+ char *p;
+
+ if (!name)
+ {
+ name = xmalloc (strlen (GNUPG_REGISTRY_DIR)
+ + strlen (gpgconf_ctl.gnupg) + 1);
+ strcpy (name, GNUPG_REGISTRY_DIR);
+ p = strrchr (name, '\\');
+ if (p)
+ p++;
+ else
+ p = name;
+ strcpy (p, gpgconf_ctl.gnupg);
+ if (!strncmp (p, "gnupg", 5))
+ {
+ /* Registry keys are case-insensitive and we use a
+ * capitalized version of gnupg by default. So, if the
+ * new value starts with "gnupg" we apply the usual
+ * capitalization for this first part. */
+ p[0] = 'G';
+ p[3] = 'P';
+ p[4] = 'G';
+ }
+ gpgrt_annotate_leaked_object (name);
+ }
+ return name;
+ }
+ return GNUPG_REGISTRY_DIR;
+}
+#endif /*HAVE_W32_SYSTEM*/
/* This is a helper function to load and call a Windows function from
@@ -324,7 +421,7 @@ standard_homedir (void)
NULL, 0);
if (path)
{
- dir = xstrconcat (path, "\\gnupg", NULL);
+ dir = xstrconcat (path, "\\", my_gnupg_dirname (), NULL);
xfree (path);
gpgrt_annotate_leaked_object (dir);
@@ -335,12 +432,12 @@ standard_homedir (void)
}
else
- dir = GNUPG_DEFAULT_HOMEDIR;
+ dir = my_fixed_default_homedir ();
}
}
return dir;
#else/*!HAVE_W32_SYSTEM*/
- return GNUPG_DEFAULT_HOMEDIR;
+ return my_fixed_default_homedir ();
#endif /*!HAVE_W32_SYSTEM*/
}
@@ -374,7 +471,7 @@ default_homedir (void)
* warning if the homedir has been taken from the
* registry. */
tmp = read_w32_registry_string (NULL,
- GNUPG_REGISTRY_DIR,
+ gnupg_registry_dir (),
"HomeDir");
if (tmp && !*tmp)
{
@@ -399,7 +496,7 @@ default_homedir (void)
#endif /*HAVE_W32_SYSTEM*/
if (!dir || !*dir)
- dir = GNUPG_DEFAULT_HOMEDIR;
+ dir = my_fixed_default_homedir ();
else
{
char *p;
@@ -427,6 +524,234 @@ default_homedir (void)
}
+/* Return true if S can be inteprtated as true. This is uised for
+ * keywords in gpgconf.ctl. Spaces must have been trimmed. */
+static int
+string_is_true (const char *s)
+{
+ return (atoi (s)
+ || !ascii_strcasecmp (s, "yes")
+ || !ascii_strcasecmp (s, "true")
+ || !ascii_strcasecmp (s, "fact"));
+}
+
+/* This function is used to parse the gpgconf.ctl file and set the
+ * information ito the gpgconf_ctl structure. This is called once
+ * with the full filename of gpgconf.ctl. There are two callers: One
+ * used on Windows and one on Unix. No error return but diagnostics
+ * are printed. */
+static void
+parse_gpgconf_ctl (const char *fname)
+{
+ gpg_error_t err;
+ char *p;
+ char *line;
+ size_t linelen;
+ ssize_t length;
+ estream_t fp;
+ const char *name;
+ int anyitem = 0;
+ int ignoreall = 0;
+ char *gnupgval = NULL;
+ char *rootdir = NULL;
+ char *sysconfdir = NULL;
+ char *socketdir = NULL;
+
+ if (gpgconf_ctl.checked)
+ return; /* Just in case this is called a second time. */
+ gpgconf_ctl.checked = 1;
+ gpgconf_ctl.found = 0;
+ gpgconf_ctl.valid = 0;
+ gpgconf_ctl.empty = 0;
+
+ if (gnupg_access (fname, F_OK))
+ return; /* No gpgconf.ctl file. */
+
+ /* log_info ("detected '%s'\n", buffer); */
+ fp = es_fopen (fname, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_info ("error opening '%s': %s\n", fname, gpg_strerror (err));
+ return;
+ }
+ gpgconf_ctl.found = 1;
+
+ line = NULL;
+ linelen = 0;
+ while ((length = es_read_line (fp, &line, &linelen, NULL)) > 0)
+ {
+ static const char *names[] =
+ {
+ "gnupg",
+ "rootdir",
+ "sysconfdir",
+ "socketdir",
+ "portable",
+ ".enable"
+ };
+ int i;
+ size_t n;
+
+ /* Strip NL and CR, if present. */
+ while (length > 0
+ && (line[length - 1] == '\n' || line[length - 1] == '\r'))
+ line[--length] = 0;
+ trim_spaces (line);
+ if (*line == '#' || !*line)
+ continue;
+ anyitem = 1;
+
+ /* Find the keyword. */
+ name = NULL;
+ p = NULL;
+ for (i=0; i < DIM (names); i++)
+ {
+ n = strlen (names[i]);
+ if (!strncmp (line, names[i], n))
+ {
+ while (line[n] == ' ' || line[n] == '\t')
+ n++;
+ if (line[n] == '=')
+ {
+ name = names[i];
+ p = line + n + 1;
+ break;
+ }
+ }
+ }
+ if (!name)
+ continue; /* Keyword not known. */
+
+ trim_spaces (p);
+ p = substitute_envvars (p);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ log_info ("error getting %s from gpgconf.ctl: %s\n",
+ name, gpg_strerror (err));
+ }
+ else if (!strcmp (name, ".enable"))
+ {
+ if (string_is_true (p))
+ ; /* Yes, this file shall be used. */
+ else
+ ignoreall = 1; /* No, this file shall be ignored. */
+ xfree (p);
+ }
+ else if (!strcmp (name, "gnupg"))
+ {
+ xfree (gnupgval);
+ gnupgval = p;
+ }
+ else if (!strcmp (name, "sysconfdir"))
+ {
+ xfree (sysconfdir);
+ sysconfdir = p;
+ }
+ else if (!strcmp (name, "socketdir"))
+ {
+ xfree (socketdir);
+ socketdir = p;
+ }
+ else if (!strcmp (name, "rootdir"))
+ {
+ xfree (rootdir);
+ rootdir = p;
+ }
+ else if (!strcmp (name, "portable"))
+ {
+ gpgconf_ctl.portable = string_is_true (p);
+ xfree (p);
+ }
+ else /* Unknown keyword. */
+ xfree (p);
+ }
+ if (es_ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ log_info ("error reading '%s': %s\n", fname, gpg_strerror (err));
+ ignoreall = 1; /* Force all entries to invalid. */
+ }
+ es_fclose (fp);
+ xfree (line);
+
+ if (ignoreall)
+ ; /* Forced error. Note that .found is still set. */
+ else if (gnupgval && (!*gnupgval || strpbrk (gnupgval, "/\\")))
+ {
+ /* We don't allow a slash or backslash in the value because our
+ * code assumes this is a single directory name. */
+ log_info ("invalid %s '%s' specified in gpgconf.ctl\n",
+ "gnupg", gnupgval);
+ }
+ else if (rootdir && (!*rootdir || *rootdir != '/'))
+ {
+ log_info ("invalid %s '%s' specified in gpgconf.ctl\n",
+ "rootdir", rootdir);
+ }
+ else if (sysconfdir && (!*sysconfdir || *sysconfdir != '/'))
+ {
+ log_info ("invalid %s '%s' specified in gpgconf.ctl\n",
+ "sysconfdir", sysconfdir);
+ }
+ else if (socketdir && (!*socketdir || *socketdir != '/'))
+ {
+ log_info ("invalid %s '%s' specified in gpgconf.ctl\n",
+ "socketdir", socketdir);
+ }
+ else
+ {
+ if (gnupgval)
+ {
+ gpgconf_ctl.gnupg = gnupgval;
+ gpgrt_annotate_leaked_object (gpgconf_ctl.gnupg);
+ /* log_info ("want gnupg '%s'\n", dir); */
+ }
+ if (rootdir)
+ {
+ while (*rootdir && rootdir[strlen (rootdir)-1] == '/')
+ rootdir[strlen (rootdir)-1] = 0;
+ gpgconf_ctl.rootdir = rootdir;
+ gpgrt_annotate_leaked_object (gpgconf_ctl.rootdir);
+ /* log_info ("want rootdir '%s'\n", dir); */
+ }
+ if (sysconfdir)
+ {
+ while (*sysconfdir && sysconfdir[strlen (sysconfdir)-1] == '/')
+ sysconfdir[strlen (sysconfdir)-1] = 0;
+ gpgconf_ctl.sysconfdir = sysconfdir;
+ gpgrt_annotate_leaked_object (gpgconf_ctl.sysconfdir);
+ /* log_info ("want sysconfdir '%s'\n", sdir); */
+ }
+ if (socketdir)
+ {
+ while (*socketdir && socketdir[strlen (socketdir)-1] == '/')
+ socketdir[strlen (socketdir)-1] = 0;
+ gpgconf_ctl.socketdir = socketdir;
+ gpgrt_annotate_leaked_object (gpgconf_ctl.socketdir);
+ /* log_info ("want socketdir '%s'\n", s2dir); */
+ }
+ gpgconf_ctl.valid = 1;
+ }
+
+ gpgconf_ctl.empty = !anyitem;
+ if (!gpgconf_ctl.valid)
+ {
+ /* Error reading some entries - clear them all. */
+ xfree (gnupgval);
+ xfree (rootdir);
+ xfree (sysconfdir);
+ xfree (socketdir);
+ gpgconf_ctl.gnupg = NULL;
+ gpgconf_ctl.rootdir = NULL;
+ gpgconf_ctl.sysconfdir = NULL;
+ gpgconf_ctl.socketdir = NULL;
+ }
+}
+
+
+
#ifdef HAVE_W32_SYSTEM
/* Check whether gpgconf is installed and if so read the gpgconf.ctl
file. */
@@ -439,17 +764,20 @@ check_portable_app (const char *dir)
if (!gnupg_access (fname, F_OK))
{
strcpy (fname + strlen (fname) - 3, "ctl");
- if (!gnupg_access (fname, F_OK))
+ parse_gpgconf_ctl (fname);
+ if ((gpgconf_ctl.found && gpgconf_ctl.empty)
+ || (gpgconf_ctl.valid && gpgconf_ctl.portable))
{
- /* gpgconf.ctl file found. Record this fact. */
+ unsigned int flags;
+
+ /* Classic gpgconf.ctl file found. This is a portable
+ * application. Note that if there are any items in that
+ * file we don't consider this a portable application unless
+ * the (later added) ".portable" keyword has also been
+ * seen. */
w32_portable_app = 1;
- {
- unsigned int flags;
- log_get_prefix (&flags);
- log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY));
- }
- /* FIXME: We should read the file to detect special flags
- and print a warning if we don't understand them */
+ log_get_prefix (&flags);
+ log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY));
}
}
xfree (fname);
@@ -528,28 +856,14 @@ w32_rootdir (void)
static const char *
unix_rootdir (enum wantdir_values wantdir)
{
- static int checked;
- static char *dir; /* for the rootdir */
- static char *sdir; /* for the sysconfdir */
- static char *s2dir; /* for the socketdir */
-
- if (!checked)
+ if (!gpgconf_ctl.checked)
{
char *p;
char *buffer;
size_t bufsize = 256-1;
int nread;
gpg_error_t err;
- char *line;
- size_t linelen;
- ssize_t length;
- estream_t fp;
- char *rootdir;
- char *sysconfdir;
- char *socketdir;
const char *name;
- int ignoreall = 0;
- int okay;
for (;;)
{
@@ -589,7 +903,7 @@ unix_rootdir (enum wantdir_values wantdir)
if (!*buffer)
{
xfree (buffer);
- checked = 1;
+ gpgconf_ctl.checked = 1;
return NULL; /* Error - assume no gpgconf.ctl. */
}
@@ -597,197 +911,36 @@ unix_rootdir (enum wantdir_values wantdir)
if (!p)
{
xfree (buffer);
- checked = 1;
+ gpgconf_ctl.checked = 1;
return NULL; /* Erroneous /proc - assume no gpgconf.ctl. */
}
*p = 0; /* BUFFER has the directory. */
- if ((p = strrchr (buffer, '/')))
- {
- /* Strip one part and expect the file below a bin dir. */
- *p = 0;
- p = xstrconcat (buffer, "/bin/gpgconf.ctl", NULL);
- xfree (buffer);
- buffer = p;
- }
- else /* !p */
+ if (!(p = strrchr (buffer, '/')))
{
/* Installed in the root which is not a good idea. Assume
* no gpgconf.ctl. */
xfree (buffer);
- checked = 1;
- return NULL;
- }
-
- if (gnupg_access (buffer, F_OK))
- {
- /* No gpgconf.ctl file. */
- xfree (buffer);
- checked = 1;
+ gpgconf_ctl.checked = 1;
return NULL;
}
- /* log_info ("detected '%s'\n", buffer); */
- fp = es_fopen (buffer, "r");
- if (!fp)
- {
- err = gpg_error_from_syserror ();
- log_info ("error opening '%s': %s\n", buffer, gpg_strerror (err));
- xfree (buffer);
- checked = 1;
- return NULL;
- }
-
- line = NULL;
- linelen = 0;
- rootdir = NULL;
- sysconfdir = NULL;
- socketdir = NULL;
- while ((length = es_read_line (fp, &line, &linelen, NULL)) > 0)
- {
- static const char *names[] =
- {
- "rootdir",
- "sysconfdir",
- "socketdir",
- ".enable"
- };
- int i;
- size_t n;
-
- /* Strip NL and CR, if present. */
- while (length > 0
- && (line[length - 1] == '\n' || line[length - 1] == '\r'))
- line[--length] = 0;
- trim_spaces (line);
- /* Find the stamement. */
- name = NULL;
- for (i=0; i < DIM (names); i++)
- {
- n = strlen (names[i]);
- if (!strncmp (line, names[i], n))
- {
- while (line[n] == ' ' || line[n] == '\t')
- n++;
- if (line[n] == '=')
- {
- name = names[i];
- p = line + n + 1;
- break;
- }
- }
- }
- if (!name)
- continue; /* Statement not known. */
- trim_spaces (p);
- p = substitute_envvars (p);
- if (!p)
- {
- err = gpg_error_from_syserror ();
- log_info ("error getting %s from gpgconf.ctl: %s\n",
- name, gpg_strerror (err));
- }
- else if (!strcmp (name, ".enable"))
- {
- if (atoi (p)
- || !ascii_strcasecmp (p, "yes")
- || !ascii_strcasecmp (p, "true")
- || !ascii_strcasecmp (p, "fact"))
- ; /* Yes, this file shall be used. */
- else
- ignoreall = 1; /* No, this file shall be ignored. */
- xfree (p);
- }
- else if (!strcmp (name, "sysconfdir"))
- {
- xfree (sysconfdir);
- sysconfdir = p;
- }
- else if (!strcmp (name, "socketdir"))
- {
- xfree (socketdir);
- socketdir = p;
- }
- else
- {
- xfree (rootdir);
- rootdir = p;
- }
- }
- if (es_ferror (fp))
- {
- err = gpg_error_from_syserror ();
- log_info ("error reading '%s': %s\n", buffer, gpg_strerror (err));
- es_fclose (fp);
- xfree (buffer);
- xfree (line);
- xfree (rootdir);
- xfree (sysconfdir);
- xfree (socketdir);
- checked = 1;
- return NULL;
- }
- es_fclose (fp);
+ /* Strip one part and expect the file below a bin dir. */
+ *p = 0;
+ p = xstrconcat (buffer, "/bin/gpgconf.ctl", NULL);
+ xfree (buffer);
+ buffer = p;
+ parse_gpgconf_ctl (buffer);
xfree (buffer);
- xfree (line);
-
- okay = 0;
- if (ignoreall)
- ;
- else if (!rootdir || !*rootdir || *rootdir != '/')
- {
- log_info ("invalid rootdir '%s' specified in gpgconf.ctl\n", rootdir);
- }
- else if (sysconfdir && (!*sysconfdir || *sysconfdir != '/'))
- {
- log_info ("invalid sysconfdir '%s' specified in gpgconf.ctl\n",
- sysconfdir);
- }
- else if (socketdir && (!*socketdir || *socketdir != '/'))
- {
- log_info ("invalid socketdir '%s' specified in gpgconf.ctl\n",
- socketdir);
- }
- else
- {
- okay = 1;
- while (*rootdir && rootdir[strlen (rootdir)-1] == '/')
- rootdir[strlen (rootdir)-1] = 0;
- dir = rootdir;
- gpgrt_annotate_leaked_object (dir);
- /* log_info ("want rootdir '%s'\n", dir); */
- if (sysconfdir)
- {
- while (*sysconfdir && sysconfdir[strlen (sysconfdir)-1] == '/')
- sysconfdir[strlen (sysconfdir)-1] = 0;
- sdir = sysconfdir;
- gpgrt_annotate_leaked_object (sdir);
- /* log_info ("want sysconfdir '%s'\n", sdir); */
- }
- if (socketdir)
- {
- while (*socketdir && socketdir[strlen (socketdir)-1] == '/')
- socketdir[strlen (socketdir)-1] = 0;
- s2dir = socketdir;
- gpgrt_annotate_leaked_object (s2dir);
- /* log_info ("want socketdir '%s'\n", s2dir); */
- }
- }
-
- if (!okay)
- {
- xfree (rootdir);
- xfree (sysconfdir);
- xfree (socketdir);
- dir = sdir = s2dir = NULL;
- }
- checked = 1;
}
+ if (!gpgconf_ctl.valid)
+ return NULL; /* No valid entries in gpgconf.ctl */
+
switch (wantdir)
{
- case WANTDIR_ROOT: return dir;
- case WANTDIR_SYSCONF: return sdir;
- case WANTDIR_SOCKET: return s2dir;
+ case WANTDIR_ROOT: return gpgconf_ctl.rootdir;
+ case WANTDIR_SYSCONF: return gpgconf_ctl.sysconfdir;
+ case WANTDIR_SOCKET: return gpgconf_ctl.socketdir;
}
return NULL; /* Not reached. */
@@ -982,7 +1135,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
if (w32_portable_app)
{
- name = xstrconcat (w32_rootdir (), DIRSEP_S, "gnupg", NULL);
+ name = xstrconcat (w32_rootdir (), DIRSEP_S, my_gnupg_dirname (), NULL);
}
else
{
@@ -993,7 +1146,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
NULL, 0);
if (path)
{
- name = xstrconcat (path, "\\gnupg", NULL);
+ name = xstrconcat (path, "\\", my_gnupg_dirname (), NULL);
xfree (path);
if (gnupg_access (name, F_OK))
gnupg_mkdir (name, "-rwx");
@@ -1101,10 +1254,11 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
};
int i;
struct stat sb;
- char prefixbuffer[19 + 1 + 20 + 6 + 1];
+ char prefixbuffer[256];
const char *prefix;
const char *s;
char *name = NULL;
+ const char *gnupgname = my_gnupg_dirname ();
*r_info = 0;
@@ -1143,12 +1297,13 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info)
goto leave;
}
- if (strlen (prefix) + 7 >= sizeof prefixbuffer)
+ if (strlen (prefix) + strlen (gnupgname) + 2 >= sizeof prefixbuffer)
{
*r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */
goto leave;
}
- strcat (prefixbuffer, "/gnupg");
+ strcat (prefixbuffer, "/");
+ strcat (prefixbuffer, gnupgname);
}
/* Check whether the gnupg sub directory (or the specified diretory)
@@ -1303,11 +1458,8 @@ gnupg_sysconfdir (void)
if (!name)
{
- const char *s1, *s2;
- s1 = w32_commondir ();
- s2 = DIRSEP_S "etc" DIRSEP_S "gnupg";
- name = xmalloc (strlen (s1) + strlen (s2) + 1);
- strcpy (stpcpy (name, s1), s2);
+ name = xstrconcat (w32_commondir (), DIRSEP_S, "etc", DIRSEP_S,
+ my_gnupg_dirname (), NULL);
gpgrt_annotate_leaked_object (name);
}
return name;
diff --git a/common/init.c b/common/init.c
index 62a48f8c7..8ea51c8b0 100644
--- a/common/init.c
+++ b/common/init.c
@@ -37,6 +37,7 @@
# include <winsock2.h>
# endif
# include <windows.h>
+# include <wctype.h>
#endif
#include <gcrypt.h>
diff --git a/common/iobuf.c b/common/iobuf.c
index 748e6935d..e46eeac95 100644
--- a/common/iobuf.c
+++ b/common/iobuf.c
@@ -166,7 +166,8 @@ block_filter_ctx_t;
/* Local prototypes. */
static int underflow (iobuf_t a, int clear_pending_eof);
static int underflow_target (iobuf_t a, int clear_pending_eof, size_t target);
-static int translate_file_handle (int fd, int for_write);
+static iobuf_t do_iobuf_fdopen (gnupg_fd_t fp, const char *mode, int keep_open);
+
/* Sends any pending data to the filter's FILTER function. Note: this
works on the filter and not on the whole pipeline. That is,
@@ -389,7 +390,7 @@ fd_cache_close (const char *fname, gnupg_fd_t fp)
close (fp);
#endif
if (DBG_IOBUF)
- log_debug ("fd_cache_close (%d) real\n", (int)fp);
+ log_debug ("fd_cache_close (%d) real\n", FD_DBG (fp));
return;
}
/* try to reuse a slot */
@@ -696,7 +697,7 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT)
{
if (DBG_IOBUF)
- log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f));
+ log_debug ("%s: close fd/handle %d\n", a->fname, FD_DBG (f));
if (!a->keep_open)
fd_cache_close (a->no_cache ? NULL : a->fname, f);
}
@@ -1410,7 +1411,7 @@ iobuf_is_pipe_filename (const char *fname)
{
if (!fname || (*fname=='-' && !fname[1]) )
return 1;
- return check_special_filename (fname, 0, 1) != -1;
+ return gnupg_check_special_filename (fname) != GNUPG_INVALID_FD;
}
@@ -1423,7 +1424,7 @@ do_open (const char *fname, int special_filenames,
file_filter_ctx_t *fcx;
size_t len = 0;
int print_only = 0;
- int fd;
+ gnupg_fd_t fd;
byte desc[MAX_IOBUF_DESC];
log_assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT);
@@ -1447,9 +1448,8 @@ do_open (const char *fname, int special_filenames,
else if (!fname)
return NULL;
else if (special_filenames
- && (fd = check_special_filename (fname, 0, 1)) != -1)
- return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1),
- opentype);
+ && (fd = gnupg_check_special_filename (fname)) != GNUPG_INVALID_FD)
+ return do_iobuf_fdopen (fd, opentype, 0);
else
{
if (use == IOBUF_INPUT)
@@ -1472,7 +1472,8 @@ do_open (const char *fname, int special_filenames,
file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n",
- a->no, a->subno, fname, iobuf_desc (a, desc), FD2INT (fcx->fp));
+ a->no, a->subno, fname, iobuf_desc (a, desc),
+ FD_DBG (fcx->fp));
return a;
}
@@ -1497,22 +1498,19 @@ iobuf_openrw (const char *fname)
static iobuf_t
-do_iobuf_fdopen (int fd, const char *mode, int keep_open)
+do_iobuf_fdopen (gnupg_fd_t fp, const char *mode, int keep_open)
{
iobuf_t a;
- gnupg_fd_t fp;
file_filter_ctx_t *fcx;
size_t len = 0;
- fp = INT2FD (fd);
-
a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
iobuf_buffer_size);
fcx = xmalloc (sizeof *fcx + 20);
fcx->fp = fp;
fcx->print_only_name = 1;
fcx->keep_open = keep_open;
- sprintf (fcx->fname, "[fd %d]", fd);
+ sprintf (fcx->fname, "[fd %d]", FD_DBG (fp));
a->filter = file_filter;
a->filter_ov = fcx;
file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
@@ -1525,15 +1523,15 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open)
iobuf_t
-iobuf_fdopen (int fd, const char *mode)
+iobuf_fdopen (gnupg_fd_t fp, const char *mode)
{
- return do_iobuf_fdopen (fd, mode, 0);
+ return do_iobuf_fdopen (fp, mode, 0);
}
iobuf_t
-iobuf_fdopen_nc (int fd, const char *mode)
+iobuf_fdopen_nc (gnupg_fd_t fp, const char *mode)
{
- return do_iobuf_fdopen (fd, mode, 1);
+ return do_iobuf_fdopen (fp, mode, 1);
}
@@ -1585,7 +1583,7 @@ iobuf_sockopen (int fd, const char *mode)
log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname);
iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
#else
- a = iobuf_fdopen (fd, mode);
+ a = do_iobuf_fdopen (fd, mode, 0);
#endif
return a;
}
@@ -2644,20 +2642,20 @@ iobuf_get_filelength (iobuf_t a)
}
-int
+gnupg_fd_t
iobuf_get_fd (iobuf_t a)
{
for (; a->chain; a = a->chain)
;
if (a->filter != file_filter)
- return -1;
+ return GNUPG_INVALID_FD;
{
file_filter_ctx_t *b = a->filter_ov;
gnupg_fd_t fp = b->fp;
- return FD2INT (fp);
+ return fp;
}
}
@@ -2948,36 +2946,6 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
return nbytes;
}
-static int
-translate_file_handle (int fd, int for_write)
-{
-#if defined(HAVE_W32_SYSTEM)
- {
- int x;
-
- (void)for_write;
-
- if (fd == 0)
- x = (int) GetStdHandle (STD_INPUT_HANDLE);
- else if (fd == 1)
- x = (int) GetStdHandle (STD_OUTPUT_HANDLE);
- else if (fd == 2)
- x = (int) GetStdHandle (STD_ERROR_HANDLE);
- else
- x = fd;
-
- if (x == -1)
- log_debug ("GetStdHandle(%d) failed: ec=%d\n",
- fd, (int) GetLastError ());
-
- fd = x;
- }
-#else
- (void)for_write;
-#endif
- return fd;
-}
-
void
iobuf_skip_rest (iobuf_t a, unsigned long n, int partial)
diff --git a/common/iobuf.h b/common/iobuf.h
index 04e6b4421..4354c718d 100644
--- a/common/iobuf.h
+++ b/common/iobuf.h
@@ -333,11 +333,11 @@ iobuf_t iobuf_openrw (const char *fname);
creates an input filter. Note: MODE must reflect the file
descriptors actual mode! When the filter is destroyed, the file
descriptor is closed. */
-iobuf_t iobuf_fdopen (int fd, const char *mode);
+iobuf_t iobuf_fdopen (gnupg_fd_t fd, const char *mode);
/* Like iobuf_fdopen, but doesn't close the file descriptor when the
filter is destroyed. */
-iobuf_t iobuf_fdopen_nc (int fd, const char *mode);
+iobuf_t iobuf_fdopen_nc (gnupg_fd_t fd, const char *mode);
/* Create a filter using an existing estream. If MODE contains the
letter 'w', creates an output filter. Otherwise, creates an input
@@ -590,7 +590,7 @@ uint64_t iobuf_get_filelength (iobuf_t a);
/* Return the file descriptor designating the underlying file. This
only works with file_filter based pipelines. */
-int iobuf_get_fd (iobuf_t a);
+gnupg_fd_t iobuf_get_fd (iobuf_t a);
/* Return the real filename, if available. This only supports
pipelines that end in file filters. Returns NULL if not
diff --git a/common/kem.c b/common/kem.c
new file mode 100644
index 000000000..0e498d37e
--- /dev/null
+++ b/common/kem.c
@@ -0,0 +1,220 @@
+/* kem.c - KEM helper functions
+ * Copyright (C) 2024 g10 Code GmbH.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute and/or modify this
+ * part of GnuPG 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 copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gpg-error.h>
+#include <gcrypt.h>
+#include "mischelp.h"
+
+
+/* domSeperation as per *PGP specs. */
+#define KMAC_KEY "OpenPGPCompositeKeyDerivationFunction"
+
+/* customizationString as per *PGP specs. */
+#define KMAC_CUSTOM "KDF"
+
+/* The blocksize used for Keccak by compute_kmac256. */
+#define KECCAK512_BLOCKSIZE 136
+
+
+
+static gpg_error_t
+compute_kmac256 (void *digest, size_t digestlen,
+ const void *key, size_t keylen,
+ const void *custom, size_t customlen,
+ gcry_buffer_t *data_iov, int data_iovlen)
+{
+ gpg_error_t err;
+ gcry_buffer_t iov[20];
+ const unsigned char headPAD[2] = { 1, KECCAK512_BLOCKSIZE };
+ unsigned char headK[3];
+ const unsigned char pad[KECCAK512_BLOCKSIZE] = { 0 };
+ unsigned char right_encode_L[3];
+ unsigned int len;
+ int iovcnt;
+
+ if (data_iovlen >= DIM(iov) - 6)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ /* Check the validity conditions of NIST SP 800-185 */
+ if (keylen >= 255 || customlen >= 255 || digestlen >= 255)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ iovcnt = 0;
+ iov[iovcnt].data = "KMAC";
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = 4;
+ iovcnt++;
+
+ iov[iovcnt].data = (void *)custom;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = customlen;
+ iovcnt++;
+
+ iov[iovcnt].data = (void *)headPAD;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = sizeof (headPAD);
+ iovcnt++;
+
+ if (keylen < 32)
+ {
+ headK[0] = 1;
+ headK[1] = (keylen*8)&0xff;
+ iov[iovcnt].data = headK;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = 2;
+ }
+ else
+ {
+ headK[0] = 2;
+ headK[1] = (keylen*8)>>8;
+ headK[2] = (keylen*8)&0xff;
+ iov[iovcnt].data = headK;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = 3;
+ }
+ iovcnt++;
+
+ iov[iovcnt].data = (void *)key;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = keylen;
+ iovcnt++;
+
+ len = iov[2].len + iov[3].len + iov[4].len;
+ len %= KECCAK512_BLOCKSIZE;
+
+ iov[iovcnt].data = (unsigned char *)pad;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = sizeof (pad) - len;
+ iovcnt++;
+
+ memcpy (&iov[iovcnt], data_iov, data_iovlen * sizeof (gcry_buffer_t));
+ iovcnt += data_iovlen;
+
+ if (digestlen < 32)
+ {
+ right_encode_L[0] = (digestlen * 8) & 0xff;
+ right_encode_L[1] = 1;
+ }
+ else
+ {
+ right_encode_L[0] = (digestlen * 8) >> 8;
+ right_encode_L[1] = (digestlen * 8) & 0xff;
+ right_encode_L[2] = 2;
+ }
+
+ iov[iovcnt].data = right_encode_L;
+ iov[iovcnt].off = 0;
+ iov[iovcnt].len = 3;
+ iovcnt++;
+
+ err = gcry_md_hash_buffers_ext (GCRY_MD_CSHAKE256, 0,
+ digest, digestlen, iov, iovcnt);
+ return err;
+}
+
+
+/* Compute KEK (shared secret) for ECC with HASHALGO, ECDH result,
+ ciphertext in ECC_CT, public key in ECC_PK. */
+gpg_error_t
+gnupg_ecc_kem_kdf (void *kek, size_t kek_len,
+ int hashalgo, const void *ecdh, size_t ecdh_len,
+ const void *ecc_ct, size_t ecc_ct_len,
+ const void *ecc_pk, size_t ecc_pk_len)
+{
+ gcry_buffer_t iov[3];
+ unsigned int dlen;
+
+ dlen = gcry_md_get_algo_dlen (hashalgo);
+ if (kek_len != dlen)
+ return gpg_error (GPG_ERR_INV_LENGTH);
+
+ memset (iov, 0, sizeof (iov));
+
+ iov[0].data = (unsigned char *)ecdh;
+ iov[0].len = ecdh_len;
+ iov[1].data = (unsigned char *)ecc_ct;
+ iov[1].len = ecc_ct_len;
+ iov[2].data = (unsigned char *)ecc_pk;
+ iov[2].len = ecc_pk_len;
+ gcry_md_hash_buffers (hashalgo, 0, kek, iov, 3);
+
+ return 0;
+}
+
+/* Compute KEK by combining two KEMs. The caller provides a buffer
+ * KEK allocated with size KEK_LEN which will receive the computed
+ * KEK. (ECC_SS, ECC_SS_LEN) is the shared secret of the first key.
+ * (ECC_CT, ECC_CT_LEN) is the ciphertext of the first key.
+ * (MLKEM_SS, ECC_SS_LEN) is the shared secret of the second key.
+ * (MLKEM_CT, MLKEM_CT_LEN) is the ciphertext of the second key.
+ * (FIXEDINFO, FIXEDINFO_LEN) is an octet string used to bind the KEK
+ * to a the key; for PGP we use the concatenation of the session key's
+ * algorithm id and the v5 fingerprint of the key.
+ */
+gpg_error_t
+gnupg_kem_combiner (void *kek, size_t kek_len,
+ const void *ecc_ss, size_t ecc_ss_len,
+ const void *ecc_ct, size_t ecc_ct_len,
+ const void *mlkem_ss, size_t mlkem_ss_len,
+ const void *mlkem_ct, size_t mlkem_ct_len,
+ const void *fixedinfo, size_t fixedinfo_len)
+{
+ gpg_error_t err;
+ gcry_buffer_t iov[6];
+
+ memset (iov, 0, sizeof (iov));
+
+ iov[0].data = "\x00\x00\x00\x01"; /* Counter */
+ iov[0].len = 4;
+
+ iov[1].data = (unsigned char *)ecc_ss;
+ iov[1].len = ecc_ss_len;
+
+ iov[2].data = (unsigned char *)ecc_ct;
+ iov[2].len = ecc_ct_len;
+
+ iov[3].data = (unsigned char *)mlkem_ss;
+ iov[3].len = mlkem_ss_len;
+
+ iov[4].data = (unsigned char *)mlkem_ct;
+ iov[4].len = mlkem_ct_len;
+
+ iov[5].data = (unsigned char *)fixedinfo;
+ iov[5].len = fixedinfo_len;
+
+ err = compute_kmac256 (kek, kek_len,
+ KMAC_KEY, strlen (KMAC_KEY),
+ KMAC_CUSTOM, strlen (KMAC_CUSTOM), iov, 6);
+ return err;
+}
diff --git a/common/miscellaneous.c b/common/miscellaneous.c
index 1a090b1f5..a41acc240 100644
--- a/common/miscellaneous.c
+++ b/common/miscellaneous.c
@@ -36,27 +36,6 @@
#include "iobuf.h"
#include "i18n.h"
-/* Used by libgcrypt for logging. */
-static void
-my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
-{
- (void)dummy;
-
- /* Map the log levels. */
- switch (level)
- {
- case GCRY_LOG_CONT: level = GPGRT_LOGLVL_CONT; break;
- case GCRY_LOG_INFO: level = GPGRT_LOGLVL_INFO; break;
- case GCRY_LOG_WARN: level = GPGRT_LOGLVL_WARN; break;
- case GCRY_LOG_ERROR:level = GPGRT_LOGLVL_ERROR; break;
- case GCRY_LOG_FATAL:level = GPGRT_LOGLVL_FATAL; break;
- case GCRY_LOG_BUG: level = GPGRT_LOGLVL_BUG; break;
- case GCRY_LOG_DEBUG:level = GPGRT_LOGLVL_DEBUG; break;
- default: level = GPGRT_LOGLVL_ERROR; break;
- }
- log_logv (level, fmt, arg_ptr);
-}
-
/* This function is called by libgcrypt on a fatal error. */
static void
@@ -100,7 +79,6 @@ my_gcry_outofcore_handler (void *opaque, size_t req_n, unsigned int flags)
void
setup_libgcrypt_logging (void)
{
- gcry_set_log_handler (my_gcry_logger, NULL);
gcry_set_fatalerror_handler (my_gcry_fatalerror_handler, NULL);
gcry_set_outofcore_handler (my_gcry_outofcore_handler, NULL);
}
@@ -687,3 +665,53 @@ parse_compatibility_flags (const char *string, unsigned int *flagvar,
*flagvar |= result;
return 0;
}
+
+
+/* Convert STRING consisting of base64 characters into its binary
+ * representation and store the result in a newly allocated buffer at
+ * R_BUFFER with its length at R_BUFLEN. If TITLE is NULL a plain
+ * base64 decoding is done. If it is the empty string the decoder
+ * will skip everything until a "-----BEGIN " line has been seen,
+ * decoding then ends at a "----END " line. On failure the function
+ * returns an error code and sets R_BUFFER to NULL. If the decoded
+ * data has a length of 0 a dummy buffer will still be allocated and
+ * the length is set to 0. */
+gpg_error_t
+b64decode (const char *string, const char *title,
+ void **r_buffer, size_t *r_buflen)
+{
+ gpg_error_t err;
+ gpgrt_b64state_t state;
+ size_t nbytes;
+ char *buffer;
+
+ *r_buffer = NULL;
+ *r_buflen = 0;
+
+ buffer = xtrystrdup (string);
+ if (!buffer)
+ return gpg_error_from_syserror();
+
+ state = gpgrt_b64dec_start (title);
+ if (!state)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (buffer);
+ return err;
+ }
+ err = gpgrt_b64dec_proc (state, buffer, strlen (buffer), &nbytes);
+ if (!err)
+ {
+ err = gpgrt_b64dec_finish (state);
+ state = NULL;
+ }
+ if (err)
+ xfree (buffer);
+ else
+ {
+ *r_buffer = buffer;
+ *r_buflen = nbytes;
+ }
+ gpgrt_b64dec_finish (state); /* Make sure it is released. */
+ return err;
+}
diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c
index 493054950..92f0dfbcd 100644
--- a/common/openpgp-oid.c
+++ b/common/openpgp-oid.c
@@ -43,23 +43,34 @@ static struct {
const char *oidstr; /* IETF formatted OID. */
unsigned int nbits; /* Nominal bit length of the curve. */
const char *alias; /* NULL or alternative name of the curve. */
+ const char *abbr; /* NULL or abbreviated name of the curve. */
int pubkey_algo; /* Required OpenPGP algo or 0 for ECDSA/ECDH. */
+ enum gcry_kem_algos kem_algo; /* 0 or the KEM algorithm for PQC. */
} oidtable[] = {
- { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", PUBKEY_ALGO_ECDH },
- { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", PUBKEY_ALGO_EDDSA },
- { "Curve25519", "1.3.101.110", 255, "cv25519", PUBKEY_ALGO_ECDH },
- { "Ed25519", "1.3.101.112", 255, "ed25519", PUBKEY_ALGO_EDDSA },
- { "X448", "1.3.101.111", 448, "cv448", PUBKEY_ALGO_ECDH },
- { "Ed448", "1.3.101.113", 456, "ed448", PUBKEY_ALGO_EDDSA },
+ { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", NULL,
+ PUBKEY_ALGO_ECDH, GCRY_KEM_RAW_X25519 /* only during development */},
+ { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", NULL,
+ PUBKEY_ALGO_EDDSA },
+ { "Curve25519", "1.3.101.110", 255, "cv25519", NULL,
+ PUBKEY_ALGO_ECDH, GCRY_KEM_RAW_X25519 },
+ { "Ed25519", "1.3.101.112", 255, "ed25519", NULL,
+ PUBKEY_ALGO_EDDSA },
+ { "X448", "1.3.101.111", 448, "cv448", NULL,
+ PUBKEY_ALGO_ECDH, GCRY_KEM_RAW_X448 },
+ { "Ed448", "1.3.101.113", 456, "ed448", NULL,
+ PUBKEY_ALGO_EDDSA },
{ "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" },
{ "NIST P-384", "1.3.132.0.34", 384, "nistp384" },
{ "NIST P-521", "1.3.132.0.35", 521, "nistp521" },
- { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256 },
- { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384 },
- { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512 },
+ { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256, NULL, "bp256",
+ 0, GCRY_KEM_RAW_BP256 },
+ { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384, NULL, "bp384",
+ 0, GCRY_KEM_RAW_BP384 },
+ { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512, NULL, "bp512",
+ 0, GCRY_KEM_RAW_BP512 },
{ "secp256k1", "1.3.132.0.10", 256 },
@@ -477,10 +488,20 @@ openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo)
/* Map an OpenPGP OID to the Libgcrypt curve name. Returns NULL for
- * unknown curve names. Unless CANON is set we prefer an alias name
- * here which is more suitable for printing. */
+ * unknown curve names. MODE defines which version of the curve name
+ * is returned. For example:
+ *
+ * | OID | mode=0 | mode=1 | mode=2 |
+ * |----------------------+-----------------+-----------------+----------|
+ * | 1.2.840.10045.3.1.7 | nistp256 | NIST P-256 | nistp256 |
+ * | 1.3.36.3.3.2.8.1.1.7 | brainpoolP256r1 | brainpoolP256r1 | bp256 |
+ *
+ * Thus mode 0 returns the name as commonly used gpg, mode 1 returns
+ * the canonical name, and mode 2 prefers an abbreviated name over the
+ * commonly used name.
+ */
const char *
-openpgp_oid_to_curve (const char *oidstr, int canon)
+openpgp_oid_to_curve (const char *oidstr, int mode)
{
int i;
@@ -489,7 +510,15 @@ openpgp_oid_to_curve (const char *oidstr, int canon)
for (i=0; oidtable[i].name; i++)
if (!strcmp (oidtable[i].oidstr, oidstr))
- return !canon && oidtable[i].alias? oidtable[i].alias : oidtable[i].name;
+ {
+ if (mode == 2)
+ {
+ if (oidtable[i].abbr)
+ return oidtable[i].abbr;
+ mode = 0; /* No abbreviation - fallback to mode 0. */
+ }
+ return !mode && oidtable[i].alias? oidtable[i].alias : oidtable[i].name;
+ }
return NULL;
}
@@ -517,6 +546,29 @@ openpgp_oid_or_name_to_curve (const char *oidname, int canon)
}
+/* Return the KEM algorithm id for the curve with OIDNAME. */
+enum gcry_kem_algos
+openpgp_oid_to_kem_algo (const char *oidname)
+{
+ int i;
+
+ if (!oidname)
+ return 0;
+
+ for (i=0; oidtable[i].name; i++)
+ if (!strcmp (oidtable[i].oidstr, oidname))
+ return oidtable[i].kem_algo;
+
+ for (i=0; oidtable[i].name; i++)
+ if (!ascii_strcasecmp (oidtable[i].name, oidname)
+ || (oidtable[i].alias
+ && !ascii_strcasecmp (oidtable[i].alias, oidname)))
+ return oidtable[i].kem_algo;
+
+ return 0;
+}
+
+
/* Return true if the curve with NAME is supported. */
static int
curve_supported_p (const char *name)
@@ -574,7 +626,9 @@ openpgp_is_curve_supported (const char *name, int *r_algo,
{
if ((!ascii_strcasecmp (name, oidtable[idx].name)
|| (oidtable[idx].alias
- && !ascii_strcasecmp (name, (oidtable[idx].alias))))
+ && !ascii_strcasecmp (name, (oidtable[idx].alias)))
+ || (oidtable[idx].abbr
+ && !ascii_strcasecmp (name, (oidtable[idx].abbr))))
&& curve_supported_p (oidtable[idx].name))
{
if (r_algo)
@@ -598,6 +652,7 @@ map_gcry_pk_to_openpgp (enum gcry_pk_algos algo)
case GCRY_PK_EDDSA: return PUBKEY_ALGO_EDDSA;
case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA;
case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH;
+ case GCRY_PK_KEM: return PUBKEY_ALGO_KYBER;
default: return algo < 110 ? (pubkey_algo_t)algo : 0;
}
}
diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h
index 180680bcc..bf11d597f 100644
--- a/common/openpgpdefs.h
+++ b/common/openpgpdefs.h
@@ -171,7 +171,11 @@ typedef enum
PUBKEY_ALGO_ECDSA = 19, /* RFC-6637 */
PUBKEY_ALGO_ELGAMAL = 20, /* Elgamal encrypt+sign (legacy). */
/* 21 reserved by OpenPGP. */
- PUBKEY_ALGO_EDDSA = 22, /* EdDSA (not yet assigned). */
+ PUBKEY_ALGO_EDDSA = 22, /* EdDSA. */
+ PUBKEY_ALGO_KYBER = 29, /* Kyber */
+ PUBKEY_ALGO_DIL3_25519 = 35, /* Dilithium3 + Ed25519 (aka ML-DSA-65) */
+ PUBKEY_ALGO_DIL5_448 = 36, /* Dilithium5 + Ed448 (aka ML-DSA-87) */
+ PUBKEY_ALGO_SPHINX_SHA2 = 41, /* SPHINX+-simple-SHA2 (aka SLH-DSA-SHA2) */
PUBKEY_ALGO_PRIVATE10 = 110
}
pubkey_algo_t;
@@ -206,7 +210,7 @@ compress_algo_t;
#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. */
+#define OPENPGP_MAX_NENC 4 /* Maximum number of encryption parameters. */
/* Decode an rfc4880 encoded S2K count. */
diff --git a/common/sexputil.c b/common/sexputil.c
index c7471be85..15fd7cf1d 100644
--- a/common/sexputil.c
+++ b/common/sexputil.c
@@ -992,7 +992,7 @@ get_pk_algo_from_key (gcry_sexp_t key)
gcry_sexp_t list;
const char *s;
size_t n;
- char algoname[6];
+ char algoname[10];
int algo = 0;
list = gcry_sexp_nth (key, 1);
@@ -1194,3 +1194,47 @@ cipher_mode_to_string (int mode)
default: return "[?]";
}
}
+
+/* Return the cannonical name of the ECC curve in KEY. */
+const char *
+get_ecc_curve_from_key (gcry_sexp_t key)
+{
+ gcry_sexp_t list = NULL;
+ gcry_sexp_t l2 = NULL;
+ const char *curve_name = NULL;
+ char *name = NULL;
+
+ /* Check that the first element is valid. */
+ list = gcry_sexp_find_token (key, "public-key", 0);
+ if (!list)
+ list = gcry_sexp_find_token (key, "private-key", 0);
+ if (!list)
+ list = gcry_sexp_find_token (key, "protected-private-key", 0);
+ if (!list)
+ list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
+ if (!list)
+ goto leave;
+
+ l2 = gcry_sexp_cadr (list);
+ gcry_sexp_release (list);
+ list = l2;
+ l2 = NULL;
+
+ name = gcry_sexp_nth_string (list, 0);
+ if (!name)
+ goto leave;
+
+ if (gcry_pk_map_name (name) != GCRY_PK_ECC)
+ goto leave;
+
+ l2 = gcry_sexp_find_token (list, "curve", 0);
+ xfree (name);
+ name = gcry_sexp_nth_string (l2, 1);
+ curve_name = openpgp_oid_or_name_to_curve (name, 1);
+ gcry_sexp_release (l2);
+
+ leave:
+ xfree (name);
+ gcry_sexp_release (list);
+ return curve_name;
+}
diff --git a/common/ssh-utils.c b/common/ssh-utils.c
index ab29558cf..d27e2e200 100644
--- a/common/ssh-utils.c
+++ b/common/ssh-utils.c
@@ -259,7 +259,7 @@ get_fingerprint (gcry_sexp_t key, int algo,
}
else
{
- struct b64state b64s;
+ gpgrt_b64state_t b64s;
estream_t stream;
char *p;
long int len;
@@ -273,15 +273,15 @@ get_fingerprint (gcry_sexp_t key, int algo,
goto leave;
}
- err = b64enc_start_es (&b64s, stream, "");
- if (err)
+ b64s = gpgrt_b64enc_start (stream, "");
+ if (!b64s)
{
es_fclose (stream);
goto leave;
}
- err = b64enc_write (&b64s,
- gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo));
+ err = gpgrt_b64enc_write (b64s, gcry_md_read (md, algo),
+ gcry_md_get_algo_dlen (algo));
if (err)
{
es_fclose (stream);
@@ -289,7 +289,7 @@ get_fingerprint (gcry_sexp_t key, int algo,
}
/* Finish, get the length, and close the stream. */
- err = b64enc_finish (&b64s);
+ err = gpgrt_b64enc_finish (b64s);
len = es_ftell (stream);
es_fclose (stream);
if (err)
@@ -566,7 +566,7 @@ ssh_public_key_in_base64 (gcry_sexp_t key, estream_t stream,
const char *identifier = NULL;
void *blob = NULL;
size_t bloblen;
- struct b64state b64_state;
+ gpgrt_b64state_t b64_state;
algo = get_pk_algo_from_key (key);
if (algo == 0)
@@ -624,15 +624,15 @@ ssh_public_key_in_base64 (gcry_sexp_t key, estream_t stream,
es_fprintf (stream, "%s ", identifier);
- err = b64enc_start_es (&b64_state, stream, "");
- if (err)
+ b64_state = gpgrt_b64enc_start (stream, "");
+ if (!b64_state)
{
es_free (blob);
- return err;
+ return gpg_error_from_syserror ();
}
- err = b64enc_write (&b64_state, blob, bloblen);
- b64enc_finish (&b64_state);
+ err = gpgrt_b64enc_write (b64_state, blob, bloblen);
+ gpgrt_b64enc_finish (b64_state);
es_free (blob);
if (err)
return err;
diff --git a/common/sysutils.c b/common/sysutils.c
index 90627b7c8..780af58bd 100644
--- a/common/sysutils.c
+++ b/common/sysutils.c
@@ -113,6 +113,8 @@ static int allow_special_filenames;
#ifdef HAVE_W32_SYSTEM
/* State of gnupg_inhibit_set_foregound_window. */
static int inhibit_set_foregound_window;
+/* Disable the use of _open_osfhandle. */
+static int no_translate_sys2libc_fd;
#endif
@@ -351,6 +353,16 @@ enable_special_filenames (void)
}
+/* Disable the use use of _open_osfhandle on Windows. */
+void
+disable_translate_sys2libc_fd (void)
+{
+#ifdef HAVE_W32_SYSTEM
+ no_translate_sys2libc_fd = 1;
+#endif
+}
+
+
/* Return a string which is used as a kind of process ID. */
const byte *
get_session_marker (size_t *rlen)
@@ -537,10 +549,10 @@ gnupg_usleep (unsigned int usecs)
different from the libc file descriptors (like open). This function
translates system file handles to libc file handles. FOR_WRITE
gives the direction of the handle. */
-int
+#if defined(HAVE_W32_SYSTEM)
+static int
translate_sys2libc_fd (gnupg_fd_t fd, int for_write)
{
-#if defined(HAVE_W32_SYSTEM)
int x;
if (fd == GNUPG_INVALID_FD)
@@ -552,24 +564,20 @@ translate_sys2libc_fd (gnupg_fd_t fd, int for_write)
if (x == -1)
log_error ("failed to translate osfhandle %p\n", (void *) fd);
return x;
-#else /*!HAVE_W32_SYSTEM */
- (void)for_write;
- return fd;
-#endif
}
+#endif /*!HAVE_W32_SYSTEM */
+
/* This is the same as translate_sys2libc_fd but takes an integer
- which is assumed to be such an system handle. On WindowsCE the
- passed FD is a rendezvous ID and the function finishes the pipe
- creation. */
+ which is assumed to be such an system handle. */
int
translate_sys2libc_fd_int (int fd, int for_write)
{
#ifdef HAVE_W32_SYSTEM
- if (fd <= 2)
- return fd; /* Do not do this for error, stdin, stdout, stderr. */
+ if (fd <= 2 || no_translate_sys2libc_fd)
+ return fd; /* Do not do this for stdin, stdout, and stderr. */
- return translate_sys2libc_fd ((void*)fd, for_write);
+ return translate_sys2libc_fd ((void*)(intptr_t)fd, for_write);
#else
(void)for_write;
return fd;
@@ -577,6 +585,70 @@ translate_sys2libc_fd_int (int fd, int for_write)
}
+/*
+ * Parse the string representation of a file reference (file handle on
+ * Windows or file descriptor on POSIX) in FDSTR. The string
+ * representation may be either of folllowing:
+
+ * (1) 0, 1, or 2 which means stdin, stdout, and stderr, respectively.
+ * (2) Integer representation (by %d of printf).
+ * (3) Hex representation which starts as "0x".
+ *
+ * Then, fill R_SYSHD, according to the value of a file reference.
+ *
+ */
+gpg_error_t
+gnupg_parse_fdstr (const char *fdstr, es_syshd_t *r_syshd)
+{
+ int fd = -1;
+#ifdef HAVE_W32_SYSTEM
+ gnupg_fd_t hd;
+ char *endptr;
+ int base;
+
+ if (!strcmp (fdstr, "0"))
+ fd = 0;
+ else if (!strcmp (fdstr, "1"))
+ fd = 1;
+ else if (!strcmp (fdstr, "2"))
+ fd = 2;
+
+ if (fd >= 0)
+ {
+ r_syshd->type = ES_SYSHD_FD;
+ r_syshd->u.fd = fd;
+ return 0;
+ }
+
+ if (!strncmp (fdstr, "0x", 2))
+ {
+ base = 16;
+ fdstr += 2;
+ }
+ else
+ base = 10;
+
+ gpg_err_set_errno (0);
+#ifdef _WIN64
+ hd = (gnupg_fd_t)strtoll (fdstr, &endptr, base);
+#else
+ hd = (gnupg_fd_t)strtol (fdstr, &endptr, base);
+#endif
+ if (errno != 0 || endptr == fdstr || *endptr != '\0')
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ r_syshd->type = ES_SYSHD_HANDLE;
+ r_syshd->u.handle = hd;
+ return 0;
+#else
+ fd = atoi (fdstr);
+ r_syshd->type = ES_SYSHD_FD;
+ r_syshd->u.fd = fd;
+ return 0;
+#endif
+}
+
+
/* Check whether FNAME has the form "-&nnnn", where N is a non-zero
* number. Returns this number or -1 if it is not the case. If the
* caller wants to use the file descriptor for writing FOR_WRITE shall
@@ -594,13 +666,74 @@ check_special_filename (const char *fname, int for_write, int notranslate)
for (i=0; digitp (fname+i); i++ )
;
if (!fname[i])
- return notranslate? atoi (fname)
- /**/ : translate_sys2libc_fd_int (atoi (fname), for_write);
+ {
+ if (notranslate)
+ return atoi (fname);
+ else
+ {
+ es_syshd_t syshd;
+
+ if (gnupg_parse_fdstr (fname, &syshd))
+ return -1;
+
+#ifdef HAVE_W32_SYSTEM
+ if (syshd.type == ES_SYSHD_FD)
+ return syshd.u.fd;
+ else
+ return translate_sys2libc_fd ((gnupg_fd_t)syshd.u.handle, for_write);
+#else
+ (void)for_write;
+ return syshd.u.fd;
+#endif
+ }
+ }
}
return -1;
}
+/* Check whether FNAME has the form "-&nnnn", where N is a number
+ * representing a file. Returns GNUPG_INVALID_FD if it is not the
+ * case. Returns a file descriptor on POSIX, a system handle on
+ * Windows. */
+gnupg_fd_t
+gnupg_check_special_filename (const char *fname)
+{
+ if (allow_special_filenames
+ && fname && *fname == '-' && fname[1] == '&')
+ {
+ int i;
+
+ fname += 2;
+ for (i=0; digitp (fname+i); i++ )
+ ;
+ if (!fname[i])
+ {
+ es_syshd_t syshd;
+
+ if (gnupg_parse_fdstr (fname, &syshd))
+ return GNUPG_INVALID_FD;
+
+#ifdef HAVE_W32_SYSTEM
+ if (syshd.type == ES_SYSHD_FD)
+ {
+ if (syshd.u.fd == 0)
+ return GetStdHandle (STD_INPUT_HANDLE);
+ else if (syshd.u.fd == 1)
+ return GetStdHandle (STD_OUTPUT_HANDLE);
+ else if (syshd.u.fd == 2)
+ return GetStdHandle (STD_ERROR_HANDLE);
+ }
+ else
+ return syshd.u.handle;
+#else
+ return syshd.u.fd;
+#endif
+ }
+ }
+ return GNUPG_INVALID_FD;
+}
+
/* Replacement for tmpfile(). This is required because the tmpfile
function of Windows' runtime library is broken, insecure, ignores
TMPDIR and so on. In addition we create a file with an inheritable
@@ -1158,6 +1291,19 @@ gnupg_setenv (const char *name, const char *value, int overwrite)
return setenv (name, value, overwrite);
#else /*!HAVE_SETENV*/
if (! getenv (name) || overwrite)
+#if defined(HAVE_W32_SYSTEM) && defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+ {
+ int e = _putenv_s (name, value);
+
+ if (e)
+ {
+ gpg_err_set_errno (e);
+ return -1;
+ }
+ else
+ return 0;
+ }
+#else
{
char *buf;
@@ -1175,6 +1321,7 @@ gnupg_setenv (const char *name, const char *value, int overwrite)
# endif
return putenv (buf);
}
+#endif /*!HAVE_W32_SYSTEM*/
return 0;
#endif /*!HAVE_SETENV*/
}
@@ -1199,6 +1346,18 @@ gnupg_unsetenv (const char *name)
#ifdef HAVE_UNSETENV
return unsetenv (name);
+#elif defined(HAVE_W32_SYSTEM) && defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+ {
+ int e = _putenv_s (name, "");
+
+ if (e)
+ {
+ gpg_err_set_errno (e);
+ return -1;
+ }
+ else
+ return 0;
+ }
#else /*!HAVE_UNSETENV*/
{
char *buf;
@@ -1829,3 +1988,22 @@ gnupg_fd_valid (int fd)
close (d);
return 1;
}
+
+
+/* Open a stream from FD (a file descriptor on POSIX, a system
+ handle on Windows), non-closed. */
+estream_t
+open_stream_nc (gnupg_fd_t fd, const char *mode)
+{
+ es_syshd_t syshd;
+
+#ifdef HAVE_W32_SYSTEM
+ syshd.type = ES_SYSHD_HANDLE;
+ syshd.u.handle = fd;
+#else
+ syshd.type = ES_SYSHD_FD;
+ syshd.u.fd = fd;
+#endif
+
+ return es_sysopen_nc (&syshd, mode);
+}
diff --git a/common/sysutils.h b/common/sysutils.h
index a78a81c64..9a90d1018 100644
--- a/common/sysutils.h
+++ b/common/sysutils.h
@@ -38,12 +38,20 @@
typedef void *gnupg_fd_t;
#define GNUPG_INVALID_FD ((void*)(-1))
#define INT2FD(s) ((void *)(s))
-#define FD2INT(h) ((unsigned int)(h))
+# ifdef _WIN64
+# define FD2INT(h) ((intptr_t)(h))
+# else
+# define FD2INT(h) ((unsigned int)(h))
+# endif
+#define FD_DBG(h) ((int)(intptr_t)(h))
+#define FD2NUM(h) ((int)(intptr_t)(h))
#else
typedef int gnupg_fd_t;
#define GNUPG_INVALID_FD (-1)
#define INT2FD(s) (s)
#define FD2INT(h) (h)
+#define FD_DBG(h) (h)
+#define FD2NUM(h) (h)
#endif
#ifdef HAVE_STAT
@@ -67,14 +75,17 @@ void trap_unaligned (void);
int disable_core_dumps (void);
int enable_core_dumps (void);
void enable_special_filenames (void);
+void disable_translate_sys2libc_fd (void);
+
const unsigned char *get_session_marker (size_t *rlen);
unsigned int get_uint_nonce (void);
/*int check_permissions (const char *path,int extension,int checkonly);*/
void gnupg_sleep (unsigned int seconds);
void gnupg_usleep (unsigned int usecs);
-int translate_sys2libc_fd (gnupg_fd_t fd, int for_write);
int translate_sys2libc_fd_int (int fd, int for_write);
+gpg_error_t gnupg_parse_fdstr (const char *fdstr, es_syshd_t *r_syshd);
int check_special_filename (const char *fname, int for_write, int notranslate);
+gnupg_fd_t gnupg_check_special_filename (const char *fname);
FILE *gnupg_tmpfile (void);
void gnupg_reopen_std (const char *pgmname);
void gnupg_inhibit_set_foregound_window (int yes);
@@ -108,6 +119,7 @@ gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname);
gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name);
int gnupg_inotify_has_name (int fd, const char *name);
+estream_t open_stream_nc (gnupg_fd_t fd, const char *mode);
#ifdef HAVE_W32_SYSTEM
int gnupg_w32_set_errno (int ec);
diff --git a/common/t-b64.c b/common/t-b64.c
deleted file mode 100644
index 16c079d1d..000000000
--- a/common/t-b64.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/* t-b64.c - Module tests for b64enc.c and b64dec.c
- * Copyright (C) 2008 Free Software Foundation, Inc.
- * Copyright (C) 2008, 2023 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 the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * 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 Lesser General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses/>.
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "util.h"
-
-#define pass() do { ; } while(0)
-#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\
- __FILE__,__LINE__, (a)); \
- errcount++; \
- } while(0)
-#define oops() do { fprintf (stderr, "%s:%d: ooops\n", \
- __FILE__,__LINE__); \
- exit (2); \
- } while(0)
-
-static int verbose;
-static int errcount;
-
-
-/* Convert STRING consisting of hex characters into its binary
- * representation and return it as an allocated buffer. The valid
- * length of the buffer is returned at R_LENGTH. The string is
- * delimited by end of string. The function returns NULL on
- * error. */
-static void *
-hex2buffer (const char *string, size_t *r_length)
-{
- const char *s;
- unsigned char *buffer;
- size_t length;
-
- buffer = xmalloc (strlen(string)/2+1);
- length = 0;
- for (s=string; *s; s +=2 )
- {
- if (!hexdigitp (s) || !hexdigitp (s+1))
- return NULL; /* Invalid hex digits. */
- ((unsigned char*)buffer)[length++] = xtoi_2 (s);
- }
- *r_length = length;
- return buffer;
-}
-
-
-static void
-test_b64decode (void)
-{
- static struct {
- const char *string; /* String to test. */
- const char *title; /* title parameter. */
- gpg_error_t err; /* expected error. */
- const char *datastr; /* Expected data (hex encoded) */
- } tests[] = {
- { "YQ==", NULL, 0,
- "61" },
- { "YWE==", NULL, 0,
- "6161" },
- { "YWFh", NULL, 0,
- "616161" },
- { "YWFhYQ==", NULL, 0,
- "61616161" },
- { "YWJjZA==", NULL, 0,
- "61626364" },
- { "AA=", NULL, 0,
- "00" },
- { "AAEA=", NULL, 0,
- "000100" },
- { "/w==", NULL, 0,
- "ff" },
- { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", NULL, 0,
- "a1143012a0030a0103a10b06092a864882f712010202" },
- { "oRQwEqADCgEDoQsGCSqGSIL3EgECA-==", NULL, GPG_ERR_BAD_DATA,
- "a1143012a0030a0103a10b06092a864882f712010202" },
- { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", "", 0,
- "" },
- { "-----BEGIN PGP\n\n"
- "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==\n"
- "-----END PGP\n", "", 0,
- "a1143012a0030a0103a10b06092a864882f712010202" },
-
- { "", NULL, 0,
- "" }
- };
- int tidx;
- gpg_error_t err;
- void *data = NULL;
- size_t datalen;
- char *wantdata = NULL;
- size_t wantdatalen;
-
- for (tidx = 0; tidx < DIM(tests); tidx++)
- {
- xfree (wantdata);
- if (!(wantdata = hex2buffer (tests[tidx].datastr, &wantdatalen)))
- oops ();
- xfree (data);
- err = b64decode (tests[tidx].string, tests[tidx].title, &data, &datalen);
- if (verbose)
- fprintf (stderr, "%s:%d: test %d, err=%d, datalen=%zu\n",
- __FILE__, __LINE__, tidx, err, datalen);
- if (gpg_err_code (err) != tests[tidx].err)
- fail (tidx);
- else if (err)
- pass ();
- else if (wantdatalen != datalen)
- fail (tidx);
- else if (memcmp (wantdata, data, datalen))
- fail (tidx);
- else
- pass ();
- }
- xfree (wantdata);
- xfree (data);
-}
-
-
-static void
-test_b64enc_pgp (const char *string)
-{
- gpg_error_t err;
- struct b64state state;
-
- if (!string)
- string = "a";
-
- err = b64enc_start (&state, stdout, "PGP MESSAGE");
- if (err)
- fail (1);
-
- err = b64enc_write (&state, string, strlen (string));
- if (err)
- fail (2);
-
- err = b64enc_finish (&state);
- if (err)
- fail (3);
-
- pass ();
-}
-
-
-static void
-test_b64enc_file (const char *fname)
-{
- gpg_error_t err;
- struct b64state state;
- FILE *fp;
- char buffer[50];
- size_t nread;
-
- fp = fname ? fopen (fname, "r") : stdin;
- if (!fp)
- {
- fprintf (stderr, "%s:%d: can't open '%s': %s\n",
- __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno));
- fail (0);
- }
-
- err = b64enc_start (&state, stdout, "DATA");
- if (err)
- fail (1);
-
- while ( (nread = fread (buffer, 1, sizeof buffer, fp)) )
- {
- err = b64enc_write (&state, buffer, nread);
- if (err)
- fail (2);
- }
-
- err = b64enc_finish (&state);
- if (err)
- fail (3);
-
- fclose (fp);
- pass ();
-}
-
-
-static void
-test_b64dec_file (const char *fname)
-{
- gpg_error_t err;
- struct b64state state;
- FILE *fp;
- char buffer[50];
- size_t nread, nbytes;
-
- fp = fname ? fopen (fname, "r") : stdin;
- if (!fp)
- {
- fprintf (stderr, "%s:%d: can't open '%s': %s\n",
- __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno));
- fail (0);
- }
-
- err = b64dec_start (&state, "");
- if (err)
- fail (1);
-
- while ( (nread = fread (buffer, 1, sizeof buffer, fp)) )
- {
- err = b64dec_proc (&state, buffer, nread, &nbytes);
- if (err)
- {
- if (gpg_err_code (err) == GPG_ERR_EOF)
- break;
- fail (2);
- }
- else if (nbytes)
- fwrite (buffer, 1, nbytes, stdout);
- }
-
- err = b64dec_finish (&state);
- if (err)
- fail (3);
-
- fclose (fp);
- pass ();
-}
-
-
-
-int
-main (int argc, char **argv)
-{
- int do_encode = 0;
- int do_decode = 0;
- int do_pgpdecode = 0;
-
- if (argc)
- { argc--; argv++; }
- if (argc && !strcmp (argv[0], "--verbose"))
- {
- verbose = 1;
- argc--; argv++;
- }
-
- if (argc && !strcmp (argv[0], "--encode"))
- {
- do_encode = 1;
- argc--; argv++;
- }
- else if (argc && !strcmp (argv[0], "--decode"))
- {
- do_decode = 1;
- argc--; argv++;
- }
- else if (argc)
- do_pgpdecode = 1;
-
- if (do_encode)
- test_b64enc_file (argc? *argv: NULL);
- else if (do_decode)
- test_b64dec_file (argc? *argv: NULL);
- else if (do_pgpdecode)
- test_b64enc_pgp (argc? *argv: NULL);
- else
- test_b64decode ();
-
- return !!errcount;
-}
diff --git a/common/t-exechelp.c b/common/t-exechelp.c
index 3bf082bbb..2179ef2a0 100644
--- a/common/t-exechelp.c
+++ b/common/t-exechelp.c
@@ -29,7 +29,7 @@
static int verbose;
-
+#ifndef HAVE_W32_SYSTEM
static void
print_open_fds (int *array)
{
@@ -169,20 +169,168 @@ test_close_all_fds (void)
}
}
+#endif
+
+static char buff12k[1024*12];
+static char buff4k[1024*4];
+
+static void
+run_server (void)
+{
+ estream_t fp;
+ int i;
+ char *p;
+ unsigned int len;
+ int ret;
+ es_syshd_t syshd;
+ size_t n;
+ off_t o;
+
+#ifdef HAVE_W32_SYSTEM
+ syshd.type = ES_SYSHD_HANDLE;
+ syshd.u.handle = (HANDLE)_get_osfhandle (1);
+#else
+ syshd.type = ES_SYSHD_FD;
+ syshd.u.fd = 1;
+#endif
+
+ fp = es_sysopen_nc (&syshd, "w");
+ if (fp == NULL)
+ {
+ fprintf (stderr, "es_fdopen failed\n");
+ exit (1);
+ }
+
+ /* Fill the buffer by ASCII chars. */
+ p = buff12k;
+ for (i = 0; i < sizeof (buff12k); i++)
+ if ((i % 64) == 63)
+ *p++ = '\n';
+ else
+ *p++ = (i % 64) + '@';
+
+ len = sizeof (buff12k);
+
+ ret = es_write (fp, (void *)&len, sizeof (len), NULL);
+ if (ret)
+ {
+ fprintf (stderr, "es_write (1) failed\n");
+ exit (1);
+ }
+
+ es_fflush (fp);
+
+ o = 0;
+ n = len;
+
+ while (1)
+ {
+ size_t n0, n1;
+
+ n0 = n > 4096 ? 4096 : n;
+ memcpy (buff4k, buff12k + o, n0);
+
+ ret = es_write (fp, buff4k, n0, &n1);
+ if (ret || n0 != n1)
+ {
+ fprintf (stderr, "es_write (2) failed\n");
+ exit (1);
+ }
+
+ o += n0;
+ n -= n0;
+ if (n == 0)
+ break;
+ }
+
+ es_fclose (fp);
+ exit (0);
+}
+
+
+static void
+test_pipe_stream (const char *pgmname)
+{
+ gpg_error_t err;
+ gnupg_process_t proc;
+ estream_t outfp;
+ const char *argv[2];
+ unsigned int len;
+ size_t n;
+ off_t o;
+ int ret;
+
+ argv[0] = "--server";
+ argv[1] = NULL;
+
+ err = gnupg_process_spawn (pgmname, argv,
+ (GNUPG_PROCESS_STDOUT_PIPE
+ |GNUPG_PROCESS_STDERR_KEEP),
+ NULL, NULL, &proc);
+ if (err)
+ {
+ fprintf (stderr, "gnupg_process_spawn failed\n");
+ exit (1);
+ }
+
+ gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL);
+
+ ret = es_read (outfp, (void *)&len, sizeof (len), NULL);
+ if (ret)
+ {
+ fprintf (stderr, "es_read (1) failed\n");
+ exit (1);
+ }
+
+ o = 0;
+ while (1)
+ {
+ if (es_feof (outfp))
+ break;
+
+ ret = es_read (outfp, buff4k, sizeof (buff4k), &n);
+ if (ret)
+ {
+ fprintf (stderr, "es_read (2) failed\n");
+ exit (1);
+ }
+
+ memcpy (buff12k + o, buff4k, n);
+ o += n;
+ }
+
+ if (o != sizeof (buff12k))
+ {
+ fprintf (stderr, "received data with wrong length %d\n", (int)o);
+ exit (1);
+ }
+ es_fclose (outfp);
+ gnupg_process_release (proc);
+}
int
main (int argc, char **argv)
{
+ const char *myname = "no-pgm";
+
if (argc)
- { argc--; argv++; }
+ {
+ myname = argv[0];
+ argc--; argv++;
+ }
if (argc && !strcmp (argv[0], "--verbose"))
{
verbose = 1;
argc--; argv++;
}
+ if (argc && !strcmp (argv[0], "--server"))
+ run_server ();
+#ifndef HAVE_W32_SYSTEM
test_close_all_fds ();
+#endif
+ test_pipe_stream (myname);
return 0;
}
diff --git a/common/t-iobuf.c b/common/t-iobuf.c
index bdeab99a4..aacf27a8b 100644
--- a/common/t-iobuf.c
+++ b/common/t-iobuf.c
@@ -1,3 +1,36 @@
+/* t-iobuf.c - Simple module test for iobuf.c
+ * Copyright (C) 2015 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
+ *
+ * - the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * or
+ *
+ * - the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
+ */
+
+/* The whole code here does not very fill into our general test frame
+ * work patter. But let's keep it as it is. */
+
#include <config.h>
#include <stdio.h>
#include <string.h>
@@ -7,6 +40,20 @@
#include "iobuf.h"
#include "stringhelp.h"
+
+static void *
+xmalloc (size_t n)
+{
+ void *p = malloc (n);
+ if (!p)
+ {
+ fprintf (stderr, "t-iobuf: out of core\n");
+ abort ();
+ }
+ return p;
+}
+
+
/* Return every other byte. In particular, reads two bytes, returns
the second one. */
static int
@@ -86,7 +133,7 @@ static struct content_filter_state *
content_filter_new (const char *buffer)
{
struct content_filter_state *state
- = malloc (sizeof (struct content_filter_state));
+ = xmalloc (sizeof (struct content_filter_state));
state->pos = 0;
state->len = strlen (buffer);
@@ -215,8 +262,7 @@ main (int argc, char *argv[])
allocate a buffer that is 5 bytes long, then no reallocation
should be required. */
size = 5;
- buffer = malloc (size);
- assert (buffer);
+ buffer = xmalloc (size);
max_len = 100;
n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
assert (n == 4);
@@ -229,7 +275,7 @@ main (int argc, char *argv[])
requires 6 bytes of storage. We pass a buffer that is 5 bytes
large and we allow the buffer to be grown. */
size = 5;
- buffer = malloc (size);
+ buffer = xmalloc (size);
max_len = 100;
n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
assert (n == 5);
@@ -243,7 +289,7 @@ main (int argc, char *argv[])
requires 7 bytes of storage. We pass a buffer that is 5 bytes
large and we don't allow the buffer to be grown. */
size = 5;
- buffer = malloc (size);
+ buffer = xmalloc (size);
max_len = 5;
n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
assert (n == 4);
diff --git a/common/util.h b/common/util.h
index 803ab3d5c..f8447aea7 100644
--- a/common/util.h
+++ b/common/util.h
@@ -149,36 +149,6 @@ ssize_t read_line (FILE *fp,
size_t *max_length);
-/*-- b64enc.c and b64dec.c --*/
-struct b64state
-{
- unsigned int flags;
- int idx;
- int quad_count;
- FILE *fp;
- estream_t stream;
- char *title;
- unsigned char radbuf[4];
- u32 crc;
- int stop_seen:1;
- int invalid_encoding:1;
- gpg_error_t lasterr;
-};
-
-gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title);
-gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp,
- const char *title);
-gpg_error_t b64enc_write (struct b64state *state,
- const void *buffer, size_t nbytes);
-gpg_error_t b64enc_finish (struct b64state *state);
-
-gpg_error_t b64dec_start (struct b64state *state, const char *title);
-gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length,
- size_t *r_nbytes);
-gpg_error_t b64dec_finish (struct b64state *state);
-gpg_error_t b64decode (const char *string, const char *title,
- void **r_buffer, size_t *r_buflen);
-
/*-- sexputil.c */
char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen);
void log_printcanon (const char *text,
@@ -226,6 +196,7 @@ char *pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid);
const char *pubkey_algo_to_string (int algo);
const char *hash_algo_to_string (int algo);
const char *cipher_mode_to_string (int mode);
+const char *get_ecc_curve_from_key (gcry_sexp_t key);
/*-- convert.c --*/
int hex2bin (const char *string, void *buffer, size_t length);
@@ -257,9 +228,10 @@ int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len);
int openpgp_oid_is_cv25519 (gcry_mpi_t a);
int openpgp_oid_is_cv448 (gcry_mpi_t a);
int openpgp_oid_is_ed448 (gcry_mpi_t a);
+enum gcry_kem_algos openpgp_oid_to_kem_algo (const char *oidname);
const char *openpgp_curve_to_oid (const char *name,
unsigned int *r_nbits, int *r_algo);
-const char *openpgp_oid_to_curve (const char *oid, int canon);
+const char *openpgp_oid_to_curve (const char *oid, int mode);
const char *openpgp_oid_or_name_to_curve (const char *oidname, int canon);
const char *openpgp_enum_curves (int *idxp);
const char *openpgp_is_curve_supported (const char *name,
@@ -274,6 +246,7 @@ void gnupg_set_homedir (const char *newdir);
void gnupg_maybe_make_homedir (const char *fname, int quiet);
const char *gnupg_homedir (void);
int gnupg_default_homedir_p (void);
+const char *gnupg_registry_dir (void);
const char *gnupg_daemon_rootdir (void);
const char *gnupg_socketdir (void);
const char *gnupg_sysconfdir (void);
@@ -328,6 +301,19 @@ char *gnupg_get_help_string (const char *key, int only_current_locale);
/*-- localename.c --*/
const char *gnupg_messages_locale_name (void);
+/*-- kem.c --*/
+gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len,
+ int hashalgo, const void *ecdh, size_t ecdh_len,
+ const void *ecc_ct, size_t ecc_ct_len,
+ const void *ecc_pk, size_t ecc_pk_len);
+
+gpg_error_t gnupg_kem_combiner (void *kek, size_t kek_len,
+ const void *ecc_ss, size_t ecc_ss_len,
+ const void *ecc_ct, size_t ecc_ct_len,
+ const void *mlkem_ss, size_t mlkem_ss_len,
+ const void *mlkem_ct, size_t mlkem_ct_len,
+ const void *fixedinfo, size_t fixedinfo_len);
+
/*-- miscellaneous.c --*/
/* This function is called at startup to tell libgcrypt to use our own
@@ -388,6 +374,10 @@ struct compatibility_flags_s
int parse_compatibility_flags (const char *string, unsigned int *flagvar,
const struct compatibility_flags_s *flags);
+gpg_error_t b64decode (const char *string, const char *title,
+ void **r_buffer, size_t *r_buflen);
+
+
/*-- Simple replacement functions. */