mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-21 11:29:33 +01:00
Store user passwords as salted hash digests
This was way overdue. User passwords are no longer stored as cleartext. When PBot is restarted after applying this commit, all stored passwords will be converted to salted hash digests. The `useradd`, `userset` and `my` commands will now hash passwords. Why did it take me so long to finally get around to hashing passwords properly, you might ask. The reason why this wasn't done sooner is because all of my users used hostmask-based `autologin`. The passwords that PBot randomly generated were ignored and never used. I do regret that it took me so long to get around to this, for those of you who might be using custom passwords instead of hostmask-based `autologin`.
This commit is contained in:
parent
784c2508e5
commit
6722fd7f8d
1
cpanfile
1
cpanfile
@ -12,6 +12,7 @@ requires 'perl' => '5.020000';
|
||||
# PBot core
|
||||
requires 'Cache::FileCache';
|
||||
requires 'Carp';
|
||||
requires 'Crypt::SaltedHash';
|
||||
requires 'DateTime';
|
||||
requires 'DateTime::Format::Duration';
|
||||
requires 'DateTime::Format::Flexible';
|
||||
|
@ -109,7 +109,7 @@ Parameter | Description
|
||||
`hostmasks` | The hostmasks from which this user is recognized/allowed to login from (e.g., `somenick!*@*.somedomain.com` or `*!*@unaffiliated/someuser`). Can be a comma-separated list of values.
|
||||
`channels` | The channels this user belongs to; use `global` for all channels. Can be a comma-separated list of values.
|
||||
`capabilities` | A comma-separated list of [user-capabilities](#user-capabilities) for this user.
|
||||
`password` | The password the user will use to login (from `/msg`, obviously). Generates a random password if omitted. Users may view and set their password by using the [`my`](Commands.md#my) command.
|
||||
`password` | The password the user will use to login (from `/msg`, obviously). Users may update their password by using the [`my`](Commands.md#my) command once logged in.
|
||||
|
||||
### userdel
|
||||
Removes a user from PBot.
|
||||
|
12
doc/FAQ.md
12
doc/FAQ.md
@ -101,13 +101,19 @@ to set the `preserve_whitespace` [factoid metadata](Factoids.md#factoid-metadata
|
||||
|
||||
## How do I change my password?
|
||||
If you have a NickServ account or a unique hostmask, you don't need a PBot password.
|
||||
The `stayloggedin` metadata on your user account can be set instead.
|
||||
The `autologin` and `stayloggedin` metadata on your user account can be set instead.
|
||||
|
||||
But if you prefer to be safe instead of sorry, use the [`my`](Commands.md#my) command
|
||||
to set the `password` user metadata for your user account. Your hostmask must match the
|
||||
user account.
|
||||
to set the `password` and unset the `autologin` and `stayloggedin` metadata for your
|
||||
user account. Your hostmask must match the user account and you must be logged in.
|
||||
|
||||
my password <your password>
|
||||
my autologin 0
|
||||
my stayloggedin 0
|
||||
|
||||
If you are unable to log in, ask an admin to set a temporary password for you
|
||||
with the [`userset`](Admin.md#userset) command. Log in with the temporary
|
||||
password and then use the above commands to update your password.
|
||||
|
||||
## How do I make PBot remember my `date` timezone?
|
||||
Use the [`my`](Commands.md#my) command to set the `timezone` user metadata for your
|
||||
|
@ -362,7 +362,7 @@ command in the PBot terminal console. Its usage is:
|
||||
|
||||
Suppose your nick is `Bob` and your hostmask is `Bob!~user@some.domain.com`. Use the following command:
|
||||
|
||||
useradd Bob Bob!~user@*.domain.com global botowner
|
||||
useradd Bob Bob!~user@*.domain.com global botowner <password>
|
||||
|
||||
This will create a user account named `Bob` with the `botowner` [user-capability](Admin.md#user-capabilities) that can administrate
|
||||
all channels. Note the wildcard replacing `some` in `some.domain.com`. Now as long as
|
||||
@ -372,16 +372,14 @@ It is very important that user account hostmasks are defined as strictly or as n
|
||||
as possible to match only the person it is intended for. Ideally, the user would have a
|
||||
NickServ account, a user-cloak given by the staff of the IRC server or a unique DNS name.
|
||||
|
||||
In your own IRC client, connected using the hostmask we just added, type the
|
||||
following command, in a private `/query` or `/msg`:
|
||||
|
||||
my password
|
||||
|
||||
This will show you the randomly generated password that was assigned to your
|
||||
user account. You can change it -- if you want to -- with:
|
||||
You can change your password with:
|
||||
|
||||
my password <new password>
|
||||
|
||||
or
|
||||
|
||||
userset Bob password <new password>
|
||||
|
||||
Then you can login with:
|
||||
|
||||
login <password>
|
||||
@ -389,8 +387,30 @@ Then you can login with:
|
||||
Now you can use `/msg` in your own IRC client to administrate PBot, instead of
|
||||
the terminal console.
|
||||
|
||||
If you want to autologin without typing a password, first ensure your hostmask is unique -- preferably
|
||||
by using a NickServ vhost/cloak or a reverse-DNS name. Then set the following metadata on your account:
|
||||
|
||||
userset Bob autologin 1
|
||||
|
||||
If you want to remain permanently logged in, ensure your hostmask is unique and set the following metadata on your account:
|
||||
|
||||
userset Bob stayloggedin 1
|
||||
|
||||
### Adding other users and admins
|
||||
To add users to PBot, use the [`useradd`](Admin.md#useradd) command. Its usage is:
|
||||
Users may create their own unprivileged accounts by using the [`my`](Commands.md#my) command. It will automatically
|
||||
set their username, hostmask, channel and log them into it.
|
||||
|
||||
Users added this way will have `autologin` and `stayloggedin` enabled. If they feel their hostmask is insecure, they
|
||||
can disable `autologin` and `stayloggedin` with:
|
||||
|
||||
my autologin 0
|
||||
my stayloggedin 0
|
||||
|
||||
And then update their login password with:
|
||||
|
||||
my password <new password>
|
||||
|
||||
Alternatively, you can manually add users to PBot with the [`useradd`](Admin.md#useradd) command. Its usage is:
|
||||
|
||||
useradd <username> <hostmasks> [channels [capabilities [password]]]
|
||||
|
||||
@ -399,14 +419,25 @@ The `hostmasks` and `channels` arguments can be a comma-separated list of values
|
||||
If you omit the `capabilities` argument, the user will be a normal unprivileged user. See [user-capabilities](Admin.md#user-capabilities)
|
||||
for more information about user-capabilities.
|
||||
|
||||
If you omit the `password` argument, a random password will be generated. The user
|
||||
can use the [`my`](Commands.md#my) command to view or change it.
|
||||
|
||||
Users may view and change their own metadata by using the [`my`](Commands.md#my) command,
|
||||
provided their hostmask matches the user account.
|
||||
|
||||
my [key [value]]
|
||||
|
||||
Admins may change a user's password with:
|
||||
|
||||
userset <username> password <new password>
|
||||
|
||||
If the user has a unique hostmask, preferable via a NickServ vhost/cloak or a reverse-DNS name, they
|
||||
may prefer to use passwordless autologin via:
|
||||
|
||||
userset <username> autologin 1
|
||||
|
||||
If the user has a unique hostmask, preferable via a NickServ vhost/cloak or a reverse-DNS name, they
|
||||
may prefer to remain permanently logged in via:
|
||||
|
||||
userset <username> stayloggedin 1
|
||||
|
||||
For more information, see the [Admin documentation](Admin.md).
|
||||
|
||||
### Adding channels
|
||||
|
@ -237,6 +237,10 @@ sub cmd_userset($self, $context) {
|
||||
return "To set the $key capability your user account must also have it." unless $self->{pbot}->{capabilities}->userhas($u, 'botowner');
|
||||
}
|
||||
|
||||
if ($key eq 'password' and defined $value) {
|
||||
$value = $self->{pbot}->{users}->digest_password($value);
|
||||
}
|
||||
|
||||
my $result = $self->{pbot}->{users}->{storage}->set($name, $key, $value);
|
||||
$result =~ s/^password: .*;?$/password: <private>;/m;
|
||||
|
||||
@ -351,6 +355,10 @@ sub cmd_my($self, $context) {
|
||||
$result = "Usage: my <key> [value]; ";
|
||||
}
|
||||
|
||||
if ($key eq 'password' and defined $value) {
|
||||
$value = $self->{pbot}->{users}->digest_password($value);
|
||||
}
|
||||
|
||||
$result .= $self->{pbot}->{users}->{storage}->set($name, $key, $value);
|
||||
$result =~ s/^password: .*;?$/password: <private>;/m;
|
||||
return $result;
|
||||
|
@ -10,6 +10,8 @@ use parent 'PBot::Core::Class';
|
||||
|
||||
use PBot::Imports;
|
||||
|
||||
use Crypt::SaltedHash;
|
||||
|
||||
sub initialize($self, %conf) {
|
||||
$self->{storage} = PBot::Core::Storage::HashObject->new(
|
||||
pbot => $conf{pbot},
|
||||
@ -27,6 +29,7 @@ sub add_user($self, $name, $channels, $hostmasks, $capabilities = 'none', $passw
|
||||
$channels = 'global' if $channels !~ m/^#/;
|
||||
|
||||
$password //= $self->{pbot}->random_nick(16);
|
||||
$password = $self->digest_password($password);
|
||||
|
||||
my $data = {
|
||||
channels => $channels,
|
||||
@ -177,7 +180,7 @@ sub login($self, $channel, $hostmask, $password = undef) {
|
||||
return "You do not have a user account$channel_text.";
|
||||
}
|
||||
|
||||
if (defined $password and $user->{password} ne $password) {
|
||||
if (defined $password and !Crypt::SaltedHash->validate($user->{password}, $password)) {
|
||||
$self->{pbot}->{logger}->log("Bad login password for $channel $hostmask\n");
|
||||
return "I don't think so.";
|
||||
}
|
||||
@ -218,4 +221,10 @@ sub get_loggedin_user_metadata($self, $channel, $hostmask, $key) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub digest_password($self, $password) {
|
||||
my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-512');
|
||||
$csh->add($password);
|
||||
return $csh->generate;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -25,8 +25,8 @@ use PBot::Imports;
|
||||
# These are set by the /misc/update_version script
|
||||
use constant {
|
||||
BUILD_NAME => "PBot",
|
||||
BUILD_REVISION => 4761,
|
||||
BUILD_DATE => "2024-06-12",
|
||||
BUILD_REVISION => 4762,
|
||||
BUILD_DATE => "2024-06-22",
|
||||
};
|
||||
|
||||
sub initialize {}
|
||||
|
60
updates/4762_hash_passwords.pl
Executable file
60
updates/4762_hash_passwords.pl
Executable file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# Replaces user cleartext passwords with salted hashes.
|
||||
#
|
||||
# This was way overdue. User passwords are no longer stored as cleartext.
|
||||
#
|
||||
# Why did it take me so long to finally get around to hashing passwords
|
||||
# properly, you might ask. The reason why this wasn't done sooner is because
|
||||
# all of my users used hostmask-based `autologin`. The passwords that PBot
|
||||
# randomly generated were ignored and never used.
|
||||
#
|
||||
# I do regret that it took me so long to get around to this, for those of you
|
||||
# who might be using custom passwords instead of hostmask-based `autologin`.
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
BEGIN {
|
||||
use File::Basename;
|
||||
my $location = -l __FILE__ ? dirname readlink __FILE__ : dirname __FILE__;
|
||||
unshift @INC, $location;
|
||||
}
|
||||
|
||||
use lib4422::HashObject;
|
||||
use lib3503::PBot;
|
||||
|
||||
use Crypt::SaltedHash;
|
||||
|
||||
my ($data_dir, $version, $last_update) = @ARGV;
|
||||
|
||||
print "Hashing passwords ... version: $version, last_update: $last_update, data_dir: $data_dir\n";
|
||||
|
||||
my $pbot = lib3503::PBot->new();
|
||||
|
||||
my $users = lib4422::HashObject->new(name => 'Users', filename => "$data_dir/users", pbot => $pbot);
|
||||
|
||||
$users->load;
|
||||
|
||||
if (not keys $users->{hash}->%*) {
|
||||
die "No users loaded";
|
||||
}
|
||||
|
||||
print "Updating users:\n";
|
||||
|
||||
foreach my $user (keys %{$users->{hash}}) {
|
||||
if ($user eq '$metadata$') {
|
||||
$users->{hash}->{$user}->{update_version} = 4762;
|
||||
next;
|
||||
}
|
||||
|
||||
print " $user ...";
|
||||
my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-512');
|
||||
$csh->add($users->{hash}->{$user}->{password});
|
||||
$users->{hash}->{$user}->{password} = $csh->generate;
|
||||
print " done\n";
|
||||
}
|
||||
|
||||
$users->save;
|
||||
print "Done.\n";
|
||||
exit 0;
|
Loading…
Reference in New Issue
Block a user