From 42f3b2f97510ab364717154c01a03f5ddd9541a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Nov 2019 23:13:42 +0100 Subject: [PATCH] shared: split out crypt() specific helpers into its own .c/.h in src/shared/ This way we can use libxcrypt specific functionality such as crypt_gensalt() and thus take benefit of the newer algorithms libxcrypt implements. (Also adds support for a new env var $SYSTEMD_CRYPT_PREFIX which may be used to select the hash algorithm to use for libxcrypt.) Also, let's move the weird crypt.h inclusion into libcrypt.h so that there's a single place for it. --- docs/ENVIRONMENT.md | 7 ++++ src/basic/user-util.c | 37 ------------------- src/basic/user-util.h | 2 - src/firstboot/firstboot.c | 14 +------ src/shared/libcrypt-util.c | 75 ++++++++++++++++++++++++++++++++++++++ src/shared/libcrypt-util.h | 20 ++++++++++ src/shared/meson.build | 3 ++ src/test/test-user-util.c | 1 + 8 files changed, 107 insertions(+), 52 deletions(-) create mode 100644 src/shared/libcrypt-util.c create mode 100644 src/shared/libcrypt-util.h diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 9b1df6b59c..fec40b1e9c 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -73,6 +73,13 @@ All tools: appropriate path under /run. This variable is also set by the manager when RuntimeDirectory= is used, see systemd.exec(5). +* `$SYSTEMD_CRYPT_PREFIX` — if set configures the hash method prefix to use for + UNIX crypt() when generating passwords. By default the system's "preferred + method" is used, but this can be overridden with this environment + variable. Takes a prefix such as `$6$` or `$y$`. (Note that this is only + honoured on systems built with libxcrypt and is ignored on systems using + glibc's original, internal crypt() implementation.) + systemctl: * `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 571578c4e4..68c6dd0da7 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -945,40 +945,3 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg) { return !!s; } #endif - -int make_salt(char **ret) { - static const char table[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - "./"; - - uint8_t raw[16]; - char *salt, *j; - size_t i; - int r; - - /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but - * SHA512, i.e. is legacy-free and minimizes our deps. */ - - assert_cc(sizeof(table) == 64U + 1U); - - /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */ - r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK); - if (r < 0) - return r; - - salt = new(char, 3+sizeof(raw)+1+1); - if (!salt) - return -ENOMEM; - - /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ - j = stpcpy(salt, "$6$"); - for (i = 0; i < sizeof(raw); i++) - j[i] = table[raw[i] & 63]; - j[i++] = '$'; - j[i] = 0; - - *ret = salt; - return 0; -} diff --git a/src/basic/user-util.h b/src/basic/user-util.h index e11dd9b379..714e83082c 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -137,6 +137,4 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg); int putsgent_sane(const struct sgrp *sg, FILE *stream); #endif -int make_salt(char **ret); - bool is_nologin_shell(const char *shell); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 528e6452cf..8001f90d57 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -4,19 +4,6 @@ #include #include -#if HAVE_CRYPT_H -/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be - * removed from glibc at some point. As part of the removal, defines for - * crypt(3) are dropped from unistd.h, and we must include crypt.h instead. - * - * Newer versions of glibc (v2.0+) already ship crypt.h with a definition - * of crypt(3) as well, so we simply include it if it is present. MariaDB, - * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the - * same way since ages without any problems. - */ -# include -#endif - #include "sd-id128.h" #include "alloc-util.h" @@ -28,6 +15,7 @@ #include "fs-util.h" #include "hostname-util.h" #include "kbd-util.h" +#include "libcrypt-util.h" #include "locale-util.h" #include "main-func.h" #include "memory-util.h" diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c new file mode 100644 index 0000000000..b1a8168030 --- /dev/null +++ b/src/shared/libcrypt-util.c @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include + +#include "alloc-util.h" +#include "libcrypt-util.h" +#include "log.h" +#include "macro.h" +#include "missing_stdlib.h" +#include "random-util.h" +#include "string-util.h" +#include "strv.h" + +int make_salt(char **ret) { + +#ifdef XCRYPT_VERSION_MAJOR + const char *e; + char *salt; + + /* If we have libxcrypt we default to the "preferred method" (i.e. usually yescrypt), and generate it + * with crypt_gensalt_ra(). */ + + e = secure_getenv("SYSTEMD_CRYPT_PREFIX"); + if (!e) + e = crypt_preferred_method(); + + log_debug("Generating salt for hash prefix: %s", e); + + salt = crypt_gensalt_ra(e, 0, NULL, 0); + if (!salt) + return -errno; + + *ret = salt; + return 0; +#else + /* If libxcrypt is not used, we use SHA512 and generate the salt on our own since crypt_gensalt_ra() + * is not available. */ + + static const char table[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "./"; + + uint8_t raw[16]; + char *salt, *j; + size_t i; + int r; + + /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but + * SHA512, i.e. is legacy-free and minimizes our deps. */ + + assert_cc(sizeof(table) == 64U + 1U); + + /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */ + r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK); + if (r < 0) + return r; + + salt = new(char, 3+sizeof(raw)+1+1); + if (!salt) + return -ENOMEM; + + /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ + j = stpcpy(salt, "$6$"); + for (i = 0; i < sizeof(raw); i++) + j[i] = table[raw[i] & 63]; + j[i++] = '$'; + j[i] = 0; + + *ret = salt; + return 0; +#endif +} diff --git a/src/shared/libcrypt-util.h b/src/shared/libcrypt-util.h new file mode 100644 index 0000000000..3da1ab5ad9 --- /dev/null +++ b/src/shared/libcrypt-util.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#if HAVE_CRYPT_H +/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be + * removed from glibc at some point. As part of the removal, defines for + * crypt(3) are dropped from unistd.h, and we must include crypt.h instead. + * + * Newer versions of glibc (v2.0+) already ship crypt.h with a definition + * of crypt(3) as well, so we simply include it if it is present. MariaDB, + * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the + * same way since ages without any problems. + */ +#include +#endif + +#include +#include + +int make_salt(char **ret); diff --git a/src/shared/meson.build b/src/shared/meson.build index 08aa480b6c..f4f5ebf3c4 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -106,6 +106,8 @@ shared_sources = files(''' json-internal.h json.c json.h + libcrypt-util.c + libcrypt-util.h libmount-util.h linux/auto_dev-ioctl.h linux/bpf.h @@ -279,6 +281,7 @@ libshared_deps = [threads, libacl, libblkid, libcap, + libcrypt, libcryptsetup, libgcrypt, libidn, diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index fde8336bf9..6bfe7dc7e1 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -2,6 +2,7 @@ #include "alloc-util.h" #include "format-util.h" +#include "libcrypt-util.h" #include "log.h" #include "macro.h" #include "memory-util.h"