exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit c1935c6bdb302b719cd82102eb2a2ea498690380
parent a1bd70722b274622328ac8f992807148782694e5
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 11 Oct 2026 15:03:52 +0200

refuse to use key files that had too broad permissions and are thus insecure

Diffstat:
Msrc/util/secmod_common.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/util/secmod_common.h | 17+++++++++++++++++
Msrc/util/secmod_cs.c | 61++-----------------------------------------------------------
Msrc/util/secmod_eddsa.c | 61++-----------------------------------------------------------
Msrc/util/secmod_rsa.c | 76+++++++++++++++++-----------------------------------------------------------
5 files changed, 111 insertions(+), 177 deletions(-)

diff --git a/src/util/secmod_common.c b/src/util/secmod_common.c @@ -227,6 +227,79 @@ TES_normalize_key_directory (const struct GNUNET_CONFIGURATION_Handle *cfg, } +int +TES_open_key_file (const char *filename, + struct stat *sbuf) +{ + int fd; + + /* O_NOFOLLOW refuses symbolic links without a separate lstat(), and + O_NONBLOCK keeps us from hanging on a FIFO, which fstat() refuses. */ + fd = open (filename, + O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK); + if (-1 == fd) + { + /* Linux reports a symbolic link with ELOOP, some BSDs with EMLINK */ + if ( (ELOOP == errno) || + (EMLINK == errno) ) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "File `%s' is a symbolic link, which is not allowed for private keys; refusing to use it\n", + filename); + else + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "open", + filename); + return -1; + } + if (0 != fstat (fd, + sbuf)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "fstat", + filename); + GNUNET_break (0 == close (fd)); + return -1; + } + if (! S_ISREG (sbuf->st_mode)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "File `%s' is not a regular file, which is not allowed for private keys; refusing to use it\n", + filename); + GNUNET_break (0 == close (fd)); + return -1; + } + if (geteuid () != sbuf->st_uid) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Private key file `%s' is owned by uid %u, not by our uid %u; refusing to use it\n", + filename, + (unsigned int) sbuf->st_uid, + (unsigned int) geteuid ()); + GNUNET_break (0 == close (fd)); + return -1; + } + if (1 != sbuf->st_nlink) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Private key file `%s' has %llu hard links instead of one; refusing to use it\n", + filename, + (unsigned long long) sbuf->st_nlink); + GNUNET_break (0 == close (fd)); + return -1; + } + if (0 != (sbuf->st_mode & (S_IRWXG | S_IRWXO))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Private key file `%s' is accessible by group or others (mode %04o); refusing to use it, restrict it to its owner (chmod 400)\n", + filename, + (unsigned int) (sbuf->st_mode & 07777)); + GNUNET_break (0 == close (fd)); + return -1; + } + return fd; +} + + /** * Head of DLL of clients connected to us. */ diff --git a/src/util/secmod_common.h b/src/util/secmod_common.h @@ -57,6 +57,23 @@ TES_normalize_key_directory (const struct GNUNET_CONFIGURATION_Handle *cfg, /** + * Open the private key file @a filename for reading and check that it may + * be used: it must be a regular file (not a symbolic link), be owned by our + * effective user, have exactly one hard link and grant no permissions to + * group or others. All checks are made with fstat() on the opened file + * descriptor. A refused file is logged at ERROR level with the reason and + * left untouched. + * + * @param filename name of the key file + * @param[out] sbuf set to the status of the opened file + * @return file descriptor open for reading, -1 if the file must not be used + */ +int +TES_open_key_file (const char *filename, + struct stat *sbuf); + + +/** * Create the listen socket for a secmod daemon. * * This function is not thread-safe, as it changes and diff --git a/src/util/secmod_cs.c b/src/util/secmod_cs.c @@ -2030,67 +2030,10 @@ import_key (void *cls, int fd; struct stat sbuf; - { - struct stat lsbuf; - - if (0 != lstat (filename, - &lsbuf)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "lstat", - filename); - return GNUNET_OK; - } - if (! S_ISREG (lsbuf.st_mode)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' is not a regular file, which is not allowed for private keys!\n", - filename); - return GNUNET_OK; - } - } - - fd = open (filename, - O_RDONLY | O_CLOEXEC); + fd = TES_open_key_file (filename, + &sbuf); if (-1 == fd) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "open", - filename); - return GNUNET_OK; - } - if (0 != fstat (fd, - &sbuf)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "stat", - filename); - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - if (! S_ISREG (sbuf.st_mode)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' is not a regular file, which is not allowed for private keys!\n", - filename); - GNUNET_break (0 == close (fd)); return GNUNET_OK; - } - if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO))) - { - /* permission are NOT tight, try to patch them up! */ - if (0 != - fchmod (fd, - S_IRUSR)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fchmod", - filename); - /* refuse to use key if file has wrong permissions */ - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - } fh = GNUNET_DISK_get_handle_from_int_fd (fd); if (NULL == fh) { diff --git a/src/util/secmod_eddsa.c b/src/util/secmod_eddsa.c @@ -881,67 +881,10 @@ import_key (void *cls, struct stat sbuf; (void) cls; - { - struct stat lsbuf; - - if (0 != lstat (filename, - &lsbuf)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "lstat", - filename); - return GNUNET_OK; - } - if (! S_ISREG (lsbuf.st_mode)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' is not a regular file, which is not allowed for private keys!\n", - filename); - return GNUNET_OK; - } - } - - fd = open (filename, - O_RDONLY | O_CLOEXEC); + fd = TES_open_key_file (filename, + &sbuf); if (-1 == fd) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "open", - filename); return GNUNET_OK; - } - if (0 != fstat (fd, - &sbuf)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "stat", - filename); - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - if (! S_ISREG (sbuf.st_mode)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' is not a regular file, which is not allowed for private keys!\n", - filename); - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO))) - { - /* permission are NOT tight, try to patch them up! */ - if (0 != - fchmod (fd, - S_IRUSR)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fchmod", - filename); - /* refuse to use key if file has wrong permissions */ - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - } fh = GNUNET_DISK_get_handle_from_int_fd (fd); if (NULL == fh) { diff --git a/src/util/secmod_rsa.c b/src/util/secmod_rsa.c @@ -1711,6 +1711,21 @@ parse_key (struct Denomination *denom, GNUNET_free (nf); return; } + /* refuse keys whose modulus does not have the configured size */ + if (denom->rsa_keysize != + GNUNET_CRYPTO_rsa_public_key_len (pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "File `%s' holds a %u-bit RSA key, but RSA_KEYSIZE of section `%s' is %u; refusing to use it\n", + (NULL == nf) ? filename : nf, + GNUNET_CRYPTO_rsa_public_key_len (pub), + denom->section, + (unsigned int) denom->rsa_keysize); + GNUNET_CRYPTO_rsa_public_key_free (pub); + GNUNET_CRYPTO_rsa_private_key_free (priv); + GNUNET_free (nf); + return; + } dk = GNUNET_new (struct DenominationKey); dk->denom_priv = priv; dk->denom = denom; @@ -1780,67 +1795,10 @@ import_key (void *cls, int fd; struct stat sbuf; - { - struct stat lsbuf; - - if (0 != lstat (filename, - &lsbuf)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "lstat", - filename); - return GNUNET_OK; - } - if (! S_ISREG (lsbuf.st_mode)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' is not a regular file, which is not allowed for private keys!\n", - filename); - return GNUNET_OK; - } - } - - fd = open (filename, - O_RDONLY | O_CLOEXEC); + fd = TES_open_key_file (filename, + &sbuf); if (-1 == fd) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "open", - filename); - return GNUNET_OK; - } - if (0 != fstat (fd, - &sbuf)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "stat", - filename); - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - if (! S_ISREG (sbuf.st_mode)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "File `%s' is not a regular file, which is not allowed for private keys!\n", - filename); - GNUNET_break (0 == close (fd)); return GNUNET_OK; - } - if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO))) - { - /* permission are NOT tight, try to patch them up! */ - if (0 != - fchmod (fd, - S_IRUSR)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fchmod", - filename); - /* refuse to use key if file has wrong permissions */ - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - } fh = GNUNET_DISK_get_handle_from_int_fd (fd); if (NULL == fh) {