mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
confd: add support for $0$cleartext password
Finalize support for IETF System YANG incl. all IANA crypt-hash types. This patch builds on the earlier work adding yescrypt and $factory$ key word. The YANG description for the crypt-hash type override has been significantly udpated to discourage use of $0$cleartext passwords. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -11,3 +11,39 @@ config BR2_PACKAGE_CONFD
|
||||
either the on-system CLI or using NETCONF.
|
||||
|
||||
https://github.com/kernelkit/infix
|
||||
|
||||
if BR2_PACKAGE_CONFD
|
||||
|
||||
choice
|
||||
prompt "Default password crypt"
|
||||
default BR2_PACKAGE_CONFD_YESCRYPT
|
||||
help
|
||||
When users set $0$cleartext-password as their password confd
|
||||
salts and encrypts the "cleartext-password" with this selected
|
||||
hash algorithm.
|
||||
|
||||
Infix defaults to yescrypt.
|
||||
|
||||
config BR2_PACKAGE_CONFD_MD5CRYPT
|
||||
bool "md5crypt"
|
||||
help
|
||||
|
||||
config BR2_PACKAGE_CONFD_SHA256CRYPT
|
||||
bool "sha256crypt"
|
||||
|
||||
config BR2_PACKAGE_CONFD_SHA512CRYPT
|
||||
bool "sha512crypt"
|
||||
|
||||
config BR2_PACKAGE_CONFD_YESCRYPT
|
||||
bool "yescrypt"
|
||||
|
||||
endchoice
|
||||
|
||||
config BR2_PACKAGE_CONFD_DEFAULT_CRYPT
|
||||
string
|
||||
default "md5crypt" if BR2_PACKAGE_CONFD_MD5CRYPT
|
||||
default "sha256crypt" if BR2_PACKAGE_CONFD_SHA256CRYPT
|
||||
default "sha512crypt" if BR2_PACKAGE_CONFD_SHA512CRYPT
|
||||
default "yescrypt" if BR2_PACKAGE_CONFD_YESCRYPT
|
||||
|
||||
endif
|
||||
|
||||
@@ -12,7 +12,7 @@ CONFD_LICENSE_FILES = LICENSE
|
||||
CONFD_REDISTRIBUTE = NO
|
||||
CONFD_DEPENDENCIES = host-sysrepo sysrepo netopeer2 jansson libite sysrepo libsrx libglib2
|
||||
CONFD_AUTORECONF = YES
|
||||
CONFD_CONF_OPTS += --disable-silent-rules
|
||||
CONFD_CONF_OPTS += --disable-silent-rules --with-crypt=$(BR2_PACKAGE_CONFD_DEFAULT_CRYPT)
|
||||
CONFD_SYSREPO_SHM_PREFIX = sr_buildroot$(subst /,_,$(CONFIG_DIR))_confd
|
||||
|
||||
define CONFD_CONF_ENV
|
||||
|
||||
@@ -36,6 +36,10 @@ AC_ARG_WITH(login-shell,
|
||||
AS_HELP_STRING([--with-login-shell=shell], [Login shell for new users, default: /bin/false]),
|
||||
[login_shell=$withval], [login_shell=yes])
|
||||
|
||||
AC_ARG_WITH(crypt,
|
||||
AS_HELP_STRING([--with-crypt=crypt], [Default crypt for $0$cleartext, default: yescrypt]),
|
||||
[crypt=$withval], [crypt="yescrypt"])
|
||||
|
||||
AS_IF([test "x$enable_containers" = "xyes"], [
|
||||
AC_DEFINE(CONTAINERS, 1, [Built with container support])])
|
||||
|
||||
@@ -44,9 +48,15 @@ AS_IF([test "x$with_login_shell" != "xno"], [
|
||||
AC_DEFINE_UNQUOTED(LOGIN_SHELL, "$login_shell", [Default: /bin/false])],[
|
||||
AC_DEFINE_UNQUOTED(LOGIN_SHELL, "/bin/false")])
|
||||
|
||||
AS_IF([test "x$with_crypt" != "xno"], [
|
||||
AS_IF([test "x$crypt" = "xyes"], [crypt=yescrypt])
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_CRYPT, "$crypt", [Default: yescrypt])],[
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_CRYPT, "yescrypt")])
|
||||
|
||||
# Check for pkg-config first, warn if it's not installed
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
PKG_CHECK_MODULES([crypt], [libxcrypt >= 4.4.36])
|
||||
PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.50 gio-2.0 gio-unix-2.0])
|
||||
PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
|
||||
PKG_CHECK_MODULES([libite], [libite >= 2.5.0])
|
||||
@@ -104,6 +114,7 @@ cat <<EOF
|
||||
Optional features:
|
||||
Container support ....: $enable_containers
|
||||
Login shell ..........: $login_shell
|
||||
Default crypt algo ...: $crypt
|
||||
|
||||
------------- Compiler version --------------
|
||||
$($CC --version || true)
|
||||
|
||||
@@ -6,6 +6,7 @@ plugin_LTLIBRARIES = confd-plugin.la
|
||||
confd_plugin_la_LDFLAGS = -module -avoid-version -shared
|
||||
|
||||
confd_plugin_la_CFLAGS = \
|
||||
$(crypt_CFLAGS) \
|
||||
$(glib_CFLAGS) \
|
||||
$(jansson_CFLAGS) \
|
||||
$(libite_CFLAGS) \
|
||||
@@ -14,6 +15,7 @@ confd_plugin_la_CFLAGS = \
|
||||
$(CFLAGS)
|
||||
|
||||
confd_plugin_la_LIBADD = \
|
||||
$(crypt_LIBS) \
|
||||
$(glib_LIBS) \
|
||||
$(jansson_LIBS) \
|
||||
$(libite_LIBS) \
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <assert.h>
|
||||
#include <crypt.h>
|
||||
#include <ctype.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
@@ -1219,6 +1220,87 @@ err_release_data:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const char *ctp_crypt(void)
|
||||
{
|
||||
struct {
|
||||
const char *crypt;
|
||||
const char *prefix;
|
||||
} list[] = {
|
||||
{ "md5crypt", "$1$" },
|
||||
{ "sha256crypt", "$5$" },
|
||||
{ "sha512crypt", "$6$" },
|
||||
{ "yescrypt", "$y$" },
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(list); i++) {
|
||||
if (strcmp(list[i].crypt, DEFAULT_CRYPT))
|
||||
continue;
|
||||
|
||||
return list[i].prefix;
|
||||
}
|
||||
|
||||
return "$y$"; /* fallback */
|
||||
}
|
||||
|
||||
static int ctp_hash_is_cleartext(const char *hash)
|
||||
{
|
||||
if (strlen(hash) < 3)
|
||||
return 0;
|
||||
|
||||
if (hash[0] == '$' && hash[1] == '0' && hash[2] == '$')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sr_error_t check_user_ctp(sr_session_ctx_t *session, struct confd *_, struct sr_change *change)
|
||||
{
|
||||
const char *prefix = ctp_crypt();
|
||||
char *hash, *salt;
|
||||
sr_val_t *val;
|
||||
|
||||
val = change->new;
|
||||
if (!val)
|
||||
return SR_ERR_OK;
|
||||
|
||||
hash = val->data.string_val;
|
||||
if (!ctp_hash_is_cleartext(hash))
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (strlen(hash) < 4) {
|
||||
sr_session_set_error(session, NULL, SR_ERR_VALIDATION_FAILED, "Too short password.");
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
|
||||
salt = crypt_gensalt(prefix, 0, NULL, 0);
|
||||
if (!salt) {
|
||||
sr_session_set_error(session, NULL, SR_ERR_INTERNAL, "error %d generating salt, "
|
||||
"prefix '%s'.", errno, prefix);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
hash = crypt(hash + 3, salt);
|
||||
if (!hash) {
|
||||
sr_session_set_error(session, NULL, SR_ERR_INTERNAL, "failed hashing password, "
|
||||
"error %d.", errno);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
return sr_set_item_str(session, val->xpath, hash, NULL, 0);
|
||||
}
|
||||
|
||||
static sr_error_t change_auth_ctp(struct confd *confd, sr_session_ctx_t *session)
|
||||
{
|
||||
sr_error_t err;
|
||||
|
||||
err = _sr_change_iter(session, confd, XPATH_AUTH_"/user[*]/password", check_user_ctp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static sr_error_t change_auth_check(struct confd *confd, sr_session_ctx_t *session)
|
||||
{
|
||||
sr_error_t err;
|
||||
@@ -1262,10 +1344,16 @@ static int change_auth(sr_session_ctx_t *session, uint32_t sub_id, const char *m
|
||||
{
|
||||
struct confd *confd = (struct confd *)priv;
|
||||
|
||||
if (event == SR_EV_CHANGE)
|
||||
switch (event) {
|
||||
case SR_EV_UPDATE:
|
||||
return change_auth_ctp(confd, session);
|
||||
case SR_EV_CHANGE:
|
||||
return change_auth_check(confd, session);
|
||||
if (event == SR_EV_DONE)
|
||||
case SR_EV_DONE:
|
||||
return change_auth_done(confd, session);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
@@ -1670,7 +1758,7 @@ int ietf_system_init(struct confd *confd)
|
||||
|
||||
os_init();
|
||||
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_AUTH_, 0, change_auth, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_AUTH_, SR_SUBSCR_UPDATE, change_auth, confd, &confd->sub);
|
||||
REGISTER_OPER(confd->session, "ietf-system", PASSWORD_PATH, auth_cb, confd, 0, &confd->sub);
|
||||
REGISTER_MONITOR(confd->session, "ietf-netconf-acm", "/ietf-netconf-acm:nacm//.",
|
||||
0, change_nacm, confd, &confd->sub);
|
||||
|
||||
@@ -125,14 +125,13 @@ module infix-system {
|
||||
+ '|$factory$.*';
|
||||
}
|
||||
description
|
||||
"The crypt-hash type is used to store passwords using a hash
|
||||
function. This type extends the existing crypt-hash type to
|
||||
support yescrypt as well as the reserved string $factory$,
|
||||
which is used for device-specific factory default hash. It
|
||||
is up to the underlying system to define this further, but
|
||||
one example is to use Vital Product Data (VPD) information,
|
||||
e.g., an onboard EEPROM where a device hash is stored for
|
||||
the initial 'admin' user.
|
||||
"This type is used to store passwords using a hash function. It
|
||||
extends the IANA crypt-hash type to support yescrypt as well as
|
||||
a reserved string '$factory$', used for device-specific factory
|
||||
default hash. It is up to the underlying system to define this
|
||||
further, one example is to use Vital Product Data (VPD), e.g.,
|
||||
an onboard EEPROM where a device hash is stored for the initial
|
||||
'admin' user.
|
||||
|
||||
A value of this type matches one of the forms:
|
||||
|
||||
@@ -140,33 +139,31 @@ module infix-system {
|
||||
$<id>$<salt>$<password hash>
|
||||
$<id>$<parameter>$<salt>$<password hash>
|
||||
|
||||
The '$0$' prefix signals that the value is clear text. When
|
||||
such a value is received by the server, a hash value is
|
||||
calculated, and the string '$<id>$<salt>$' or
|
||||
$<id>$<parameter>$<salt>$ is prepended to the result. This
|
||||
value is stored in the configuration data store.
|
||||
If a value starting with '$<id>$', where <id> is not '0', is
|
||||
received, the server knows that the value already represents a
|
||||
hashed value and stores it 'as is' in the data store.
|
||||
The '$0$' prefix signals that the value is clear text, and even
|
||||
though it is supported, it is *not* recommended! When such a
|
||||
value is received it passes through multiple subsystems before
|
||||
a hash value is calculated and the string '$<id>$<salt>$' or
|
||||
$<id>$<parameter>$<salt>$ is prepended and the result is stored
|
||||
in the configuration data store. The hash function used depend
|
||||
on end system requirements.
|
||||
|
||||
When any other '$<id>$' prefix is received, the system store it
|
||||
'as is' in the configuration data store.
|
||||
|
||||
When a server needs to verify a password given by a user, it
|
||||
finds the stored password hash string for that user, extracts
|
||||
the salt, and calculates the hash with the salt and given
|
||||
password as input. If the calculated hash value is the same
|
||||
as the stored value, the password given by the client is
|
||||
accepted.
|
||||
password as input. If the calculated hash value is the same as
|
||||
the stored value, the password given by the client is accepted.
|
||||
|
||||
This type defines the following hash functions:
|
||||
This type defines the following supported hash functions:
|
||||
|
||||
id | hash function | feature
|
||||
---+---------------+-------------------
|
||||
1 | MD5 | crypt-hash-md5
|
||||
5 | SHA-256 | crypt-hash-sha-256
|
||||
6 | SHA-512 | crypt-hash-sha-512
|
||||
y | yescrypt | crypt-hash-yescrypt
|
||||
|
||||
The server indicates support for the different hash functions
|
||||
by advertising the corresponding feature.";
|
||||
y | yescrypt | crypt-hash-yescrypt";
|
||||
reference
|
||||
"IEEE Std 1003.1-2008 - crypt() function
|
||||
RFC 1321: The MD5 Message-Digest Algorithm
|
||||
|
||||
Reference in New Issue
Block a user