[Pkg-shadow-devel] [Git][debian/adduser][wip/feature-system-locks] 20 commits: deluser --lock [--system]
Marc Haber (@zugschlus)
gitlab at salsa.debian.org
Wed Jan 7 07:14:10 GMT 2026
Marc Haber pushed to branch wip/feature-system-locks at Debian / adduser
Commits:
e0e54b0a by Matt Barry at 2026-01-07T08:12:42+01:00
deluser --lock [--system]
* Add --lock/--unlock options for dealing with disabled accounts.
See documentation. Should probably have a notice specifically
about behavioral changes (eg. adding/removing system accounts).
- - - - -
c3549104 by Matt Barry at 2026-01-07T08:13:36+01:00
the documentation commit
this contains documentation changes related to account locking.
might still need some work.
- - - - -
f89e6a2a by Matt Barry at 2026-01-07T08:13:38+01:00
add some basic tests
- - - - -
e892d803 by Marc Haber at 2026-01-07T08:13:38+01:00
improve log level handling
Thanks: Matt Barry
Git-Dch: ignore
- - - - -
401c7936 by Marc Haber at 2026-01-07T08:13:38+01:00
add new constants for password handling
Git-Dch: ignore
Thanks: Matt Barry
- - - - -
b4685b6d by Marc Haber at 2026-01-07T08:13:38+01:00
introduce password handling logic
Git-Dch: ignore
Thanks: Matt Barry
- - - - -
ea577a22 by Marc Haber at 2026-01-07T08:13:38+01:00
introduce new function existing_value_desc
Git-Dch: ignore
Thanks: Matt Barry
- - - - -
acac7c73 by Marc Haber at 2026-01-07T08:13:38+01:00
use existing_value_desc
Thanks: Matt Barry
Git-Dch: ignore
- - - - -
b68e0b67 by Matt Barry at 2026-01-07T08:13:38+01:00
locked.account.tests
- - - - -
8ff40b1f by Marc Haber at 2026-01-07T08:13:38+01:00
prepare AdduserTestsCommon for the new tests
Git-Dch: ignore
- - - - -
0bbc7c70 by Matt Barry at 2026-01-07T08:13:38+01:00
user locked account tests
Git-Dch: ignore
- - - - -
9b827221 by Marc Haber at 2026-01-07T08:13:38+01:00
fix testsuite warning in test08.pl
Git-Dch: ignore
- - - - -
625fa3f8 by Marc Haber at 2026-01-07T08:13:38+01:00
run upstream testsuite with redirected stdin
otherwise tests might hang invisibly when the software
goes interactive
Git-Dch: ignore
- - - - -
05dbf730 by Marc Haber at 2026-01-07T08:13:38+01:00
--unlock makes sense also for system accounts
This is consisten with the documentation
Git-Dch: ignore
- - - - -
de21d3c3 by Marc Haber at 2026-01-07T08:13:38+01:00
fix EXISTING_HAS_PASSWORD to correctly handle !
This now also handles !something in the password field
- - - - -
79b2d13c by Marc Haber at 2026-01-07T08:13:38+01:00
add unlock_user function
Git-Dch: ignore
- - - - -
16433741 by Marc Haber at 2026-01-07T08:13:38+01:00
error out if asked to add an already locked non-ystem user
Git-Dch: ignore
- - - - -
31b32d81 by Marc Haber at 2026-01-07T08:13:38+01:00
unlock existing system user with new function
Git-Dch: ignore
- - - - -
9ffd40dd by Marc Haber at 2026-01-07T08:13:38+01:00
unlocking an existing system user is info
Git-Dch: ignore
- - - - -
86c3aa1d by Marc Haber at 2026-01-07T08:13:38+01:00
implement adduser --unlock
Git-Dch: ignore
- - - - -
15 changed files:
- AdduserCommon.pm
- adduser
- adduser.conf
- + debian/tests/f/account_locks.t
- debian/tests/lib/AdduserTestsCommon.pm
- deluser
- deluser.conf
- doc/adduser.8
- doc/deluser.8
- doc/deluser.conf.5
- + notes.100808x.md
- + testsuite/deluser-delete.conf
- testsuite/runsuite.sh
- testsuite/test08.pl
- + testsuite/test10.pl
Changes:
=====================================
AdduserCommon.pm
=====================================
@@ -102,6 +102,17 @@ use constant {
EXISTING_ID_MISMATCH => 4,
EXISTING_LOCKED => 8,
EXISTING_HAS_PASSWORD => 16,
+ EXISTING_DISABLED_PASS => 40, # 32 | EXISTING_LOCKED
+ EXISTING_INVALID_PASS => 72, # 64 | EXISTING_LOCKED
+ EXISTING_EXPIRED => 136, # 128 | EXISTING_LOCKED
+ EXISTING_NOLOGIN => 264, # 256 | EXISTING_LOCKED
+ EXISTING_PASSWORDLESS => 512,
+};
+
+use constant {
+ STDOUTDEFLEVEL => "warn",
+ STDERRDEFLEVEL => "warn",
+ LOGMSGDEFLEVEL => "info",
};
@EXPORT = (
@@ -138,6 +149,14 @@ use constant {
'EXISTING_ID_MISMATCH',
'EXISTING_LOCKED',
'EXISTING_HAS_PASSWORD',
+ 'EXISTING_DISABLED_PASS',
+ 'EXISTING_INVALID_PASS',
+ 'EXISTING_EXPIRED',
+ 'EXISTING_NOLOGIN',
+ 'EXISTING_PASSWORDLESS',
+ 'STDOUTDEFLEVEL',
+ 'STDERRDEFLEVEL',
+ 'LOGMSGDEFLEVEL',
'existing_user_status',
'existing_group_status',
);
@@ -507,6 +526,7 @@ sub preseed_config {
no_del_paths => "^/bin\$ ^/boot\$ ^/dev\$ ^/etc\$ ^/initrd ^/lib ^/lost+found\$ ^/media\$ ^/mnt\$ ^/opt\$ ^/proc\$ ^/root\$ ^/run\$ ^/sbin\$ ^/srv\$ ^/sys\$ ^/tmp\$ ^/usr\$ ^/var\$ ^/vmlinu",
name_regex => def_name_regex,
sys_name_regex => def_sys_name_regex,
+ sys_delete_action => "delete",
exclude_fstypes => "(proc|sysfs|usbfs|devpts|devtmpfs|devfs|afs)",
skel_ignore_regex => "\.(dpkg|ucf)-(old|new|dist)\$",
extra_groups => "users",
@@ -597,28 +617,41 @@ END {
# EXISTING_ID_MISMATCH => 4
# EXISTING_LOCKED => 8
# EXISTING_HAS_PASSWORD => 16
-# e.g. if the requested account name exists as a locked system user,
-# return 8|2|1 == 11
+# EXISTING_DISABLED_PASS => 32
+# EXISTING_INVALID_PASS => 64
+# EXISTING_NOLOGIN => 128
+# EXISTING_EXPIRED => 256
+# EXISTING_PASSWORDLESS => 512
sub existing_user_status {
my ($config, $new_name,$new_uid) = @_;
- my ($dummy1,$pw,$uid);
my $ret = EXISTING_NOT_FOUND;
log_trace( "existing_user_status called with new_name %s, new_uid %s, first_system_uid %s, last_system_uid %s", $new_name, $new_uid, $config->{"first_system_uid"}, $config->{"last_system_uid"} );
- if (($dummy1,$pw,$uid) = egetpwnam($new_name)) {
+ if (my (undef,$pw,$uid,undef,undef,$home,$shell) = egetpwnam($new_name)) {
# user with the name exists
- log_trace( "egetpwnam(%s) returns %s, %s, %s", $new_name, $dummy1, $pw, $uid );
+ log_trace( "egetpwnam(%s) returns %s, %s, %s, %s", $new_name, $pw, $uid, $home, $shell );
$ret |= EXISTING_FOUND;
$ret |= EXISTING_ID_MISMATCH if (defined($new_uid) && $uid != $new_uid);
$ret |= EXISTING_SYSTEM if
(($uid >= $config->{"first_system_uid"}) && ($uid <= $config->{"last_system_uid"}));
$ret |= EXISTING_HAS_PASSWORD if
- (defined $pw && $pw ne '' && $pw ne '!' && $pw !~ /^\*/);
+ (defined $pw && $pw ne '' && $pw !~ /^[!*]/);
$ret |= EXISTING_LOCKED if (substr($pw,0,1) eq "!"); # TODO: also check expiry?
+
+ # Note: the following conditions will also be true against EXISTING_LOCKED
+ # iow, if $x & EXISTING_INVALID_PASS, then $x & EXISTING_LOCKED
+ $ret |= EXISTING_DISABLED_PASS if (substr($pw,0,1) eq "!");
+ $ret |= EXISTING_INVALID_PASS if (substr($pw,0,1) eq "*");
+ $ret |= EXISTING_PASSWORDLESS if ($pw eq '!');
+ $ret |= EXISTING_NOLOGIN if ($shell =~ /bin\/nologin/);
+
+ my $age = `chage -l $new_name`;
+ $ret |= EXISTING_EXPIRED if ($age =~ /password must be changed/);
+
} elsif (defined($new_uid) && getpwuid($new_uid)) {
# user with the uid exists
$ret |= EXISTING_ID_MISMATCH;
}
- log_trace( "existing_user_status( %s, %s ) returns %s", $new_name, $new_uid, $ret );
+ log_trace( "existing_user_status( %s, %s ) returns %s (%s)", $new_name, $new_uid, $ret, existing_value_desc($ret) );
return $ret;
}
@@ -634,10 +667,10 @@ sub existing_user_status {
# EXISTING_ID_MISMATCH => 4
sub existing_group_status {
my ($config, $new_name,$new_gid) = @_;
- my ($dummy1,$dummy2,$gid);
+ my ($gid);
my $ret = EXISTING_NOT_FOUND;
log_trace( "existing_group_status called with new_name %s, new_gid %s", $new_name, $new_gid );
- if (($dummy1,$dummy2,$gid) = egetgrnam($new_name)) {
+ if ((undef,undef,$gid) = egetgrnam($new_name)) {
# group with the name exists
log_trace("egetgrnam %s returned successfully, gid = %s", $new_name, $gid);
$ret |= EXISTING_FOUND;
@@ -647,10 +680,26 @@ sub existing_group_status {
} elsif (defined($new_gid) && getgrgid($new_gid)) {
$ret |= EXISTING_ID_MISMATCH;
}
- log_trace( "existing_group_status( %s, %s ) returns %s", $new_name, $new_gid, $ret );
+ log_trace( "existing_group_status( %s, %s ) returns %s (%s)", $new_name, $new_gid, $ret, existing_value_desc($ret) );
return $ret;
}
+sub existing_value_desc {
+ my ($val) = @_;
+ my @flags = ();
+ push @flags, "found" if ($val & EXISTING_FOUND);
+ push @flags, "wrongid" if $val & EXISTING_ID_MISMATCH;
+ push @flags, "system" if ($val & EXISTING_SYSTEM);
+ push @flags, "locked" if $val & EXISTING_LOCKED;
+ push @flags, "haspass" if $val & EXISTING_HAS_PASSWORD;
+ push @flags, "badpass" if $val & EXISTING_INVALID_PASS;
+ push @flags, "disabled" if $val & EXISTING_DISABLED_PASS;
+ push @flags, "nologin" if $val & EXISTING_NOLOGIN;
+ push @flags, "expired" if $val & EXISTING_EXPIRED;
+ push @flags, "notfound" unless $#flags > 0;
+ return join '|', at flags
+}
+
1;
# Local Variables:
=====================================
adduser
=====================================
@@ -103,9 +103,9 @@ $0 =~ s+.*/++;
our $action;
our $verbose; # should we be verbose?
my $name_check_level = 0; # should we allow bad names?
-our $stdoutmsglevel = "warn";
-our $stderrmsglevel = "warn";
-our $logmsglevel = "info";
+our $stdoutmsglevel = undef;
+our $stderrmsglevel = undef;
+our $logmsglevel = undef;
my $allow_badname = 0; # should we allow bad names?
my $ask_passwd = 1; # ask for a passwd?
my $disabled_login = 0; # leave the new account disabled?
@@ -114,6 +114,7 @@ our @configfiles;
our @defaults = undef;
our $found_group_opt = undef;
our $found_sys_opt = undef;
+our $found_unlock_opt = undef;
our $ingroup_name = undef;
our $new_firstgid = undef;
our $new_firstuid = undef;
@@ -186,6 +187,7 @@ GetOptions(
'shell=s' => \$special_shell,
'system' => \$found_sys_opt,
'uid=i' => \$new_uid,
+ 'unlock' => \$found_unlock_opt,
'verbose' => sub { $verbose = 1; },
'version|v' => sub { &version; exit },
) or &usage_error;
@@ -199,10 +201,11 @@ if (!@configfiles) {
# make sure that message levels apply for reading configuration
# this will be overridden again after reading configuration
-$stdoutmsglevel = sanitize_string($stdoutmsglevel);
-$stderrmsglevel = sanitize_string($stderrmsglevel);
-$logmsglevel = sanitize_string($logmsglevel);
-set_msglevel( $stderrmsglevel, $stdoutmsglevel, $logmsglevel );
+set_msglevel(
+ sanitize_string($stderrmsglevel or STDERRDEFLEVEL),
+ sanitize_string($stdoutmsglevel or STDOUTDEFLEVEL),
+ sanitize_string($logmsglevel or LOGMSGDEFLEVEL),
+);
log_trace("ARGV %s", join(@ARGV,"-"));
log_trace("special_home %s", $special_home);
log_trace("special_home %s", encode($charset, $special_home));
@@ -223,8 +226,13 @@ if( $> != 0) {
log_fatal( mtx("Only root may add a user or group to the system.") );
exit( RET_ROOT_NEEDED );
}
-
-# TODO: Handle configuration file input, allow bare input there.
+# ARGV > adduser.conf > (default)
+$stdoutmsglevel //= $config{'stdoutmsglevel'};
+$stdoutmsglevel //= STDOUTDEFLEVEL;
+$stderrmsglevel //= $config{'stderrmsglevel'};
+$stderrmsglevel //= STDERRDEFLEVEL;
+$logmsglevel //= $config{'logmsglevel'};
+$logmsglevel //= LOGMSGDEFLEVEL;
$stdoutmsglevel = sanitize_string($stdoutmsglevel);
$stderrmsglevel = sanitize_string($stderrmsglevel);
$logmsglevel = sanitize_string($logmsglevel);
@@ -235,6 +243,7 @@ if( defined $verbose ) {
} elsif( $verbose == 1 ) {
set_msglevel( $stderrmsglevel, "info", $logmsglevel );
} elsif( $verbose == 2 ) {
+ set_msglevel( $stdoutmsglevel, "debug", $logmsglevel );
set_msglevel( $stderrmsglevel, "debug", $logmsglevel );
}
}
@@ -313,7 +322,6 @@ if ($action ne "addgroup" &&
exit( RET_EXCLUSIVE_PARAMETERS );
}
-
if ($found_group_opt) {
if ($action eq "addsysuser") {
$make_group_also = 1;
@@ -326,6 +334,10 @@ if ($found_group_opt) {
}
}
+if ($found_unlock_opt) {
+ $action = "unlockuser";
+}
+
# $new_firstuid = $new_firstuid || $config{"first_uid"} || 1000;
# $new_lastuid = $new_lastuid || $config{"last_uid"} || 59999;
# $new_firstgid = $new_firstgid || $config{"first_gid"} || 1000;
@@ -431,6 +443,8 @@ $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = 'handler';
# $action = "addusertogroup"
# $existing_user - the user to be added
# $existing_group - the group to add her to
+# $action = "unlockuser"
+# $new_name - the user to be unlocked
#####
@@ -589,6 +603,9 @@ if ($action eq "addsysuser") {
exit( RET_WRONG_OBJECT_PROPERTIES );
}
if ($ret & EXISTING_FOUND) {
+ if ($ret & EXISTING_LOCKED) {
+ unlock_user($new_name, 1);
+ }
log_info( mtx("The system user `%s' already exists. Exiting.\n"), $new_name );
exit( RET_OK );
}
@@ -1051,6 +1068,15 @@ if ($action eq "adduser") {
exit( $returnvalue );
}
+if ($action eq "unlockuser") {
+ log_trace( "unlockuser %s", $new_name );
+ acquire_lock();
+ unlock_user($new_name, 0);
+ release_lock(0);
+
+ exit( $returnvalue );
+}
+
#
# we never go here
#
@@ -1171,6 +1197,13 @@ sub check_user_group {
log_debug( "check_user_group %s called, make_group_also %s", $system, $make_group_also );
my $ustat = existing_user_status(\%config, $new_name, $new_uid);
+ if ($ustat & EXISTING_FOUND) {
+ if ($ustat & EXISTING_LOCKED) {
+ # this must be a non-system user, addsysuser handles the locked case before we get called
+ log_fatal( mtx("User `%s' already exists and is locked. use adduser --unlock explicitly to unlock. Exiting.\n"), $new_name );
+ exit( RET_OBJECT_EXISTS );
+ }
+ }
if ($system) {
if (($ustat & EXISTING_FOUND) && !($ustat & EXISTING_SYSTEM)) {
log_fatal( mtx("The user `%s' already exists, and is not a system user."), $new_name);
@@ -1465,6 +1498,33 @@ sub user_is_member {
return 0;
}
+# unlock user
+sub unlock_user {
+ my ($user_name, $system) = @_;
+ log_debug( "unlock_user %s called, system %s", $user_name, $system );
+ my $ret = existing_user_status(\%config, $user_name, undef);
+ if ($ret & EXISTING_FOUND) {
+ if ($ret & EXISTING_LOCKED) {
+ log_info( mtx("unlocking user `%s' ...", $user_name) );
+ my $unlock_ret = systemcall_useradd($name_check_level,
+ 'usermod',
+ '-e',
+ '-1',
+ '-U',
+ $new_name);
+ if( $unlock_ret == 0 ) {
+ log_info( mtx("user `%s' successfully unlocked.\n", $user_name) );
+ } else {
+ log_fatal( mtx("error %s while unlocking user `%s'. Exiting.\n", $unlock_ret, $user_name) );
+ exit( RET_SYSTEMCALL_ERROR );
+ }
+ }
+ log_info( mtx("User `%s' is already unlocked.\n"), $new_name );
+ } else {
+ log_fatal( mtx("User `%s' does not exist\n"), $new_name );
+ exit( RET_OBJECT_DOES_NOT_EXIST );
+ }
+}
sub cleanup {
if ($undohome) {
@@ -1523,6 +1583,11 @@ sub usage {
user
Add a regular user
+adduser --unlock
+ [--system]
+ user
+ Unlock an existing locked user account
+
adduser --system
[--uid id] [--group] [--ingroup group] [--gid id]
[--shell shell] [--comment comment] [--home dir] [--no-create-home]
=====================================
adduser.conf
=====================================
@@ -113,4 +113,3 @@
# EXTRA_GROUPS.
# Default: ADD_EXTRA_GROUPS=0
#ADD_EXTRA_GROUPS=0
-
=====================================
debian/tests/f/account_locks.t
=====================================
@@ -0,0 +1,109 @@
+#! /usr/bin/perl -Idebian/tests/lib
+
+# Ref: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=701110
+
+
+use diagnostics;
+use strict;
+use warnings;
+
+use AdduserTestsCommon;
+
+my $prefix = "lockedtest";
+my $un;
+
+END {
+ remove_tree("/home/$prefix-user");
+ remove_tree("/var/mail/$prefix-user");
+}
+
+## system user
+
+$un = "${prefix}-sys";
+
+assert_user_does_not_exist($un);
+assert_command_success('/usr/sbin/adduser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--disabled-password',
+ '--system',
+ $un);
+assert_user_exists($un);
+assert_command_success('/usr/sbin/deluser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--system', "--lock",
+ $un);
+assert_user_exists($un);
+assert_user_is_locked($un);
+assert_user_is_disabled($un);
+assert_user_is_invalid($un);
+assert_user_is_expired($un);
+assert_user_is_nologin($un);
+assert_command_success('/usr/sbin/deluser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--system', "--lock",
+ $un);
+assert_user_exists($un);
+assert_command_success('/usr/sbin/adduser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--system',
+ $un);
+assert_user_exists($un);
+assert_user_is_not_locked($un);
+assert_user_is_not_disabled($un);
+assert_user_is_not_invalid($un);
+assert_user_is_not_expired($un);
+assert_user_is_not_nologin($un);
+assert_command_success('/usr/sbin/deluser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--system', '--force-delete',
+ $un);
+assert_user_does_not_exist($un);
+
+## normal user
+
+$un = "${prefix}-user";
+
+assert_user_does_not_exist($un);
+assert_command_success('/usr/sbin/adduser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--disabled-password',
+ $un);
+assert_user_exists($un);
+assert_command_success('/usr/sbin/deluser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ "--lock",
+ $un);
+assert_user_exists($un);
+assert_user_is_locked($un);
+assert_user_is_disabled($un);
+assert_user_is_invalid($un);
+assert_user_is_expired($un);
+assert_user_is_nologin($un);
+assert_command_success('/usr/sbin/deluser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ "--lock",
+ $un);
+assert_user_exists($un);
+assert_command_failure('/usr/sbin/adduser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ $un);
+assert_user_exists($un);
+assert_user_is_locked($un);
+assert_command_success('/usr/sbin/adduser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--unlock',
+ $un);
+assert_user_exists($un);
+assert_user_is_not_locked($un);
+assert_user_is_not_disabled($un);
+assert_user_is_not_invalid($un);
+assert_user_is_not_expired($un);
+assert_user_is_not_nologin($un);
+assert_command_success('/usr/sbin/deluser',
+ '--stdoutmsglevel=error', '--stderrmsglevel=error',
+ '--system', '--force-delete', '--remove-home',
+ $un);
+assert_user_does_not_exist($un);
+
+
+# vim: tabstop=4 shiftwidth=4 expandtab
=====================================
debian/tests/lib/AdduserTestsCommon.pm
=====================================
@@ -30,6 +30,24 @@ END {
if (-f '/var/cache/adduser/tests/state.tar');
}
+use constant {
+ EXISTING_NOT_FOUND => 0,
+ EXISTING_FOUND => 1,
+ EXISTING_SYSTEM => 2,
+ EXISTING_ID_MISMATCH => 4,
+ EXISTING_LOCKED => 8,
+ EXISTING_HAS_PASSWORD => 16,
+ EXISTING_DISABLED_PASS => 40, # 32 | EXISTING_LOCKED
+ EXISTING_INVALID_PASS => 72, # 64 | EXISTING_LOCKED
+ EXISTING_EXPIRED => 136, # 128 | EXISTING_LOCKED
+ EXISTING_NOLOGIN => 264, # 256 | EXISTING_LOCKED
+ EXISTING_PASSWORDLESS => 512,
+ SYS_MIN => 100,
+ SYS_MAX => 999,
+ USER_MIN => 1000,
+ USER_MAX => 9999,
+};
+
my $charset = langinfo(CODESET);
binmode(STDOUT, ":encoding($charset)");
binmode(STDERR, ":encoding($charset)");
@@ -291,11 +309,6 @@ sub assert_path_has_ownership {
is(sprintf('%s:%s', $user, $group), $ownership, $name);
}
-sub assert_path_is_a_file {
- my $path = shift;
- ok(-f $path, "path is a file $path");
-}
-
sub assert_path_is_a_directory {
my $path = shift;
ok(-d $path, "path is a directory: $path");
@@ -387,6 +400,7 @@ sub assert_user_has_home_directory {
sub assert_user_has_comment {
my ($user, $comment) = @_;
+ $comment .= ',,,';
is((egetpwnam($user))[6], $comment, "user has comment: ~$user is $comment");
}
@@ -459,6 +473,65 @@ sub apply_config_hash {
close(CONF);
}
+sub assert_user_is_locked {
+ return existing_user_status($1) & EXISTING_LOCKED;
+}
+sub assert_user_is_disabled {
+ return (existing_user_status($1) & EXISTING_DISABLED_PASS)==EXISTING_DISABLED_PASS;
+}
+sub assert_user_is_invalid {
+ return (existing_user_status($1) & EXISTING_INVALID_PASS)==EXISTING_INVALID_PASS;
+}
+sub assert_user_is_nologin {
+ return (existing_user_status($1) & EXISTING_NOLOGIN)==EXISTING_NOLOGIN;
+}
+sub assert_user_is_expired {
+ return (existing_user_status($1) & EXISTING_EXPIRED)==EXISTING_EXPIRED;
+}
+sub assert_user_is_not_locked { return !assert_user_is_locked($1) }
+sub assert_user_is_not_disabled { return !assert_user_is_disabled($1) }
+sub assert_user_is_not_invalid { return !assert_user_is_invalid($1) }
+sub assert_user_is_not_nologin { return !assert_user_is_nologin($1) }
+sub assert_user_is_not_expired { return !assert_user_is_expired($1) }
+
+sub existing_user_status {
+ my ($new_name,$new_uid) = @_;
+ my $ret = EXISTING_NOT_FOUND;
+ if (my (undef,$pw,$uid,undef,undef,$home,$shell) = egetpwnam($new_name)) {
+ $ret |= EXISTING_FOUND;
+ $ret |= EXISTING_ID_MISMATCH if (defined($new_uid) && $uid != $new_uid);
+ $ret |= EXISTING_SYSTEM if \
+ ($uid >= SYS_MIN && $uid <= SYS_MAX);
+
+ # Note: the following conditions will also be true against EXISTING_LOCKED
+ # iow, if $x & EXISTING_INVALID_PASS, then $x & EXISTING_LOCKED
+ $ret |= EXISTING_DISABLED_PASS if (substr($pw,0,1) eq "!");
+ $ret |= EXISTING_INVALID_PASS if (substr($pw,0,1) eq "*");
+ $ret |= EXISTING_NOLOGIN if ($shell =~ /bin\/nologin/);
+
+ my $age = `chage -l $new_name`;
+ $ret |= EXISTING_EXPIRED if ($age =~ /password must be changed/);
+ } elsif ($new_uid && getpwuid($new_uid)) {
+ $ret |= EXISTING_ID_MISMATCH;
+ }
+ return $ret;
+}
+
+sub existing_group_status {
+ my ($new_name,$new_gid) = @_;
+ my $gid;
+ my $ret = EXISTING_NOT_FOUND;
+ if ((undef,undef,$gid) = egetgrnam($new_name)) {
+ $ret |= EXISTING_FOUND;
+ $ret |= EXISTING_ID_MISMATCH if (defined($new_gid) && $gid != $new_gid);
+ $ret |= EXISTING_SYSTEM if \
+ ($gid >= SYS_MIN && $gid <= SYS_MAX);
+ } elsif ($new_gid && getgrgid($new_gid)) {
+ $ret |= EXISTING_ID_MISMATCH;
+ }
+ return $ret;
+}
+
1;
# vim: tabstop=4 shiftwidth=4 expandtab
=====================================
deluser
=====================================
@@ -122,22 +122,24 @@ our @defaults = undef;
our @names;
GetOptions (
+ 'backup' => \$pconfig{'backup'},
+ 'backup-to=s' => \$pconfig{'backup_to'},
+ 'backup-suffix=s' => \$pconfig{'backup_suffix'},
'conf|c=s' => \@configfiles,
'debug' => sub { $verbose = 2; },
- 'stdoutmsglevel=s' => \$stdoutmsglevel,
- 'stderrmsglevel=s' => \$stderrmsglevel,
- 'logmsglevel=s' => \$logmsglevel,
- 'help|h' => sub { &usage(); exit 0; },
+ 'force-delete' => \$pconfig{'force_delete'},
'group' => sub { $action = 'delgroup'; },
- 'system' => \$pconfig{'system'},
+ 'help|h' => sub { &usage(); exit 0; },
+ 'lock|L' => \$pconfig{'lock'},
+ 'logmsglevel=s' => \$logmsglevel,
+ 'no-preserve-root' => \$no_preserve_root,
'only-if-empty' => \$pconfig{'only_if_empty'},
+ 'quiet|q' => sub { $verbose = 0; },
'remove-home' => \$pconfig{'remove_home'},
'remove-all-files' => \$pconfig{'remove_all_files'},
- 'backup' => \$pconfig{'backup'},
- 'backup-to=s' => \$pconfig{'backup_to'},
- 'backup-suffix=s' => \$pconfig{'backup_suffix'},
- 'no-preserve-root' => \$no_preserve_root,
- 'quiet|q' => sub { $verbose = 0; },
+ 'stdoutmsglevel=s' => \$stdoutmsglevel,
+ 'stderrmsglevel=s' => \$stderrmsglevel,
+ 'system' => \$pconfig{'system'},
'verbose' => sub { $verbose = 1; },
'version|v' => sub { &version; exit },
) or &usage_error;
@@ -287,11 +289,6 @@ if($action eq "deluser") {
log_info( mtx("The user `%s' does not exist, but --system was given. Exiting."), $user);
exit( RET_OK );
}
- } else {
- if (($config{remove_home} || $config{remove_all_files} || $config{backup}) && ($install_more_packages)) {
- log_fatal( mtx("In order to use the --remove-home, --remove-all-files, and --backup features, you need to install the `perl' package. To accomplish that, run apt-get install perl.") );
- exit( RET_MORE_PACKAGES );
- }
}
unless(exist_user($user)) {
@@ -312,6 +309,39 @@ if($action eq "deluser") {
$config{"backup"} = 1;
}
+ # default behavior is to lock if deleting a system account;
+ # this can be changed via deluser.conf or --force-delete.
+ if(!$config{"lock"}) {
+ if ($config{"system"} && !($config{"sys_delete_action"} eq "delete")) {
+ $config{"lock"} = 1;
+ }
+ }
+ if ($config{"force_delete"}) {
+ $config{"lock"} = 0;
+ }
+
+ # if account is to be locked (not deleted), leave files alone
+ if ($config{"lock"}) {
+ # TODO: remove this section - any account can be locked
+ if ($config{"backup"} or $config{"remove"} or $config{"remove_all_files"}) {
+ log_warn( mtx("Account will be locked; files will not be backed up or removed.") );
+ log_warn( mtx("Hint: Use --force-delete to delete the account as normal.") )
+ }
+
+ log_info( mtx("Locking user account `%s' ...", $user));
+ acquire_lock();
+ my $lock_ret = &systemcall('usermod', "-e", 1, "-f", 0, "-L", $user);
+ if( $lock_ret == 0 ) {
+ log_info( mtx("user `%s' successfully locked.\n", $user_name) );
+ } else {↲
+ log_fatal( mtx("error %s while unlocking user `%s'. Exiting.\n", $unlock_ret, $user_name) );
+ exit( RET_SYSTEMCALL_ERROR );
+ }
+ release_lock();
+
+ exit( RET_OK )
+ }
+
if($config{"remove_home"} || $config{"remove_all_files"}) {
log_trace( mtx("remove_home or remove_all_files beginning") );
log_info( mtx("Looking for files to backup/remove ...") );
@@ -495,8 +525,7 @@ if ($action eq 'delgroup') {
}
-if($action eq 'deluserfromgroup')
-{
+if($action eq 'deluserfromgroup') {
unless(exist_user($user)) {
log_fatal( mtx("The user `%s' does not exist.\n"), $user );
exit( RET_OBJECT_DOES_NOT_EXIST );
@@ -562,7 +591,7 @@ sub usage {
printf( gtx(
"deluser [--system] [--remove-home] [--remove-all-files] [--backup]
[--backup-to dir] [--backup-suffix str] [--conf file]
- [--quiet] [--verbose] [--debug] user
+ [--quiet] [--verbose] [--debug] [--lock] user
remove a regular user from the system
@@ -629,3 +658,4 @@ sub check_backup_suffix {
# End:
# vim: tabstop=4 shiftwidth=4 expandtab
+0
=====================================
deluser.conf
=====================================
@@ -39,3 +39,8 @@
# be excluded when looking for files of a user to be deleted.
# Default: EXCLUDE_FSTYPES = "(proc|sysfs|usbfs|devpts|tmpfs|afs)"
#EXCLUDE_FSTYPES = "(proc|sysfs|usbfs|devpts|tmpfs|afs)"
+
+# What action to take when deleting a system account
+# Options: lock|delete
+# Default: SYS_DELETE_ACTION=delete
+#SYS_DELETE_ACTION=delete
=====================================
doc/adduser.8
=====================================
@@ -65,6 +65,11 @@ adduser, addgroup \- add or manipulate users or groups
.B user
.YS
.SY adduser
+.B \-\-unlock
+.OP \-\-system
+.B user
+.YS
+.SY adduser
.B \-\-group
.OP \-\-conf file
.OP \-\-debug
@@ -162,6 +167,11 @@ that means a
in the sense of Debian Policy.
This is commonly referred to in \fBadduser\fP as a \fInon-system user.\fP
.PP
+If \fBadduser\fP is called with the \fB\-\-unlock\fP option, it will
+search for an existing account, and ensure it is unlocked. If the
+user is not found, \fBadduser\fP will return an error. See
+\fBUnlock an account\fP below for more details.
+.PP
\fBadduser\fP will choose the first available UID
from the range specified by
\fBFIRST_UID\fP and \fBLAST_UID\fP
@@ -224,6 +234,10 @@ often abbreviated as
\fIsystem user\fP
in the context of the \fBadduser\fP package.
.PP
+If the user already exists and is a system account, then
+\fBadduser\fP will ensure the account is unlocked and return
+successfully. See \fBUnlock a user account\fP below.
+.PP
\fBadduser\fP will choose the first available UID
from the range specified by
\fBFIRST_SYSTEM_UID\fP and \fBLAST_SYSTEM_UID\fP
@@ -266,6 +280,13 @@ Skeletal configuration files are not copied.
Other options will behave as for the creation of a regular user.
The files referenced by \fBUID_POOL\fP and \fBGID_POOL\fP are also honored.
+.SS "Unlock a user account"
+If called with the \fB\-\-unlock\fP option, \fBadduser\fP will
+unlock an existing, previously locked account. If found, the
+user's password will be re-enabled, and expiry restrictions will
+be removed. Any previous expiry settings will have to be
+added manually after unlocking the account.
+
.SS "Add a group"
If \fBadduser\fP is called with the \fB\-\-group\fP option and
without the \fB\-\-system\fP option, or
=====================================
doc/deluser.8
=====================================
@@ -27,6 +27,7 @@ deluser, delgroup \- remove a user or group from the system
.OP \-\-stdoutmsglevel prio
.OP \-\-stderrmsglevel prio
.OP \-\-logmsglevel prio
+.OP \-\-lock
.B user
.YS
@@ -44,6 +45,8 @@ deluser, delgroup \- remove a user or group from the system
.OP \-\-stdoutmsglevel prio
.OP \-\-stderrmsglevel prio
.OP \-\-logmsglevel prio
+.OP \-\-lock
+.OP \-\-force\-delete
.B user
.YS
@@ -115,6 +118,10 @@ If called with one non-option argument and
without the \fB\-\-group\fP option,
\fBdeluser\fP will remove a non-system user.
.PP
+Note: If \fBdeluser\fP is called with the \fB\-\-lock\fP option,
+the user account will be locked rather than removed as described
+here; see \fB\-\-lock\fP, below.
+.PP
By default,
\fBdeluser\fP will remove the user
without removing the home directory,
@@ -263,6 +270,19 @@ That allows the local admin to control \fBadduser\fP's chattiness
on the console and in the log independently, keeping probably confusing
information to itself while still leaving helpful information in the log.
.TP
+.B \-\-lock
+If \fBdeluser\fP is called with the \fB\-\-lock\fP option,
+the user account will be locked rather than removed. In this
+case, the user's password is invalidated and the account is
+set to expired; no other action is taken. A locked account may be
+subsequently unlocked using \fBadduser\fP \fB\-\-unlock\fP.
+
+System users may be set to lock upon delete by default by setting
+\fBSYS_DELETE_ACTION\fP=\fBlock\fP in \fB/etc/deluser.conf\fP.
+.B \-\-force\-delete
+If deleting a system user, and the default \fBSYS_DELETE_ACTION\fP
+would lock the account, instead delete it normally.
+.TP
.B \-\-version
Display version and copyright information.
=====================================
doc/deluser.conf.5
=====================================
@@ -78,6 +78,12 @@ Values may be 0 or 1. Defaults to \fI0\fP.
.B REMOVE_HOME
Removes the home directory and mail spool of the user to be removed.
Value may be 0 (don't delete) or 1 (do delete). Defaults to \fI0\fP.
+.TP
+.B SYS_DELETE_ACTION
+The default action to take when deleting system accounts. Valid options
+are "lock" and "delete" (the default). If set to "lock" and not
+overridden by \fB\-\-force\-delete\fP, behave as if \fB\-\-lock\fP were
+passed and lock the account. See \fBdeluser(8)\fP for specific details.
.SH FILES
.I /etc/deluser.conf
=====================================
notes.100808x.md
=====================================
@@ -0,0 +1,28 @@
+#1008082: deluser --system(?) --lock
+
+- leaves the account intact but makes login impossible (by setting an invalid
+password, leaving existing password recoverable, and setting shell to
+/usr/sbin/nologin)
+- only system accounts?
+- adding state (/var/lib/adduser)?
+ - or set to nologin, reset shell to default and WARN on unlock
+
+#1008083: deluser --system
+
+- /etc/deluser.conf: DELUSER_SYS_ACTION = (lock*|delete) *default
+- delgroup --system honors the above (lock == NOOP)
+
+- basically: extends --lock to /etc/deluser.conf for system users
+
+#1008084: adduser --system behavior if trying to create existing locked account
+ - if system account already exists, just unlock and set shell
+ - addgroup --system silently ignore existing also (?)
+
+#?: --homeless
+ - couldn't find a bug for this but saw it mentioned; is this something we
+ still want to do?
+
+blocked above:
+#1006912: is it time to have account deletion in policy?
+- anything relevant policy-wise? (i have not read the whole thread,
+ or any list discussion)
=====================================
testsuite/deluser-delete.conf
=====================================
@@ -0,0 +1,47 @@
+# /etc/deluser.conf: `deluser' configuration.
+# See deluser(8) and deluser.conf(5) for full documentation.
+
+# A commented out setting indicates that this is the default in the
+# code. If you need to change those settings, remove the comment and
+# make your intended change.
+
+# Remove home directory and mail spool when user is removed
+# Default: REMOVE_HOME = 0
+#REMOVE_HOME = 0
+
+# Remove all files on the system owned by the user to be removed
+# Default: REMOVE_ALL_FILES = 0
+#REMOVE_ALL_FILES = 0
+
+# Backup files before removing them. This options has only an effect if
+# REMOVE_HOME or REMOVE_ALL_FILES is set.
+# Default: BACKUP = 0
+#BACKUP = 0
+
+# Target directory for the backup file
+# Default: BACKUP_TO = "."
+#BACKUP_TO = "."
+
+# Select compression (from tar --auto-compress) for backups
+# Default: BACKUP_SUFFIX = .gz
+#BACKUP_SUFFIX = .gz
+
+# Space-Separated list of regular expressions. Do not delete files
+# matching any of these.
+# Default: NO_DEL_PATHS="^/bin\$ ^/boot\$ ^/dev\$ ^/etc\$ ^/initrd ^/lib ^/lost+found\$ ^/media\$ ^/mnt\$ ^/opt\$ ^/proc\$ ^/root\$ ^/run\$ ^/sbin\$ ^/srv\$ ^/sys\$ ^/tmp\$ ^/usr\$ ^/var\$ ^/vmlinu"
+#NO_DEL_PATHS="^/bin\$ ^/boot\$ ^/dev\$ ^/etc\$ ^/initrd ^/lib ^/lost+found\$ ^/media\$ ^/mnt\$ ^/opt\$ ^/proc\$ ^/root\$ ^/run\$ ^/sbin\$ ^/srv\$ ^/sys\$ ^/tmp\$ ^/usr\$ ^/var\$ ^/vmlinu"
+
+# Only delete a group if there are no users belonging to this group.
+# Default: ONLY_IF_EMPTY = 0
+#ONLY_IF_EMPTY = 0
+
+# Single regular expression which describes filesystems types which should
+# be excluded when looking for files of a user to be deleted.
+# Default: EXCLUDE_FSTYPES = "(proc|sysfs|usbfs|devpts|tmpfs|afs)"
+#EXCLUDE_FSTYPES = "(proc|sysfs|usbfs|devpts|tmpfs|afs)"
+
+# What action to take when deleting a system account
+# Options: lock|delete
+# Default: SYS_DELETE_ACTION=delete
+#SYS_DELETE_ACTION=delete
+SYS_DELETE_ACTION=lock
=====================================
testsuite/runsuite.sh
=====================================
@@ -20,7 +20,7 @@ for a in on; do
fi
echo
echo "Starting $i (shadow $a)"
- /usr/bin/perl -I. $i
+ < /dev/null /usr/bin/perl -I. $i
if [ "$?" != "0" ]; then
FAILED="$FAILED $i($a)"
fi
=====================================
testsuite/test08.pl
=====================================
@@ -91,7 +91,7 @@ unless (!defined getgrnam($newgroup)) {
print "ok\n";
}
-my $newgroup = find_unused_name();
+$newgroup = find_unused_name();
$cmd = "adduser --group $newgroup";
unless (defined getgrnam($newgroup)) {
=====================================
testsuite/test10.pl
=====================================
@@ -0,0 +1,374 @@
+#!/usr/bin/perl -w
+
+# there is a deluser.conf in the same directory as the tests
+# that has SYS_DELETE_ACTION=lock so that we can choose the behavior
+
+use strict;
+
+use lib_test;
+
+my $error;
+my $output;
+
+my $cmd;
+my $username;
+my $num;
+
+$username = find_unused_name();
+$num = 0;
+
+use strict;
+use warnings;
+
+sub get_user_status {
+ my ($username) = @_;
+ return 'absent' unless defined $username && length $username;
+
+ # Get passwd entry (returns actual password if run as root)
+ my @pw = getpwnam($username);
+ return 'absent' unless @pw;
+
+ my $pw_field = $pw[1]; # encrypted password field
+
+ # Locked if password starts with '!'
+ return ($pw_field =~ /^!/) ? 'locked' : 'unlocked';
+}
+
+# unlock a non-existing account
+$cmd = "adduser --unlock $username";
+++$num && print "Testing (10.$num) $cmd... ";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+if (!$error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "absent" ) {
+ print "failed\n $username is not absent\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# add the account
+$cmd = "adduser --no-create-home --comment '' --disabled-password $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "locked" ) {
+ print "failed\n $username is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock the account (should fail, no password set)
+$cmd = "adduser --unlock $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "locked" ) {
+ print "failed\n $username is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# set a password
+$cmd = "usermod --password \$y\$j9T\$6KrIYfSdT/O2rBLrkyzcF/\$pMxfrOqQgNn/jlZZVjSs1ELUZjpFRyjZ5ahXKZ84115 $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "unlocked" ) {
+ print "failed\n $username is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# lock the account
+$cmd = "deluser --lock $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "locked" ) {
+ print "failed\n $username is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# add the account (should fail)
+$cmd = "adduser --no-create-home --comment '' --disabled-password $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if (!$error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "locked" ) {
+ print "failed\n $username is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock the account
+$cmd = "adduser --unlock $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "unlocked" ) {
+ print "failed\n $username is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# deluser with SYS_DELETE_ACTION=lock
+$cmd = "deluser --conf ./deluser.conf $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "locked" ) {
+ print "failed\n $username is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock the account
+$cmd = "adduser --unlock $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "unlocked" ) {
+ print "failed\n $username is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# regular deluser
+$cmd = "deluser $username";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($username) ne "absent" ) {
+ print "failed\n $username is not absent\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+
+
+#=======================
+# system user
+$susername = find_unused_name();
+
+# unlock a non-existing system ccount
+$cmd = "adduser --unlock --system $susername";
+++$num && print "Testing (10.$num) $cmd... ";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+if (!$error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "absent" ) {
+ print "failed\n $susername is not absent\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# add system account
+$cmd = "adduser --system $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "locked" ) {
+ print "failed\n $susername is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock the account (should fail, no password set)
+$cmd = "adduser --system --unlock $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "locked" ) {
+ print "failed\n $susername is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# set a password
+$cmd = "usermod --password \$y\$j9T\$6KrIYfSdT/O2rBLrkyzcF/\$pMxfrOqQgNn/jlZZVjSs1ELUZjpFRyjZ5ahXKZ84115 $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "unlocked" ) {
+ print "failed\n $susername is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# lock the account
+$cmd = "deluser --system --lock $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "locked" ) {
+ print "failed\n $susername is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# re-enable via add
+$cmd = "adduser --system $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "unlocked" ) {
+ print "failed\n $susername is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock already-unlocked
+$cmd = "adduser --unlock $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "unlocked" ) {
+ print "failed\n $susername is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# lock the account
+$cmd = "deluser --system --lock $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "locked" ) {
+ print "failed\n $susername is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock the account
+$cmd = "adduser --system --unlock $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "unlocked" ) {
+ print "failed\n $susername is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# deluser with SYS_DELETE_ACTION=lock
+$cmd = "deluser --conf ./deluser.conf --system $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "locked" ) {
+ print "failed\n $susername is not locked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# unlock the account
+$cmd = "adduser --system --unlock $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "unlocked" ) {
+ print "failed\n $susername is not unlocked\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+# regular deluser
+$cmd = "deluser --system $susername";
+$output=`$cmd 2>&1`;
+$error = ($?>>8);
+++$num && print "Testing (10.$num) $cmd... ";
+if ($error) {
+ print "failed\n $cmd returned errorcode ($error)\n $output\n";
+ exit 1;
+}
+if (get_user_status($susername) ne "absent" ) {
+ print "failed\n $susername is not absent\n $output}n";
+ exit 1;
+}
+print "ok\n";
+
+print "not completely implemented yet\n";
+exit 1;
View it on GitLab: https://salsa.debian.org/debian/adduser/-/compare/e8e85ad145b02e60a3d3c95fbe19f0400a2f3d73...86c3aa1de298fcaaaace7e1bf1867f5ebbb95709
--
View it on GitLab: https://salsa.debian.org/debian/adduser/-/compare/e8e85ad145b02e60a3d3c95fbe19f0400a2f3d73...86c3aa1de298fcaaaace7e1bf1867f5ebbb95709
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-shadow-devel/attachments/20260107/a22658cc/attachment-0001.htm>
More information about the Pkg-shadow-devel
mailing list