Created
March 15, 2012 18:54
-
-
Save skarllot/2046012 to your computer and use it in GitHub Desktop.
Script to manage database file for PAM authentication (vsftpd)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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