mirror of
https://github.com/ovh/the-bastion.git
synced 2025-09-07 13:34:28 +08:00
To avoid having e.g. a group creation interrupted in the middle just because the caller killed their ssh connection while we're still working
96 lines
3.2 KiB
Perl
Executable file
96 lines
3.2 KiB
Perl
Executable file
#! /usr/bin/perl -T
|
|
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
|
|
# NEEDGROUP osh-accountMFAResetPassword
|
|
# SUDOERS %osh-accountMFAResetPassword ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountMFAResetPassword --account *
|
|
# FILEMODE 0700
|
|
# FILEOWN 0 0
|
|
|
|
#>HEADER
|
|
use common::sense;
|
|
use Getopt::Long;
|
|
|
|
use File::Basename;
|
|
use lib dirname(__FILE__) . '/../../lib/perl';
|
|
use OVH::Result;
|
|
use OVH::Bastion;
|
|
local $| = 1;
|
|
|
|
#
|
|
# Globals
|
|
#
|
|
$SIG{'HUP'} = 'IGNORE'; # continue even when attached terminal is closed (we're called with setsid on supported systems anyway)
|
|
$SIG{'PIPE'} = 'IGNORE'; # continue even if osh_info gets a SIGPIPE because there's no longer a terminal
|
|
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/pkg/bin';
|
|
my ($self) = $ENV{'SUDO_USER'} =~ m{^([a-zA-Z0-9._-]+)$};
|
|
if (not defined $self) {
|
|
if ($< == 0) {
|
|
$self = 'root';
|
|
}
|
|
else {
|
|
HEXIT('ERR_SUDO_NEEDED', msg => 'This command must be run under sudo');
|
|
}
|
|
}
|
|
|
|
# Fetch command options
|
|
my $fnret;
|
|
my ($result, @optwarns);
|
|
my ($account);
|
|
eval {
|
|
local $SIG{__WARN__} = sub { push @optwarns, shift };
|
|
$result = GetOptions("account=s" => sub { $account //= $_[1] },);
|
|
};
|
|
if ($@) { die $@ }
|
|
|
|
if (!$result) {
|
|
local $" = ", ";
|
|
HEXIT('ERR_BAD_OPTIONS', msg => "Error parsing options: @optwarns");
|
|
}
|
|
|
|
if (not $account) {
|
|
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'account'");
|
|
}
|
|
|
|
#>PARAMS:ACCOUNT
|
|
osh_debug("Checking account");
|
|
$fnret = OVH::Bastion::is_bastion_account_valid_and_existing(account => $account);
|
|
$fnret or HEXIT($fnret);
|
|
$account = $fnret->value->{'account'}; # untainted
|
|
|
|
#<PARAMS:ACCOUNT
|
|
|
|
#>RIGHTSCHECK
|
|
if ($self eq 'root') {
|
|
osh_debug "Real root, skipping checks of permissions";
|
|
}
|
|
|
|
# special case for self: if account==self, then is ok
|
|
elsif ($self ne $account) {
|
|
$fnret = OVH::Bastion::is_user_in_group(user => $self, group => "osh-accountMFAResetPassword");
|
|
if (!$fnret) {
|
|
HEXIT('ERR_SECURITY_VIOLATION', msg => "You're not allowed to run this, dear $self");
|
|
}
|
|
}
|
|
|
|
#<RIGHTSCHECK
|
|
|
|
# don't allow a non-admin to reset the Password of an admin
|
|
if (OVH::Bastion::is_admin(account => $account, sudo => 1) && !OVH::Bastion::is_admin(account => $self, sudo => 1)) {
|
|
HEXIT('ERR_SECURITY_VIOLATION', msg => "You can't reset the password of an admin without being admin yourself");
|
|
}
|
|
|
|
if (OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_CONFIGURED_GROUP)) {
|
|
$fnret = OVH::Bastion::sys_delmemberfromgroup(user => $account, group => OVH::Bastion::MFA_PASSWORD_CONFIGURED_GROUP);
|
|
$fnret or HEXIT($fnret);
|
|
}
|
|
|
|
$fnret = OVH::Bastion::sys_neutralizepassword(user => $account);
|
|
$fnret or HEXIT($fnret);
|
|
|
|
# remove expiration, or user could get locked out if s/he doesn't quickly set a new password,
|
|
# as the password expiration time is still taken into account even for '*' passwords
|
|
# 99999 is the /etc/shadow way to say "never" (273 years)
|
|
$fnret = OVH::Bastion::sys_setpasswordpolicy(user => $account, maxDays => 99999);
|
|
$fnret or HEXIT($fnret);
|
|
|
|
osh_info "Password has been reset, " . ($account eq $self ? 'you' : $account) . " can setup a new password by using the `--osh selfMFASetupPassword' command, if applicable";
|
|
HEXIT('OK');
|