Skip to content

Commit

Permalink
confd: add support for $0$cleartext password
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
troglobit committed Jun 20, 2024
1 parent 6855bed commit 5de9fd8
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 28 deletions.
36 changes: 36 additions & 0 deletions package/confd/Config.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion package/confd/confd.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/confd/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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])])

Expand All @@ -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])
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/confd/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand All @@ -14,6 +15,7 @@ confd_plugin_la_CFLAGS = \
$(CFLAGS)

confd_plugin_la_LIBADD = \
$(crypt_LIBS) \
$(glib_LIBS) \
$(jansson_LIBS) \
$(libite_LIBS) \
Expand Down
94 changes: 91 additions & 3 deletions src/confd/src/ietf-system.c
Original file line number Diff line number Diff line change
@@ -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>
Expand Down Expand Up @@ -1219,6 +1220,87 @@ static sr_error_t generate_auth_keys(sr_session_ctx_t *session, const char *xpat
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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
45 changes: 21 additions & 24 deletions src/confd/yang/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -125,48 +125,45 @@ 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:
$0$<clear text password>
$<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
Expand Down

0 comments on commit 5de9fd8

Please sign in to comment.