mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-07 07:53:18 +02:00
confd: add support for $factory$ password hash
This patch simplifies the handling of factory default password for the admin user by overloading the ietf-system password type. The new type, $factory$, acts a system hint to use any device specific (or built-in software/device-tree) default password hash. Fixes #435 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostkeys gen-admin-auth \
|
||||
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostkeys \
|
||||
gen-hostname gen-interfaces gen-motd gen-hardware
|
||||
sbin_SCRIPTS = dagger
|
||||
|
||||
@@ -102,12 +102,6 @@ factory()
|
||||
# shellcheck disable=SC2086
|
||||
gen-interfaces $GEN_IFACE_OPTS >"$FACTORY_D/20-interfaces.json"
|
||||
|
||||
# Extract password for admin user from VPD
|
||||
if ! gen-admin-auth infix-shell-type:bash >"$FACTORY_D/20-authentication.json"; then
|
||||
console_error "Unable to create factory-config, gen-admin-auth failed"
|
||||
return
|
||||
fi
|
||||
|
||||
[ -s "$FACTORY_D/20-hostkey.json" ] || gen-hostkeys >"$FACTORY_D/20-hostkey.json"
|
||||
|
||||
# Optional commands (from an overlay) to run for br2-externals
|
||||
@@ -127,11 +121,6 @@ failure()
|
||||
gen-hostname "$FAIL_HOSTNAME" >"$FAILURE_D/20-hostname.json"
|
||||
gen-interfaces >"$FAILURE_D/20-interfaces.json"
|
||||
|
||||
# Same password as factory-config, but another login shell
|
||||
if ! gen-admin-auth infix-shell-type:bash >"$FAILURE_D/20-authentication.json"; then
|
||||
console_error "Invalid password hash in vital product data, failure-config incomplete!"
|
||||
fi
|
||||
|
||||
[ -s "$FAILURE_D/20-hostkey.json" ] || gen-hostkeys >"$FAILURE_D/20-hostkey.json"
|
||||
|
||||
# Optional failure/error config to generate (or override) for br2-externals
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This script extracts the admin user's password hash from VPD
|
||||
|
||||
shell="$1"
|
||||
pwhash=$(jq -r '."factory-password-hash"' /run/system.json)
|
||||
|
||||
if [ -z "$pwhash" ] || [ "$pwhash" = "null" ]; then
|
||||
# Do not fail, lock account instead. This way developers can enable
|
||||
# root account login at build-time to diagnose the system.
|
||||
password=""
|
||||
rc=1
|
||||
else
|
||||
password="\"password\": \"$pwhash\","
|
||||
rc=0
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"ietf-system:system": {
|
||||
"authentication": {
|
||||
"user": [
|
||||
{
|
||||
"name": "admin",
|
||||
${password}
|
||||
"infix-system:shell": "$shell"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
exit $rc
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"ietf-system:system": {
|
||||
"hostname": "infix"
|
||||
"hostname": "infix",
|
||||
"authentication": {
|
||||
"user": [
|
||||
{
|
||||
"name": "admin",
|
||||
"password": "$factory$",
|
||||
"infix-system:shell": "infix-shell-type:bash"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"ieee802-dot1ab-lldp:lldp": {
|
||||
"infix-lldp:enabled": true
|
||||
},
|
||||
"infix-services:mdns": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"ietf-system:system": {
|
||||
"hostname": "failure"
|
||||
},
|
||||
"ieee802-dot1ab-lldp:lldp": {
|
||||
"infix-lldp:enabled": true
|
||||
},
|
||||
"infix-services:mdns": {
|
||||
"enabled": true
|
||||
"hostname": "failure",
|
||||
"authentication": {
|
||||
"user": [
|
||||
{
|
||||
"name": "admin",
|
||||
"password": "$factory$",
|
||||
"infix-system:shell": "infix-shell-type:bash"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-14
@@ -6,6 +6,7 @@
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <shadow.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
@@ -126,8 +127,8 @@ static char *fmtime(time_t t, char *buf, size_t len)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static sr_error_t _sr_change_iter(sr_session_ctx_t *session, char *xpath,
|
||||
sr_error_t cb(sr_session_ctx_t *, struct sr_change *))
|
||||
static sr_error_t _sr_change_iter(sr_session_ctx_t *session, struct confd *confd, char *xpath,
|
||||
sr_error_t cb(sr_session_ctx_t *, struct confd *, struct sr_change *))
|
||||
{
|
||||
struct sr_change change = {};
|
||||
sr_change_iter_t *iter;
|
||||
@@ -138,7 +139,7 @@ static sr_error_t _sr_change_iter(sr_session_ctx_t *session, char *xpath,
|
||||
return err;
|
||||
|
||||
while (sr_get_change_next(session, iter, &change.op, &change.old, &change.new) == SR_ERR_OK) {
|
||||
err = cb(session, &change);
|
||||
err = cb(session, confd, &change);
|
||||
sr_free_val(change.old);
|
||||
sr_free_val(change.new);
|
||||
if (err) {
|
||||
@@ -903,12 +904,29 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_password(const char *user, const char *hash, bool lock)
|
||||
static int set_password(struct confd *confd, const char *user, const char *hash, bool lock)
|
||||
{
|
||||
const char *factory = "$factory$";
|
||||
struct spwd *sp;
|
||||
FILE *fp = NULL;
|
||||
int fd = -1;
|
||||
|
||||
if (!strncmp(hash, factory, strlen(factory))) {
|
||||
struct json_t *pwd;
|
||||
|
||||
pwd = json_object_get(confd->root, "factory-password-hash");
|
||||
if (!pwd || !json_is_string(pwd)) {
|
||||
/*
|
||||
* Do not fail, lock account instead. This way developers can
|
||||
* enable root account login at build-time to diagnose the system.
|
||||
*/
|
||||
ERROR("%s: cannot find factory-default password hash!", user);
|
||||
lock = true;
|
||||
} else {
|
||||
hash = json_string_value(pwd);
|
||||
}
|
||||
}
|
||||
|
||||
if (lckpwdf())
|
||||
goto exit;
|
||||
|
||||
@@ -956,7 +974,7 @@ exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static sr_error_t handle_sr_passwd_update(sr_session_ctx_t *, struct sr_change *change)
|
||||
static sr_error_t handle_sr_passwd_update(sr_session_ctx_t *, struct confd *confd, struct sr_change *change)
|
||||
{
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
const char *hash;
|
||||
@@ -988,13 +1006,13 @@ static sr_error_t handle_sr_passwd_update(sr_session_ctx_t *, struct sr_change *
|
||||
ERROR("Empty passwords are not allowed, disabling password login.");
|
||||
hash = "*";
|
||||
}
|
||||
if (set_password(user, hash, false))
|
||||
if (set_password(confd, user, hash, false))
|
||||
err = SR_ERR_SYS;
|
||||
else
|
||||
NOTE("Password updated for user %s", user);
|
||||
break;
|
||||
case SR_OP_DELETED:
|
||||
if (set_password(user, "*", true))
|
||||
if (set_password(confd, user, "*", true))
|
||||
err = SR_ERR_SYS;
|
||||
else
|
||||
NOTE("Password deleted for user %s", user);
|
||||
@@ -1007,7 +1025,7 @@ static sr_error_t handle_sr_passwd_update(sr_session_ctx_t *, struct sr_change *
|
||||
return err;
|
||||
}
|
||||
|
||||
static sr_error_t handle_sr_shell_update(sr_session_ctx_t *sess, struct sr_change *change)
|
||||
static sr_error_t handle_sr_shell_update(sr_session_ctx_t *sess, struct confd *confd, struct sr_change *change)
|
||||
{
|
||||
char *shell = NULL;
|
||||
char *user;
|
||||
@@ -1034,7 +1052,7 @@ static sr_error_t handle_sr_shell_update(sr_session_ctx_t *sess, struct sr_chang
|
||||
return err;
|
||||
}
|
||||
|
||||
static sr_error_t check_sr_user_update(sr_session_ctx_t *, struct sr_change *change)
|
||||
static sr_error_t check_sr_user_update(sr_session_ctx_t *, struct confd *, struct sr_change *change)
|
||||
{
|
||||
sr_xpath_ctx_t state;
|
||||
sr_val_t *val;
|
||||
@@ -1053,7 +1071,7 @@ static sr_error_t check_sr_user_update(sr_session_ctx_t *, struct sr_change *cha
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static sr_error_t handle_sr_user_update(sr_session_ctx_t *sess, struct sr_change *change)
|
||||
static sr_error_t handle_sr_user_update(sr_session_ctx_t *sess, struct confd *, struct sr_change *change)
|
||||
{
|
||||
sr_xpath_ctx_t state;
|
||||
char *name;
|
||||
@@ -1152,7 +1170,7 @@ static sr_error_t change_auth_check(struct confd *confd, sr_session_ctx_t *sessi
|
||||
{
|
||||
sr_error_t err;
|
||||
|
||||
err = _sr_change_iter(session, XPATH_AUTH_"/user", check_sr_user_update);
|
||||
err = _sr_change_iter(session, confd, XPATH_AUTH_"/user", check_sr_user_update);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1163,15 +1181,15 @@ static sr_error_t change_auth_done(struct confd *confd, sr_session_ctx_t *sessio
|
||||
{
|
||||
sr_error_t err;
|
||||
|
||||
err = _sr_change_iter(session, XPATH_AUTH_"/user", handle_sr_user_update);
|
||||
err = _sr_change_iter(session, confd, XPATH_AUTH_"/user", handle_sr_user_update);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = _sr_change_iter(session, XPATH_AUTH_"/user[*]/password", handle_sr_passwd_update);
|
||||
err = _sr_change_iter(session, confd, XPATH_AUTH_"/user[*]/password", handle_sr_passwd_update);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
err = _sr_change_iter(session, XPATH_AUTH_"/user[*]/shell", handle_sr_shell_update);
|
||||
err = _sr_change_iter(session, confd, XPATH_AUTH_"/user[*]/shell", handle_sr_shell_update);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
|
||||
@@ -79,6 +79,63 @@ module infix-system {
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
typedef crypt-hash {
|
||||
type string {
|
||||
pattern
|
||||
'$0$.*'
|
||||
+ '|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}'
|
||||
+ '|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}'
|
||||
+ '|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}'
|
||||
+ '|$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 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
This type defines the following 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
|
||||
|
||||
The server indicates support for the different hash functions
|
||||
by advertising the corresponding feature.";
|
||||
reference
|
||||
"IEEE Std 1003.1-2008 - crypt() function
|
||||
RFC 1321: The MD5 Message-Digest Algorithm
|
||||
FIPS.180-4.2012: Secure Hash Standard (SHS)";
|
||||
}
|
||||
|
||||
typedef username {
|
||||
type string {
|
||||
pattern "[_a-zA-Z0-9][-._a-zA-Z0-9]*$?";
|
||||
@@ -186,4 +243,11 @@ module infix-system {
|
||||
type infix-sys:username;
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/sys:system/sys:authentication/sys:user/sys:password" {
|
||||
description "Extended password hash, including missing types, and $factory$.";
|
||||
deviate replace {
|
||||
type infix-sys:crypt-hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user