396 lines
11 KiB
Bash
Executable File
396 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
E_VALIDATE="1" # wrong password etc
|
|
E_CONFLICT="2" # mapping already exists etc
|
|
E_INVALID="3" # invalid argument given
|
|
E_PRIVILEGE="4" # user cannot access password etc
|
|
E_GENERAL="99" # uncategorized error
|
|
|
|
function bootstrap {
|
|
local fuser="$1"
|
|
local upass="$2"
|
|
# initializes passman into a usable state, using '$1' as its initial administrator
|
|
# '$2' is the initial user's password
|
|
# returns an error if any remnants of a working state are detected, in which case it does nothing
|
|
|
|
local count=0
|
|
for i in user group pass
|
|
do
|
|
[ -e $i ] && (( count++ ))
|
|
done
|
|
|
|
if [ "$count" = 3 ] && [ -e group/admin.* ]
|
|
then
|
|
echo "passman seems to be bootstrapped already, not doing anything."
|
|
return $E_CONFLICT
|
|
|
|
elif [ ! "$count" = 0 ]
|
|
then
|
|
echo "passman seems to be in an inconsistent state, please clean up before trying to bootstrap."
|
|
return $E_GENERAL
|
|
fi
|
|
|
|
mkdir user
|
|
mkdir group
|
|
mkdir pass
|
|
chmod -R u=rwX,g=rX,o=rX .
|
|
|
|
local utoken=$(make-token)
|
|
local atoken=$(make-token)
|
|
|
|
encrypt user/"$fuser" "$upass" "$utoken"
|
|
encrypt group/admin."$fuser" "$utoken" "$atoken"
|
|
encrypt user/"$fuser".admin "$atoken" "$utoken"
|
|
|
|
return 0
|
|
}
|
|
|
|
function get-user-token {
|
|
local uname=$1
|
|
local pass=$2
|
|
# prints the token associated with '$1' by decrypting the file
|
|
# user/'$1' with '$2'
|
|
# if the decryption encounters an error, its error code is returned
|
|
|
|
decrypt user/"$uname" "$pass"
|
|
}
|
|
|
|
function get-admin-token {
|
|
local uname="$1"
|
|
local pass="$2"
|
|
# prints the token associated with '$1' by decrypting the file
|
|
# group/admin.'$1' with '$2'
|
|
# if the decryption encounters an error, its error code is returned
|
|
|
|
decrypt group/admin."$uname" "$pass"
|
|
}
|
|
|
|
function encrypt {
|
|
local name="$1"
|
|
local pass="$2"
|
|
local cont="$3"
|
|
# encrypts '$3' in '$1' with '$2'
|
|
# if the file exists, an error is returned
|
|
# if the encryption encounters an error, its error code is returned
|
|
|
|
[ -e "$name" ] && return $E_CONFLICT
|
|
|
|
touch "$name"
|
|
chmod 600 "$name"
|
|
echo -e "$cont" > "$name"
|
|
|
|
export pass
|
|
ccrypt -eq -S "" -E pass "$name" 2>/dev/null
|
|
}
|
|
|
|
function decrypt {
|
|
local name="$1"
|
|
local pass="$2"
|
|
# tries to decrypt and print '$1' using '$2'
|
|
# if the decryption encounters an error, its error code is returned
|
|
|
|
export pass
|
|
ccrypt -cq -E pass "$name" 2>/dev/null || return $E_VALIDATE
|
|
}
|
|
|
|
function make-token {
|
|
# prints a generated string for use as intermediate encryption key for groups/users
|
|
|
|
pwgen -s 128 1
|
|
}
|
|
|
|
function list-user-groups {
|
|
local uname="$1"
|
|
# prints all groups that '$1' belongs to as a space-delimited list
|
|
|
|
local out=$(ls group/*.$uname 2>/dev/null | sed -r -e "s%^group/%%" -e "s%\.$uname$%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-group-passes {
|
|
local gname="$1"
|
|
# prints all password files belonging to '$1' as a space-delimited list
|
|
# does not decrypt any passwords
|
|
|
|
local out=$(ls pass/*.$gname 2>/dev/null | sed -r -e "s%^pass/%%" -e "s%\.$gname$%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-password-groups {
|
|
local pname="$1"
|
|
# prints all the groups that '$1' belongs to as a space-delimited list
|
|
|
|
local out=$(ls pass/$pname.* 2>/dev/null | sed -r "s%^pass/$pname\.%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-group-users {
|
|
local gname="$1"
|
|
# prints all the users that belong to '$1' as a space-delimited list
|
|
|
|
local out=$(ls group/$gname.* 2>/dev/null | sed -r "s%^group/$gname\.%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-passwords {
|
|
# prints all the passwords stored in the system
|
|
# does not decrypt any passwords, only prints identifiers
|
|
local out=$(ls pass/ | sed -r -e "s%^pass/%%" -e "s%\.[^.]+$%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-users {
|
|
local out=$(ls user/ | sed -r -e "s%^user/%%" -e "s%\.[^.]+$%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-groups {
|
|
local out=$(ls group/ | sed -r -e "s%^group/%%" -e "s%\.[^.]+$%%" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function list-available {
|
|
local uname="$1"
|
|
# prints all the password files that '$1' has access to as a space-delimited list
|
|
# does not decrypt any passwords
|
|
|
|
local groups=$(list-user-groups "$uname")
|
|
local passes=""
|
|
|
|
for gname in $groups
|
|
do
|
|
passes=$(list-group-passes "$gname")' '"$passes"
|
|
done
|
|
|
|
local out=$(printf "%s" "$passes" | sort -u)
|
|
printf "%s\n" "$out"
|
|
}
|
|
|
|
function show-pass {
|
|
local uname="$1"
|
|
local utoken="$2"
|
|
local pname="$3"
|
|
# decrypts and prints '$3', using '$2' belonging to '$1'
|
|
# if '$1' cannot access '$3', an error is returned
|
|
|
|
local ugroups=$(list-user-groups "$uname")
|
|
local pgroups=$(list-password-groups "$pname")
|
|
|
|
for group in $pgroups
|
|
do
|
|
echo "$ugroups" | grep -q "$group"
|
|
if [ "$?" = "0" ]
|
|
then
|
|
local gtoken=$(decrypt group/"$group"."$uname" "$utoken")
|
|
|
|
decrypt pass/"$pname"."$group" "$gtoken"
|
|
return $?
|
|
fi
|
|
done
|
|
return $E_PRIVILEGE
|
|
}
|
|
|
|
function add-user {
|
|
local admintoken="$1"
|
|
local uname="$2"
|
|
local upass="$3"
|
|
# adds '$2' to the system with '$3', authenticating with '$1'
|
|
# returns an error if the username 'admin' is supplied or the username already exists
|
|
# will NOT return an error if an invalid '$1' is supplied, because there isn't necessarily anything to validate against
|
|
|
|
local utoken=$(make-token)
|
|
|
|
[ "$uname" = "admin" ] && return $E_CONFLICT
|
|
|
|
encrypt user/"$uname".admin "$admintoken" "$utoken" || return $?
|
|
encrypt user/"$uname" "$upass" "$utoken" || return $?
|
|
}
|
|
|
|
function change-user-pass {
|
|
local utoken="$1"
|
|
local uname="$2"
|
|
local newpass="$3"
|
|
# changes '$2's password to '$3', authenticating with '$1'
|
|
# if the user does not belong to any groups, the password is changed without question
|
|
# otherwise, the supplied '$1' is checked against one of the groups the user belongs to
|
|
|
|
local testfile=$(ls group/*."$uname" 2>/dev/null | tr '\n' ' ' | cut -d' ' -f1)
|
|
|
|
[ -n "$testfile" ] && { decrypt "$testfile" "$utoken" &> /dev/null || return $E_VALIDATE; }
|
|
rm user/"$uname"
|
|
encrypt user/"$uname" "$newpass" "$utoken" || return $?
|
|
|
|
}
|
|
|
|
function remove-user {
|
|
local admintoken="$1"
|
|
local uname="$2"
|
|
# removes '$2' from the system, given that '$1' is valid
|
|
# also removes all group mappings for the user
|
|
# returns an error if the user doesn't exist or '$1' is invalid
|
|
|
|
decrypt user/"$uname".admin "$admintoken" &> /dev/null|| return $E_VALIDATE
|
|
|
|
rm user/"$uname"*
|
|
rm group/*."$uname" 2>/dev/null || true
|
|
}
|
|
|
|
function make-user-admin {
|
|
local admintoken="$1"
|
|
local uname="$2"
|
|
# makes '$2' an administrator
|
|
# an error will be returned if '$1' is invalid
|
|
|
|
local utoken=$(decrypt user/"$uname".admin "$admintoken") || return $E_VALIDATE
|
|
|
|
encrypt group/admin."$uname" "$utoken" "$admintoken"
|
|
}
|
|
|
|
function unmake-user-admin {
|
|
local admintoken="$1"
|
|
local uname="$2"
|
|
# revokes '$2's admin privileges
|
|
# an error is returned if the user isn't an administrator or '$1' is invalid
|
|
|
|
decrypt user/"$uname".admin "$admintoken" &> /dev/null || return $?
|
|
|
|
rm group/admin."$uname"
|
|
}
|
|
|
|
function map-user-group {
|
|
local admintoken="$1"
|
|
local uname="$2"
|
|
local gname="$3"
|
|
# adds '$2' to '$3'
|
|
# admin privileges cannot be granted via this function
|
|
# an error is returned if '$3' is 'admin', '$2' already belongs to '$3', or '$1' is invalid
|
|
|
|
[ "$gname" = "admin" ] && return $E_PRIVILEGE
|
|
[ -e group/"$gname"."$uname" ] && return $E_CONFLICT
|
|
local utoken=$(decrypt user/"$uname".admin "$admintoken") || return $E_VALIDATE
|
|
local gtoken=$(decrypt group/"$gname".admin "$admintoken") || return $E_VALIDATE
|
|
|
|
encrypt group/"$gname"."$uname" "$utoken" "$gtoken"
|
|
}
|
|
|
|
function unmap-user-group {
|
|
local admintoken="$1"
|
|
local uname="$2"
|
|
local gname="$3"
|
|
# removes '$2' from '$3'
|
|
# admin privileges cannot be revoked via this function
|
|
# an error is returned if '$3' is admin, the mapping doesn't exist, or '$1' is invalid
|
|
|
|
[ "$gname" = "admin" ] && return $E_PRIVILEGE
|
|
decrypt group/"$gname".admin "$admintoken" &> /dev/null || return $?
|
|
|
|
rm group/"$gname"."$uname"
|
|
}
|
|
|
|
function add-group {
|
|
local admintoken="$1"
|
|
local gname="$2"
|
|
# adds a group called '$2'
|
|
# an error is returned if '$2' is 'admin', the group already exists, or '$1' is invalid
|
|
|
|
[ "$gname" = "admin" ] && return $E_PRIVILEGE
|
|
[ -e group/"$gname".admin ] && return $E_CONFLICT
|
|
|
|
local gtoken=$(make-token)
|
|
|
|
encrypt group/"$gname".admin "$admintoken" "$gtoken"
|
|
}
|
|
|
|
function remove-group {
|
|
local admintoken="$1"
|
|
local gname="$2"
|
|
# removes the group named '$2'
|
|
# an error is returned if '$2' is 'admin', '$2' doesn't exist, or '$1' is invalid
|
|
|
|
[ "$gname" = "admin" ] && return $E_PRIVILEGE
|
|
decrypt group/"$gname".admin "$admintoken" &> /dev/null || return $?
|
|
|
|
rm pass/*."$gname" 2>/dev/null || true
|
|
rm group/"$gname".*
|
|
}
|
|
|
|
function map-pass-group {
|
|
local admintoken="$1"
|
|
local pname="$2"
|
|
local gname="$3"
|
|
# adds '$3' to '$2'
|
|
# an error is returned if '$2' is 'admin', the password is already in the group, or '$1' is invalid
|
|
|
|
[ "$gname" = "admin" ] && return $E_PRIVILEGE
|
|
[ -e pass/"$pname"."$gname" ] && return $E_CONFLICT
|
|
|
|
local gtoken=$(decrypt group/"$gname".admin "$admintoken") || return $E_VALIDATE
|
|
local pass=$(decrypt pass/"$pname".admin "$admintoken") || return $E_VALIDATE
|
|
|
|
encrypt pass/"$pname"."$gname" "$gtoken" "$pass"
|
|
}
|
|
|
|
function unmap-pass-group {
|
|
local admintoken="$1"
|
|
local pname="$2"
|
|
local gname="$3"
|
|
# removes '$3' from '$2'
|
|
# returns an error if '$2' or '$3' is 'admin', the mapping doesn't exist, or '$1' is invalid
|
|
|
|
[ "$gname" = "admin" ] && return $E_PRIVILEGE
|
|
decrypt pass/"$pname".admin "$admintoken" &> /dev/null || return $E_VALIDATE
|
|
decrypt group/"$gname".admin "$admintoken" &> /dev/null || return $E_VALIDATE
|
|
|
|
rm pass/"$pname"."$gname"
|
|
}
|
|
|
|
function add-pass {
|
|
local admintoken="$1"
|
|
local pname="$2"
|
|
local pass="$3"
|
|
# adds a password to the system. It is identified by '$2', and its value is '$3'
|
|
# an error is returned if '$1' is invalid or '$2' already exists
|
|
|
|
encrypt pass/"$pname".admin "$admintoken" "$pass"
|
|
}
|
|
|
|
function remove-pass {
|
|
local admintoken="$1"
|
|
local pname="$2"
|
|
# removes the password named '$2' from the system
|
|
# an error is returned if '$1' is invalid or '$2' doesn't exist
|
|
|
|
decrypt pass/"$pname".admin "$admintoken" &> /dev/null || return $?
|
|
|
|
rm pass/"$pname".*
|
|
}
|
|
|
|
function modify-pass {
|
|
local admintoken="$1"
|
|
local pname="$2"
|
|
local newpass="$3"
|
|
# replaces the content of '$2' with '$3'
|
|
# an error is returned if '$1' is invalid or '$2' doesn't exist
|
|
|
|
for group in $(list-password-groups "$pname")
|
|
do
|
|
rm pass/"$pname"."$group"
|
|
if [ "$group" == "admin" ]
|
|
then
|
|
continue
|
|
fi
|
|
|
|
gtok=$(decrypt group/"$group".admin "$admintoken")
|
|
res=$?
|
|
[ -z "$gtok" ] && return "$res"
|
|
|
|
encrypt pass/"$pname"."$group" "$gtok" "$newpass"
|
|
|
|
done
|
|
|
|
encrypt pass/"$pname".admin "$admintoken" "$newpass"
|
|
return $?
|
|
}
|