#!/usr/bin/perl -I.
#
# Simple management for OpenFPC users and passwords.
#
#########################################################################################
# Copyright (C) 2011 Leon Ward 
# openfpc-passwd - Part of the OpenFPC - (Full Packet Capture) project
#
# Contact: leon@openfpc.org
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#########################################################################################

use strict;
use warnings;
use Data::Dumper;
use Switch;
use Term::ReadKey;
use File::Copy;
use Getopt::Long;
use Digest::SHA;

my $hashout=0;
my $pass=0;
my %users=();
my $update=0;
my $action="add";
my $file="/etc/openfpc/openfpc.passwd";
my $user=0;
my $showhelp;
my $noroot=0;		# Disable root user check
my $openfpcver="0.9";


sub showhelp{
	print "
Usage: openfpc-password <args>
  -a or --action        One of add / change / list / del / check
     add:               Add a new user
     change:            Change password for existing user
     list:              List all users in file
     del:               Remove user
     check:             Check password for user
  -u or --user          UserID to take action on
  -p or --password      Password
  -f or --file          OpenFPC User file to edit (default = /etc/openfpc/openfpc.passwd)
  -n or --noroot        Don't check for root privs
        --hash          Output the hash that would be used for a --user with --password

Note: backups are taken if any changes are made.
";
	exit 0;
}

sub mkhash{
	my $user=shift;
	my $pass=shift;
	my ($digest,$hash);
	die("ERROR: $user or $pass not set") unless ($user and $pass);
	
	$digest = Digest::SHA->new(1);
	$digest->add($user,$pass);
	$hash = $digest->hexdigest;
	
	return($hash);
}

sub getpass{
	my $p1;
	my $p2;
	print "    Enter new password: ";
	ReadMode 'noecho';
	$p1 = ReadLine 0;
	chomp $p1;
	print "\n    Retype password: ";
	$p2 = ReadLine 0;
	chomp $p2;
	ReadMode 'normal';

	if ($p1 eq $p2) {
		print "\n    Password Okay\n";
	} else {
		print "\n    Password mismatch, please retry\n";
		exit(1);
	}
	return $p1;
}

sub getuser{
	my $user;
	print "[-] Enter Username: ";
	$user=<STDIN>;
	chomp $user;
	return $user;
}

GetOptions (    'a|action=s' => 	\$action,
		'u|username=s' => 	\$user,
		'p|password=s' =>	\$pass,
		'f|file=s' => 	\$file,
		'h|help' => 		\$showhelp,
		'n|noroot' =>		\$noroot,
		'hash' =>		\$hashout,
);

showhelp if $showhelp;


if ($hashout) {
	$pass=getpass();
	my $hash=mkhash($user,$pass);
	print "\npassword=$hash\n";
	exit 0;
}

die("Error: Must specify filename. See --help\n") unless $file;


if ( -f $file ) {
       	open FILE, '<', "$file" or die "Unable to open config file $file $!";
       	while(my $line=<FILE>) {
               	chomp $line;
		if ($line =~ /^OFPC_ENABLED/ ) {
			die("This Looks like this is an OpenFPC config file, not a user file. \nWont continue.\n");
		}

	        if ( $line =~ m/^SHA1/) {
 	 		(my $key, my @value) = split /=/, $line;
               	        #$users{$key} = $value[0];
			$users{$value[0]} = $value[1] ;
               	}	   
       	}   
       	close(FILE);
} else {
	print "Creating new user file $file...\n";
}

switch($action) {
	case "add" {
		unless ($noroot) {
			unless ($> == 0 || $< == 0) { die "This action requires root privileges.\n" }
		}
		$user=getuser() unless $user;
		unless ( defined $users{$user}) {
			print "[*] Adding user $user\n";
			unless ($pass) {
				$pass=getpass();
			}
			my $hash=mkhash($user,$pass);
			$users{$user}=$hash;
			$update=1;
		} else {
			die("[!] Error: User $user already exists\n");
		}
	}
	case /(delete|del)/ {
		print "[*] Deleting user\n";
		unless ($noroot) {
			unless ($> == 0 || $< == 0) { die "This action requires root privileges.\n" }
		}
		$user=getuser() unless $user;

		if (defined $users{$user}) {
			delete $users{$user};
			$update=1;
			print "[?] Delete user $user? (y/N): ";
			my $confirm = <STDIN>;
			chomp($confirm);
			if ( $confirm =~ /^[Yy]/ ) {
				print "    Deleting user $user\n";
			} else {
				print "    Canceling operation\n";
				exit(1);
			}
		} else {
			die("[!] Error: Cant find user in $file\n");
		}
	}
	case "list" {
		print "======================================================\n".
		      " OpenFPC users in file $file\n" .
		      "======================================================\n";
		foreach (keys %users) {
			print "User $_ Pass $users{$_}\n";
		}
	}
	case "change" {
		unless ($noroot) {
			unless ($> == 0 || $< == 0) { die "This action requires root privileges.\n" }
		}
		$user=getuser() unless $user;

		if ( defined $users{$user}) {
			my $pass = getpass();
			my $hash=mkhash($user,$pass);
			$users{$user}=$hash;
			$update=1;
		} else {
			die("[!] Error: User $user not found in $file\n");
		}
	}
	case "check" {
		$user=getuser() unless $user;

		# Check a password against a stored value
		if ( defined $users{$user}) {
			print "    Enter password for $user :";
			ReadMode 'noecho';
			my $pass = ReadLine 0;
			chomp $pass;	
			ReadMode 'normal';
			
			
			if (mkhash($user,$pass) eq $users{$user}) {
				print " - Password correct\n";
			} else {
				print " - Password incorrect\n";
			}
		} else {
			die("User $user not found in $file\n");
		}
	} else {
		die("[!] Error: Invalid action \"$action\". Specify an action with --action. \nSee --help for more details.\n");
	}
}

if ($update) {
	# If we have added or changed a value in the user hash, lets write it back to the file 
	# while also creating a backup.
	# Backup existing config, and replace it with our new file.
	my $epoch=time();
	if ( -f $file) {
        		move($file,"$file.backup.$epoch") or die ("ERROR: Unable to backup $file to $file.backup.$epoch - Check file permissions\n");
	}

	open(NEWFILE,'>', "$file") or die("ERROR: Can't open file $file");
	print NEWFILE "########################################\n";
	print NEWFILE "# OpenFPC User file.\n# This file is auto generated. Do not edit\n";

	foreach (keys %users){
		print NEWFILE "SHA1=$_=$users{$_}\n";
	}
	close(NEWFILE);
	print "[*] Done.\n";
}
