1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2026-03-04 15:54:48 +01:00

Merge remote-tracking branch 'fspoettel/admin-panel-2fa' into totp

# Conflicts:
#	management/auth.py
#	management/daemon.py
#	management/mailconfig.py
#	setup/mail-users.sh
This commit is contained in:
downtownallday
2020-09-28 23:25:16 -04:00
21 changed files with 602 additions and 366 deletions

View File

@@ -29,7 +29,7 @@ create_user() {
local email="$1"
local pass="${2:-$email}"
local priv="${3:-test}"
local totpVal="${4:-}" # "secret,token"
local totpVal="${4:-}" # "secret,token,label"
local localpart="$(awk -F@ '{print $1}' <<< "$email")"
local domainpart="$(awk -F@ '{print $2}' <<< "$email")"
#local uid="$localpart"
@@ -47,12 +47,13 @@ create_user() {
local totpObjectClass=""
local totpSecret="$(awk -F, '{print $1}' <<< "$totpVal")"
local totpMruToken="$(awk -F, '{print $2}' <<< "$totpVal")"
local totpLabel="$(awk -F, '{print $3}' <<< "$totpVal")"
if [ ! -z "$totpVal" ]; then
local nl=$'\n'
totpObjectClass="${nl}objectClass: totpUser"
totpSecret="${nl}totpSecret: ${totpSecret}"
[ ! -z "$totpMruToken" ] && \
totpMruToken="${nl}totpMruToken: ${totpMruToken}"
totpSecret="${nl}totpSecret: {0}${totpSecret}"
totpMruToken="${nl}totpMruToken: {0}${totpMruToken}"
totpLabel="${nl}totpLabel: {0}${totpLabel}"
fi
ldapadd -H "$LDAP_URL" -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" >>$TEST_OF 2>&1 <<EOF
@@ -66,7 +67,7 @@ sn: $localpart
displayName: $localpart
mail: $email
maildrop: $email
mailaccess: $priv${totpSecret}${totpMruToken}
mailaccess: $priv${totpSecret}${totpMruToken}${totpLabel}
userPassword: $(slappasswd_hash "$pass")
EOF
[ $? -ne 0 ] && die "Unable to add user $dn (as admin)"

View File

@@ -218,6 +218,18 @@ mgmt_get_totp_token() {
return 1
}
mgmt_mfa_status() {
local user="$1"
local pw="$2"
record "[Get MFA status]"
if ! mgmt_rest_as_user "GET" "/admin/mfa/status" "$user" "$pw"; then
REST_ERROR="Failed: GET /admin/mfa/status: $REST_ERROR"
return 1
fi
# json is in REST_OUTPUT...
return 0
}
mgmt_totp_enable() {
# enable TOTP for user specified
@@ -228,17 +240,17 @@ mgmt_totp_enable() {
local user="$1"
local pw="$2"
local label="$3" # optional
TOTP_SECRET=""
record "[Enable TOTP for $user]"
# 1. get a totp secret
if ! mgmt_rest_as_user "GET" "/admin/mfa/status" "$user" "$pw"; then
REST_ERROR="Failed: GET/admin/mfa/status: $REST_ERROR"
if ! mgmt_mfa_status "$user" "$pw"; then
return 1
fi
TOTP_SECRET="$(/usr/bin/jq -r ".totp_secret" <<<"$REST_OUTPUT")"
TOTP_SECRET="$(/usr/bin/jq -r ".new_mfa.totp.secret" <<<"$REST_OUTPUT")"
if [ $? -ne 0 ]; then
record "Unable to obtain setup totp secret - is 'jq' installed?"
return 2
@@ -255,9 +267,9 @@ mgmt_totp_enable() {
return 2
fi
# 3. enable TOTP
# 2. enable TOTP
record "Enabling TOTP using the secret and token"
if ! mgmt_rest_as_user "POST" "/admin/mfa/totp/enable" "$user" "$pw" "secret=$TOTP_SECRET" "token=$TOTP_TOKEN"; then
if ! mgmt_rest_as_user "POST" "/admin/mfa/totp/enable" "$user" "$pw" "secret=$TOTP_SECRET" "token=$TOTP_TOKEN" "label=$label"; then
REST_ERROR="Failed: POST /admin/mfa/totp/enable: ${REST_ERROR}"
return 1
else
@@ -288,13 +300,41 @@ mgmt_assert_totp_enable() {
}
mgmt_totp_disable() {
mgmt_mfa_disable() {
# returns:
# 0: success
# 1: a REST error occurred, message in REST_ERROR
# 2: some system error occured
# 3: mfa is not configured for the user specified
local user="$1"
local pw="$2"
record "[Disable TOTP for $user]"
if ! mgmt_rest_as_user "POST" "/admin/mfa/totp/disable" "$user" "$pw"
local mfa_id="$3"
record "[Disable MFA for $user]"
if [ "$mfa_id" == "all" ]; then
mfa_id=""
elif [ "$mfa_id" == "" ]; then
# get first mfa-id
if ! mgmt_mfa_status "$user" "$pw"; then
return 1
fi
mfa_id="$(/usr/bin/jq -r ".enabled_mfa[0].id" <<<"$REST_OUTPUT")"
if [ $? -ne 0 ]; then
record "Unable to use /usr/bin/jq - is it installed?"
return 2
fi
if [ "$mfa_id" == "null" ]; then
record "No enabled mfa found at .enabled_mfa[0].id"
return 3
fi
fi
if ! mgmt_rest_as_user "POST" "/admin/mfa/disable" "$user" "$pw" "mfa-id=$mfa_id"
then
REST_ERROR="Failed: POST /admin/mfa/totp/disable: $REST_ERROR"
REST_ERROR="Failed: POST /admin/mfa/disable: $REST_ERROR"
return 1
else
record "Success"
@@ -302,12 +342,12 @@ mgmt_totp_disable() {
fi
}
mgmt_assert_totp_disable() {
mgmt_assert_mfa_disable() {
local user="$1"
mgmt_totp_disable "$@"
mgmt_mfa_disable "$@"
local code=$?
if [ $code -ne 0 ]; then
test_failure "Unable to disable TOTP for $user: $REST_ERROR"
test_failure "Unable to disable MFA for $user: $REST_ERROR"
return 1
fi
get_attribute "$LDAP_USERS_BASE" "(&(mail=$user)(objectClass=totpUser))" "dn"

View File

@@ -3,16 +3,16 @@
# Access assertions:
# service accounts, except management:
# can bind but not change passwords, including their own
# can read all attributes of all users but not userPassword, totpSecret, totpMruToken
# can read all attributes of all users but not userPassword, totpSecret, totpMruToken, totpLabel
# can not write any user attributes, including shadowLastChange
# can read config subtree (permitted-senders, domains)
# no access to services subtree, except their own dn
# users:
# can bind and change their own password
# can read and change their own shadowLastChange
# no read or write access to user's own totpSecret or totpMruToken
# no read or write access to user's own totpSecret, totpMruToken or totpLabel
# can read attributess of all users except:
# mailaccess, totpSecret, totpMruToken
# mailaccess, totpSecret, totpMruToken, totpLabel
# no access to config subtree
# no access to services subtree
# other:
@@ -38,24 +38,25 @@ test_user_change_password() {
test_user_access() {
# 1. can read attributess of all users except mailaccess, totpSecret, totpMruToken
# 1. can read attributess of all users except mailaccess, totpSecret, totpMruToken, totpLabel
# 2. can read and change their own shadowLastChange
# 3. no access to config subtree
# 4. no access to services subtree
# 5. no read or write access to own totpSecret or totpMruToken
# 5. no read or write access to own totpSecret, totpMruToken, or totpLabel
test_start "user-access"
local totpSecret="12345678901234567890"
local totpMruToken="94287082"
local totpLabel="my phone"
# create regular user's alice and bob
local alice="alice@somedomain.com"
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken"
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken,$totpLabel"
local alice_dn="$ATTR_DN"
local bob="bob@somedomain.com"
create_user "bob@somedomain.com" "bob" "" "$totpSecret,$totpMruToken"
create_user "bob@somedomain.com" "bob" "" "$totpSecret,$totpMruToken,$totpLabel"
local bob_dn="$ATTR_DN"
# alice should be able to set her own shadowLastChange
@@ -64,27 +65,27 @@ test_user_access() {
# test that alice can read her own attributes
assert_r_access "$alice_dn" "$alice_dn" "alice" read mail maildrop cn sn shadowLastChange
# alice should not have access to her own mailaccess, totpSecret or totpMruToken, though
assert_r_access "$alice_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken
# alice should not have access to her own mailaccess, totpSecret, totpMruToken or totpLabel, though
assert_r_access "$alice_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken totpLabel
# test that alice cannot change her own select attributes
assert_w_access "$alice_dn" "$alice_dn" "alice"
# test that alice cannot change her own totpSecret or totpMruToken
assert_w_access "$alice_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456"
# test that alice cannot change her own totpSecret, totpMruToken or totpLabel
assert_w_access "$alice_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456" "totpLabel=x-phone"
# test that alice can read bob's attributes
assert_r_access "$bob_dn" "$alice_dn" "alice" read mail maildrop cn sn
# alice should not have access to bob's mailaccess, totpSecret, or totpMruToken
assert_r_access "$bob_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken
# alice should not have access to bob's mailaccess, totpSecret, totpMruToken, or totpLabel
assert_r_access "$bob_dn" "$alice_dn" "alice" no-read mailaccess totpSecret totpMruToken totpLabel
# test that alice cannot change bob's select attributes
assert_w_access "$bob_dn" "$alice_dn" "alice"
# test that alice cannot change bob's attributes
assert_w_access "$bob_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456"
assert_w_access "$bob_dn" "$alice_dn" "alice" no-write "totpSecret=ABC" "totpMruToken=123456" "totpLabel=x-phone"
# test that alice cannot read a service account's attributes
@@ -151,10 +152,11 @@ test_service_access() {
local totpSecret="12345678901234567890"
local totpMruToken="94287082"
local totpLabel="my phone"
# create regular user with password "alice"
local alice="alice@somedomain.com"
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken"
create_user "alice@somedomain.com" "alice" "" "$totpSecret,$totpMruToken,$totpLabel"
# create a test service account
create_service_account "test" "test"
@@ -174,12 +176,12 @@ test_service_access() {
# check that service account can read user attributes
assert_r_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" read mail maildrop uid cn sn shadowLastChange
# service account should not be able to read user's userPassword, totpSecret or totpMruToken
assert_r_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-read userPassword totpSecret totpMruToken
# service account should not be able to read user's userPassword, totpSecret, totpMruToken, or totpLabel
assert_r_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-read userPassword totpSecret totpMruToken totpLabel
# service accounts cannot change user attributes
assert_w_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD"
assert_w_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-write "shadowLastChange=1" "totpSecret=ABC" "totpMruToken=333333"
assert_w_access "$alice_dn" "$LDAP_POSTFIX_DN" "$LDAP_POSTFIX_PASSWORD" no-write "shadowLastChange=1" "totpSecret=ABC" "totpMruToken=333333" "totpLabel=x-phone"
fi
# service accounts can read config subtree (permitted-senders, domains)

View File

@@ -215,7 +215,11 @@ test_totp() {
# alice must be admin to use TOTP
if ! have_test_failures; then
mgmt_assert_privileges_add "$alice" "admin"
if mgmt_totp_enable "$alice" "$alice_pw"; then
test_failure "User must be an admin to use TOTP, but server allowed it"
else
mgmt_assert_privileges_add "$alice" "admin"
fi
fi
# add totp to alice's account (if successful, secret is in TOTP_SECRET)
@@ -227,7 +231,7 @@ test_totp() {
# logging in with just the password should now fail
if ! have_test_failures; then
record "Expect a login failure..."
mgmt_assert_admin_me "$alice" "$alice_pw" "missing_token"
mgmt_assert_admin_me "$alice" "$alice_pw" "missing-totp-token"
fi
@@ -251,7 +255,7 @@ test_totp() {
# ensure the totpMruToken was changed in LDAP
get_attribute "$LDAP_USERS_BASE" "(mail=$alice)" "totpMruToken"
if [ "$ATTR_VALUE" != "$TOTP_TOKEN" ]; then
if [ "$ATTR_VALUE" != "{0}$TOTP_TOKEN" ]; then
record_search "(mail=$alice)"
test_failure "totpMruToken wasn't updated in LDAP"
fi
@@ -268,7 +272,7 @@ test_totp() {
# disable totp on the account - login should work with just the password
# and the ldap entry should not have the 'totpUser' objectClass
if ! have_test_failures; then
if mgmt_assert_totp_disable "$alice" "$api_key"; then
if mgmt_assert_mfa_disable "$alice" "$api_key"; then
mgmt_assert_admin_me "$alice" "$alice_pw" "ok"
fi
fi