tests: Move tests with certificates to separate user

This avoids very-long test and false positives when using some
auto-pubkey authentication from picking up default keys, which are available in
bob's home directory when we want to test the certificate authentication.

The separate file is also needed because once we change to bob's UID, we can not
simply go back different UID and this sounds cleaner than setting up SSH_DIR to
different users ...

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Sahana Prasad <sahana@redhat.com>
This commit is contained in:
Jakub Jelen
2023-09-29 15:27:50 +02:00
committed by Sahana Prasad
parent 14c7b6a3fb
commit baa4eb1232
8 changed files with 446 additions and 208 deletions

View File

@@ -303,10 +303,13 @@ if (CLIENT_TESTING OR SERVER_TESTING)
file(READ keys/pkcs11/id_pkcs11_ecdsa_521_openssh.pub CONTENTS)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
# Copy the signed key to an alternative directory in bob's homedir.
file(COPY keys/certauth/id_rsa DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/certauth/id_rsa.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/certauth/id_rsa-cert.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh_cert/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
# Copy the signed key to an doe's homedir.
file(COPY keys/certauth/id_rsa DESTINATION
${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/certauth/id_rsa.pub DESTINATION
${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/certauth/id_rsa-cert.pub DESTINATION
${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
endif ()
if (WITH_PKCS11_URI)

View File

@@ -9,6 +9,7 @@ set(LIBSSH_CLIENT_TESTS
torture_connect
torture_hostkey
torture_auth
torture_auth_cert
torture_rekey
torture_forward
torture_knownhosts

View File

@@ -32,8 +32,7 @@
#include <sys/types.h>
#include <pwd.h>
/* agent_is_running */
#include "agent.c"
#include "torture_auth_common.c"
static int sshd_setup(void **state)
{
@@ -112,7 +111,7 @@ static int agent_setup(void **state)
char ssh_agent_cmd[4096];
char ssh_agent_sock[1024];
char ssh_agent_pidfile[1024];
char bob_ssh_key[1024];
char ssh_key_add[1024];
struct passwd *pwd;
int rc;
@@ -149,38 +148,12 @@ static int agent_setup(void **state)
setenv("SSH_AUTH_SOCK", ssh_agent_sock, 1);
setenv("TORTURE_SSH_AGENT_PIDFILE", ssh_agent_pidfile, 1);
snprintf(bob_ssh_key,
sizeof(bob_ssh_key),
snprintf(ssh_key_add,
sizeof(ssh_key_add),
"ssh-add %s/.ssh/id_rsa",
pwd->pw_dir);
rc = system(bob_ssh_key);
assert_return_code(rc, errno);
return 0;
}
static int agent_cert_setup(void **state)
{
char bob_alt_ssh_key[1024];
struct passwd *pwd;
int rc;
rc = agent_setup(state);
if (rc != 0) {
return rc;
}
pwd = getpwnam("bob");
assert_non_null(pwd);
/* remove all keys, load alternative key + cert */
snprintf(bob_alt_ssh_key,
sizeof(bob_alt_ssh_key),
"ssh-add -D && ssh-add %s/.ssh_cert/id_rsa",
pwd->pw_dir);
rc = system(bob_alt_ssh_key);
rc = system(ssh_key_add);
assert_return_code(rc, errno);
return 0;
@@ -659,73 +632,15 @@ static void torture_auth_password_nonblocking(void **state) {
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
static void torture_auth_agent(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
if (!ssh_agent_is_running(session)){
print_message("*** Agent not running. Test ignored\n");
return;
}
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_none(session,NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
rc = ssh_userauth_agent(session, NULL);
assert_ssh_return_code(session, rc);
}
static void torture_auth_agent_nonblocking(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
if (!ssh_agent_is_running(session)){
print_message("*** Agent not running. Test ignored\n");
return;
}
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_none(session,NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
ssh_set_blocking(session,0);
do {
rc = ssh_userauth_agent(session, NULL);
} while (rc == SSH_AUTH_AGAIN);
assert_ssh_return_code(session, rc);
}
static void torture_auth_agent_identities_only(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
char bob_ssh_key[1024];
struct passwd *pwd;
struct passwd *pwd = NULL;
int rc;
int identities_only = 1;
char *id;
char *id = NULL;
pwd = getpwnam("bob");
assert_non_null(pwd);
@@ -831,109 +746,6 @@ static void torture_auth_agent_identities_only_protected(void **state)
assert_ssh_return_code(session, rc);
}
static void torture_auth_cert(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_key privkey = NULL;
ssh_key cert = NULL;
char bob_ssh_key[1024];
char bob_ssh_cert[2048];
struct passwd *pwd;
int rc;
pwd = getpwnam("bob");
assert_non_null(pwd);
snprintf(bob_ssh_key,
sizeof(bob_ssh_key),
"%s/.ssh_cert/id_rsa",
pwd->pw_dir);
snprintf(bob_ssh_cert,
sizeof(bob_ssh_cert),
"%s-cert.pub",
bob_ssh_key);
/* cert has been signed for login as alice */
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &privkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_pki_import_cert_file(bob_ssh_cert, &cert);
assert_int_equal(rc, SSH_OK);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_try_publickey(session, NULL, cert);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_publickey(session, NULL, privkey);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(cert);
}
static void torture_auth_agent_cert(void **state)
{
#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
/* Skip this test if in FIPS mode.
*
* OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
* using certificates. It always uses SHA1.
*
* This should be removed as soon as OpenSSH agent bug is fixed.
* (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
if (ssh_fips_mode()) {
skip();
} else {
/* After the bug is solved, this also should be removed */
rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
"ssh-rsa-cert-v01@openssh.com");
assert_int_equal(rc, SSH_OK);
}
#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
/* Setup loads a different key, tests are exactly the same. */
torture_auth_agent(state);
}
static void torture_auth_agent_cert_nonblocking(void **state)
{
#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
/* Skip this test if in FIPS mode.
*
* OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
* using certificates. It always uses SHA1.
*
* This should be removed as soon as OpenSSH agent bug is fixed.
* (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
if (ssh_fips_mode()) {
skip();
} else {
/* After the bug is solved, this also should be removed */
rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
"ssh-rsa-cert-v01@openssh.com");
assert_int_equal(rc, SSH_OK);
}
#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
torture_auth_agent_nonblocking(state);
}
static void torture_auth_pubkey_types(void **state)
{
struct torture_state *s = *state;
@@ -1396,15 +1208,6 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only_protected,
agent_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_cert,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_agent_cert,
agent_cert_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_agent_cert_nonblocking,
agent_cert_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types,
pubkey_setup,
session_teardown),

View File

@@ -0,0 +1,338 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
* Copyright (c) 2023 by Jakub Jelen
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#define LIBSSH_STATIC
#include "torture.h"
#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include <errno.h>
#include <sys/types.h>
#include <pwd.h>
#include "torture_auth_common.c"
static int sshd_setup(void **state)
{
torture_setup_sshd_server(state, true);
return 0;
}
static int sshd_teardown(void **state) {
torture_teardown_sshd_server(state);
return 0;
}
static int session_setup(void **state)
{
struct torture_state *s = *state;
int verbosity = torture_libssh_verbosity();
const char *all_keytypes = NULL;
struct passwd *pwd;
bool b = false;
int rc;
pwd = getpwnam("doe");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
/* Make sure no other configuration options from system will get used */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
assert_ssh_return_code(s->ssh.session, rc);
/* Enable all hostkeys */
all_keytypes = ssh_kex_get_supported_method(SSH_HOSTKEYS);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, all_keytypes);
assert_ssh_return_code(s->ssh.session, rc);
return 0;
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static int cert_setup(void **state)
{
int rc;
rc = session_setup(state);
if (rc != 0) {
return rc;
}
/* Make sure we do not interfere with another ssh-agent */
unsetenv("SSH_AUTH_SOCK");
unsetenv("SSH_AGENT_PID");
return 0;
}
static int agent_setup(void **state)
{
struct torture_state *s = *state;
char ssh_agent_cmd[4096];
char ssh_agent_sock[1024];
char ssh_agent_pidfile[1024];
char ssh_key_add[1024];
struct passwd *pwd;
int rc;
rc = cert_setup(state);
if (rc != 0) {
return rc;
}
pwd = getpwnam("doe");
assert_non_null(pwd);
snprintf(ssh_agent_sock,
sizeof(ssh_agent_sock),
"%s/agent.sock",
s->socket_dir);
snprintf(ssh_agent_pidfile,
sizeof(ssh_agent_pidfile),
"%s/agent.pid",
s->socket_dir);
/* Production ready code!!! */
snprintf(ssh_agent_cmd,
sizeof(ssh_agent_cmd),
"eval `ssh-agent -a %s`; echo $SSH_AGENT_PID > %s",
ssh_agent_sock, ssh_agent_pidfile);
/* run ssh-agent and ssh-add as the normal user */
unsetenv("UID_WRAPPER_ROOT");
rc = system(ssh_agent_cmd);
assert_return_code(rc, errno);
setenv("SSH_AUTH_SOCK", ssh_agent_sock, 1);
setenv("TORTURE_SSH_AGENT_PIDFILE", ssh_agent_pidfile, 1);
snprintf(ssh_key_add,
sizeof(ssh_key_add),
"ssh-add %s/.ssh/id_rsa",
pwd->pw_dir);
rc = system(ssh_key_add);
assert_return_code(rc, errno);
return 0;
}
static int agent_cert_setup(void **state)
{
char doe_alt_ssh_key[1024];
struct passwd *pwd;
int rc;
rc = agent_setup(state);
if (rc != 0) {
return rc;
}
pwd = getpwnam("doe");
assert_non_null(pwd);
/* remove all keys, load alternative key + cert */
snprintf(doe_alt_ssh_key,
sizeof(doe_alt_ssh_key),
"ssh-add -D && ssh-add %s/.ssh/id_rsa",
pwd->pw_dir);
rc = system(doe_alt_ssh_key);
assert_return_code(rc, errno);
return 0;
}
static int agent_teardown(void **state)
{
const char *ssh_agent_pidfile;
int rc;
rc = session_teardown(state);
if (rc != 0) {
return rc;
}
ssh_agent_pidfile = getenv("TORTURE_SSH_AGENT_PIDFILE");
assert_non_null(ssh_agent_pidfile);
/* kill agent pid */
rc = torture_terminate_process(ssh_agent_pidfile);
assert_return_code(rc, errno);
unlink(ssh_agent_pidfile);
unsetenv("TORTURE_SSH_AGENT_PIDFILE");
unsetenv("SSH_AUTH_SOCK");
return 0;
}
static void torture_auth_cert(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_key privkey = NULL;
ssh_key cert = NULL;
char doe_ssh_key[1024];
char doe_ssh_cert[2048];
struct passwd *pwd;
int rc;
pwd = getpwnam("doe");
assert_non_null(pwd);
snprintf(doe_ssh_key,
sizeof(doe_ssh_key),
"%s/.ssh/id_rsa",
pwd->pw_dir);
snprintf(doe_ssh_cert,
sizeof(doe_ssh_cert),
"%s-cert.pub",
doe_ssh_key);
/* cert has been signed for login as alice */
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_pki_import_privkey_file(doe_ssh_key, NULL, NULL, NULL, &privkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_pki_import_cert_file(doe_ssh_cert, &cert);
assert_int_equal(rc, SSH_OK);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_try_publickey(session, NULL, cert);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_publickey(session, NULL, privkey);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(cert);
}
static void torture_auth_agent_cert(void **state)
{
#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
/* Skip this test if in FIPS mode.
*
* OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
* using certificates. It always uses SHA1.
*
* This should be removed as soon as OpenSSH agent bug is fixed.
* (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
if (ssh_fips_mode()) {
skip();
} else {
/* After the bug is solved, this also should be removed */
rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
"ssh-rsa-cert-v01@openssh.com");
assert_int_equal(rc, SSH_OK);
}
#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
/* Setup loads a different key, tests are exactly the same. */
torture_auth_agent(state);
}
static void torture_auth_agent_cert_nonblocking(void **state)
{
#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
/* Skip this test if in FIPS mode.
*
* OpenSSH agent has a bug which makes it to not use SHA2 in signatures when
* using certificates. It always uses SHA1.
*
* This should be removed as soon as OpenSSH agent bug is fixed.
* (see https://gitlab.com/libssh/libssh-mirror/merge_requests/34) */
if (ssh_fips_mode()) {
skip();
} else {
/* After the bug is solved, this also should be removed */
rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
"ssh-rsa-cert-v01@openssh.com");
assert_int_equal(rc, SSH_OK);
}
#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
torture_auth_agent_nonblocking(state);
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_auth_cert,
cert_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_agent_cert,
agent_cert_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_agent_cert_nonblocking,
agent_cert_setup,
agent_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
ssh_finalize();
return rc;
}

View File

@@ -0,0 +1,90 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "torture.h"
#include "libssh/libssh.h"
/* agent_is_running */
#include "agent.c"
void torture_auth_agent(void **state);
void torture_auth_agent(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
if (!ssh_agent_is_running(session)){
print_message("*** Agent not running. Test ignored\n");
return;
}
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_none(session,NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
rc = ssh_userauth_agent(session, NULL);
assert_ssh_return_code(session, rc);
}
void torture_auth_agent_nonblocking(void **state);
void torture_auth_agent_nonblocking(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
if (!ssh_agent_is_running(session)){
print_message("*** Agent not running. Test ignored\n");
return;
}
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_none(session,NULL);
/* This request should return a SSH_REQUEST_DENIED error */
if (rc == SSH_ERROR) {
assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
}
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
ssh_set_blocking(session,0);
do {
rc = ssh_userauth_agent(session, NULL);
} while (rc == SSH_AUTH_AGAIN);
assert_ssh_return_code(session, rc);
}

View File

@@ -1,3 +1,4 @@
bob:secret:sshd
alice:secret:sshd
charlie:secret:sshd
doe:secret:sshd

View File

@@ -1,6 +1,7 @@
bob:x:5000:9000:bob gecos:@HOMEDIR@/bob:/bin/sh
alice:x:5001:9000:alice gecos:@HOMEDIR@/alice:/bin/sh
charlie:x:5002:9000:charlie gecos:@HOMEDIR@/charlie:/bin/sh
doe:x:5003:9000:doe gecos:@HOMEDIR@/doe:/bin/sh
sshd:x:65530:65531:sshd:@HOMEDIR@:/sbin/nologin
nobody:x:65533:65534:nobody gecos:@HOMEDIR@:/bin/false
root:x:65534:65532:root gecos:@HOMEDIR@:/bin/false

View File

@@ -1,3 +1,4 @@
alice:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
bob:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
charlie:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
doe:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::