Files
passman/cryptapi.sh
thuning @ admin 1c7a197cc1 Whitespace
2024-10-11 13:57:44 +02:00

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 $?
}