Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Script to manage database file for PAM authentication (vsftpd)
#!/usr/bin/perl -w
#
# pam_userdb_admin.pl - pam_userdb(8) authentication database management tool
#
# Copyright (c) 2011 EPIPE Communications <http://epipe.com/>
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
#
# DESCRIPTION
# ===========
#
# This script may be used to maintain a user database file for PAM module
# mod_userdb(8). It can be convenient for example for maintaining vsftpd
# virtual users database.
#
# The $usrtxtfn configuration variable below defines a text format file
# which is used as a master copy of the database. It has the following
# format:
#
# USERNAME:PASSWORD
#
# The PASSWORD can be in plain text or hashed with crypt(3) depending on
# your mod_userdb(8) arguments in your PAM configuration file. Please set
# the $hashpwd configuration variable below accordingly.
#
# The $usrdbfn variable below defines the authentication database which is
# actually used by pam_userdb(8). This script always completely overwrites
# the database file based on the text file contents.
#
#
# USAGE
# =====
#
# Basic usage is as follows:
#
# Create the database from text file:
#
# pam_userdb_admin update
#
# Add user "foo" with password "kissa" to the end of the text file and
# update the database:
#
# pam_userdb_admin adduser foo kissa
#
# Add user "test", read password from standard input:
#
# pam_userdb_admin adduser test -
#
# Add user, read username and password from standard input (on separate lines):
#
# pam_userdb_admin adduser - -
#
# Dump the current database contents:
#
# pam_userdb_admin dump
#
# Delete user:
# vi /etc/vsftpd_login
# pam_userdb_admin update
#
# Usage help:
#
# pam_userdb_admin help
#
use strict;
#
# CONFIGURATION SETTINGS
# ======================
#
# The name of the text format user file which contains the entries which are
# converted to the machine readable database file:
my $usrtxtfn = '/etc/vsftpd_login';
#
# The name of the user database file which is used by pam_userdb(8) for
# authentication:
my $usrdbfn = '/etc/vsftpd_login.db';
#
# The permissions (in octal) of the user database file (umask is also applied):
my $usrdbmode = 0660;
#
# Are hashed or plaintext passwords in use? (1 = hashed, 0 = plaintext)
my $hashpwd = 1;
#
# End of configuration settings.
#
use DB_File;
# The following function is used to create or update the database file from
# the text file:
sub updatedb () {
# open the input file
open(my $txtfh, '<', $usrtxtfn)
or die "$usrtxtfn: $!";
# create a temporary user database file with the following name
my $usrdbtmpfn = $usrdbfn . ".$$.tmp";
my %usrdb = ();
# create the temporary database
tie %usrdb, 'DB_File', $usrdbtmpfn, O_RDWR|O_CREAT, $usrdbmode, $DB_HASH
or die "$usrdbtmpfn: $1";
while (<$txtfh>) {
chomp;
next if /^#/;
next if /^\s*$/;
my ($user, $pass) = split /:/;
if (!defined($user) || $user eq '' || !defined($pass)) {
print STDERR "$usrtxtfn:$. invalid line ignored\n";
next;
}
if (defined($usrdb{$user})) {
print STDERR "$usrtxtfn:$. duplicate user $user"
. " ignored\n";
next;
}
$usrdb{$user} = $pass;
}
untie %usrdb;
undef $txtfh;
# move the new user database in place, hopefully atomically
rename($usrdbtmpfn, $usrdbfn)
or die "rename($usrdbtmpfn, $usrdbfn): $!";
}
# generate salt
sub gensalt ($) {
my $count = shift;
my @saltchars = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
my $salt;
for (1..$count) {
$salt .= (@saltchars)[rand @saltchars];
}
return $salt;
}
# add user
sub adduser ($$) {
my $username = shift;
my $password = shift;
chomp($username = <STDIN>) if $username eq '-';
chomp($password = <STDIN>) if $password eq '-';
if ($hashpwd) {
# The following uses MD5 hashed password with 8 character salt
# (which is the default on most Linux distros and FreeBSD:
#my $salt = '$1$' . gensalt(8) . '$';
# Unfortunately that does not work with mod_userdb, so we use
# traditional format
my $salt = gensalt(2);
$password = crypt($password, $salt);
}
open(my $fh, '>>', $usrtxtfn)
or die "$usrtxtfn: $!";
print $fh "$username:$password\n";
undef $fh;
}
# dump the db to STDOUT
sub dumpdb () {
my %usrdb = ();
# attach to the database
tie %usrdb, 'DB_File', $usrdbfn, O_RDONLY
or die "$usrdbfn: $!";
while (my ($k, $v) = each %usrdb) {
print "$k:$v\n";
}
untie %usrdb;
}
# usage help
sub usage () {
print "usage: $0 command [opts]\n";
print "\n";
print "command is one of:\n";
print "\tupdate\n\t\tre-creates the database from text file\n";
print "\tdump\n\t\tdump the current database to standard output\n";
print "\tadduser USERNAME PASSWORD\n";
print "\t\tadd user, crypt the password if needed and update the db\n";
print "\t\tif USERNAME and/or PASSWORD is \"-\" it is read from STDIN\n";
print "\n";
exit(2);
}
# main()
if (@ARGV == 0) {
usage();
}
if ($ARGV[0] eq 'update' && @ARGV == 1) {
updatedb();
} elsif ($ARGV[0] eq 'adduser' && @ARGV == 3) {
adduser($ARGV[1], $ARGV[2]);
updatedb();
} elsif ($ARGV[0] eq 'dump' && @ARGV == 1) {
dumpdb();
} else {
usage();
}
exit(0);
# eof
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.