Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions db/comdb2.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ int gbl_vtab_externalauth = 1;
int gbl_uses_simpleauth = 0;
int gbl_uses_externalauth_connect = 0;
int gbl_externalauth_warn = 0;
int gbl_passwords_with_externalauth = 0;
int gbl_identity_cache_max = 500;
int gbl_authorization_cache_max = 2000;
int gbl_authentication_cache_ageout = 900;
Expand Down
1 change: 1 addition & 0 deletions db/comdb2.h
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,7 @@ extern int gbl_vtab_externalauth;
extern int gbl_uses_simpleauth;
extern int gbl_uses_externalauth_connect;
extern int gbl_externalauth_warn;
extern int gbl_passwords_with_externalauth;
extern int gbl_identity_cache_max;
extern int gbl_authorization_cache_max;
extern int gbl_authentication_cache_ageout;
Expand Down
78 changes: 32 additions & 46 deletions db/db_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,21 @@ void get_client_origin(char *out, size_t outlen, struct sqlclntstate *clnt) {
clnt->argv0 ? clnt->argv0 : "?",
clnt->origin ? clnt->origin: "?",
clnt->conninfo.pid);
}
}

static void report_access_denied(const char *action, const char *table, const char *user, int bdberr, errstat_t *err)
{
char msg[1024];
if (bdberr)
snprintf(msg, sizeof(msg), "%s access denied to %s for user %s bdberr=%d", action, table, user, bdberr);
else
snprintf(msg, sizeof(msg), "%s access denied to table %s for user %s", action, table, user);
logmsg(LOGMSG_INFO, "%s\n", msg);
if (err) {
errstat_set_rc(err, SQLITE_ACCESS);
errstat_set_str(err, msg);
}
}

int gbl_fdb_auth_error = 0;

Expand Down Expand Up @@ -126,7 +140,8 @@ int check_user_password(struct sqlclntstate *clnt)
clnt->allow_make_request = 1;
ATOMIC_ADD64(gbl_num_auth_allowed, 1);
}
return rc;
if (rc || !gbl_passwords_with_externalauth)
return rc;
}

if ((!remsql_warned || gbl_fdb_auth_error) && (!gbl_uses_password && !gbl_uses_externalauth) &&
Expand Down Expand Up @@ -265,11 +280,7 @@ int access_control_check_sql_write(struct BtCursor *pCur,
if ((authdata = get_authdata(clnt)) != NULL)
clnt->authdata = authdata;
char client_info[1024];
snprintf(client_info, sizeof(client_info),
"%s:origin:%s:pid:%d",
clnt->argv0 ? clnt->argv0 : "?",
clnt->origin ? clnt->origin: "?",
clnt->conninfo.pid);
get_client_origin(client_info, sizeof(client_info), clnt);
if (!clnt->authdata && clnt->secure && !gbl_allow_anon_id_for_spmux) {
return reject_anon_id(clnt);
}
Expand All @@ -278,31 +289,20 @@ int access_control_check_sql_write(struct BtCursor *pCur,
clnt->argv0 ? clnt->argv0 : "???", clnt->conninfo.pid, clnt->conninfo.node);
} else if (externalComdb2AuthenticateUserWrite(clnt->authdata, table_name, client_info)) {
ATOMIC_ADD64(gbl_num_auth_denied, 1);
char msg[1024];
snprintf(msg, sizeof(msg), "Write access denied to table %s for user %s",
table_name, clnt->externalAuthUser ? clnt->externalAuthUser : "");
logmsg(LOGMSG_INFO, "%s\n", msg);
errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS);
errstat_set_str(&thd->clnt->osql.xerr, msg);
report_access_denied("Write", table_name, clnt->externalAuthUser ? clnt->externalAuthUser : "", 0,
&thd->clnt->osql.xerr);
return SQLITE_ABORT;
}
} else {
/* Check read access if its not user schema. */
}
if ((gbl_passwords_with_externalauth && !clnt->admin) || !gbl_uses_externalauth) {
/* Check write access via password ACLs. */
/* Check it only if engine is open already. */
if (gbl_uses_password && !clnt->current_user.bypass_auth && (thd->clnt->in_sqlite_init == 0)) {
rc = bdb_check_user_tbl_access(
pCur->db->dbenv->bdb_env, thd->clnt->current_user.name,
pCur->db->tablename, ACCESS_WRITE, &bdberr);
rc = bdb_check_user_tbl_access(pCur->db->dbenv->bdb_env, thd->clnt->current_user.name, pCur->db->tablename,
ACCESS_WRITE, &bdberr);
if (rc != 0) {
ATOMIC_ADD64(gbl_num_auth_denied, 1);
char msg[1024];
snprintf(msg, sizeof(msg),
"Write access denied to %s for user %s bdberr=%d",
table_name, thd->clnt->current_user.name, bdberr);
logmsg(LOGMSG_INFO, "%s\n", msg);
errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS);
errstat_set_str(&thd->clnt->osql.xerr, msg);

report_access_denied("Write", table_name, thd->clnt->current_user.name, bdberr, &thd->clnt->osql.xerr);
return SQLITE_ABORT;
}
}
Expand Down Expand Up @@ -352,40 +352,26 @@ int access_control_check_sql_read(struct BtCursor *pCur, struct sql_thread *thd,
if ((authdata = get_authdata(clnt)) != NULL)
clnt->authdata = authdata;
char client_info[1024];
snprintf(client_info, sizeof(client_info),
"%s:origin:%s:pid:%d",
clnt->argv0 ? clnt->argv0 : "?",
clnt->origin ? clnt->origin: "?",
clnt->conninfo.pid);
get_client_origin(client_info, sizeof(client_info), clnt);
if (!clnt->authdata && clnt->secure && !gbl_allow_anon_id_for_spmux)
return reject_anon_id(clnt);
if (gbl_externalauth_warn && !clnt->authdata) {
logmsg(LOGMSG_INFO, "Client %s pid:%d mach:%d is missing authentication data\n",
clnt->argv0 ? clnt->argv0 : "???", clnt->conninfo.pid, clnt->conninfo.node);
} else if (externalComdb2AuthenticateUserRead(clnt->authdata, table_name, client_info)) {
ATOMIC_ADD64(gbl_num_auth_denied, 1);
char msg[1024];
snprintf(msg, sizeof(msg), "Read access denied to table %s for user %s",
table_name, clnt->externalAuthUser ? clnt->externalAuthUser : "");
logmsg(LOGMSG_INFO, "%s\n", msg);
errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS);
errstat_set_str(&thd->clnt->osql.xerr, msg);
report_access_denied("Read", table_name, clnt->externalAuthUser ? clnt->externalAuthUser : "", 0,
&thd->clnt->osql.xerr);
return SQLITE_ABORT;
}
} else {
}
if ((gbl_passwords_with_externalauth && !clnt->admin) || !gbl_uses_externalauth) {
if (gbl_uses_password && !clnt->current_user.bypass_auth && table_name && thd->clnt->in_sqlite_init == 0) {
rc = bdb_check_user_tbl_access(thedb->bdb_env, thd->clnt->current_user.name, (char *)table_name,
ACCESS_READ, &bdberr);
if (rc != 0) {
ATOMIC_ADD64(gbl_num_auth_denied, 1);
char msg[1024];
snprintf(msg, sizeof(msg),
"Read access denied to %s for user %s bdberr=%d",
table_name, thd->clnt->current_user.name, bdberr);
logmsg(LOGMSG_INFO, "%s\n", msg);
errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS);
errstat_set_str(&thd->clnt->osql.xerr, msg);

report_access_denied("Read", table_name, thd->clnt->current_user.name, bdberr, &thd->clnt->osql.xerr);
return SQLITE_ABORT;
}
}
Expand Down
2 changes: 2 additions & 0 deletions db/db_tunables.h
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,8 @@ REGISTER_TUNABLE("externalauth_warn", "Warn instead of returning error in case o
TUNABLE_BOOLEAN, &gbl_externalauth_warn, NOARG | READEARLY,
NULL, NULL, NULL, NULL);

REGISTER_TUNABLE("passwords_with_externalauth", "Check password auth in addition to externalauth (Default: off)",
TUNABLE_BOOLEAN, &gbl_passwords_with_externalauth, NOARG | READEARLY, NULL, NULL, NULL, NULL);

REGISTER_TUNABLE("view_feature", "Enables support for VIEWs (Default: ON)",
TUNABLE_BOOLEAN, &gbl_view_feature, 0, NULL, NULL, NULL, NULL);
Expand Down
8 changes: 8 additions & 0 deletions tests/simpleauth_password.test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ifeq ($(TESTSROOTDIR),)
include ../testcase.mk
else
include $(TESTSROOTDIR)/testcase.mk
endif
ifeq ($(TEST_TIMEOUT),)
export TEST_TIMEOUT=3m
endif
4 changes: 4 additions & 0 deletions tests/simpleauth_password.test/dummy.csc2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
schema
{
byte dummy[1]
}
4 changes: 4 additions & 0 deletions tests/simpleauth_password.test/lrl.options
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
simpleauth
passwords_with_externalauth
table dummy dummy.csc2
create_dba_user off
173 changes: 173 additions & 0 deletions tests/simpleauth_password.test/runit
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#!/usr/bin/env bash
bash -n "$0" | exit 1

set -e
source ${TESTSROOTDIR}/tools/runit_common.sh

dbnm=$1

if [ "x$dbnm" == "x" ] ; then
failexit "need a DB name"
fi

SA="${TESTSBUILDDIR}/simpleauth_test ${dbnm}"
OP="bpi:procauth:cluster:test:user:op:bpkg:setup"
MOHIT="bpi:procauth:cluster:testcluster:user:mohit:bpkg:myapp"

passed=0
failed=0

run_expect_success() {
local principal="$1"
local sql="$2"
local desc="$3"
if $SA "$principal" "$sql" > /dev/null 2>&1; then
echo " PASS: $desc"
passed=$((passed + 1))
else
echo " FAIL: $desc"
failed=$((failed + 1))
fi
}

run_expect_failure() {
local principal="$1"
local sql="$2"
local desc="$3"
if $SA "$principal" "$sql" > /dev/null 2>&1; then
echo " FAIL: $desc (expected failure but succeeded)"
failed=$((failed + 1))
else
echo " PASS: $desc"
passed=$((passed + 1))
fi
}

# Run SQL via admin connection with OP password user (bypasses IAM).
admin_sql() {
local sql="$1"
${CDB2SQL_EXE} --admin ${CDB2_OPTIONS} ${dbnm} default - <<EOF
set user admin_user
set password adminpass
$sql
EOF
}

#---------------------------------------------------------------
echo "Test: Setup comdb2_simple_auth table and IAM rules"
#---------------------------------------------------------------
run_expect_success "$OP" "create table if not exists comdb2_simple_auth(cluster cstring(20) default '*', user cstring(20) default '*', bpkg cstring(50) default '*', verb cstring(20) default '*', resourcetype cstring(20) default '*', resourcename cstring(50) default '*')" \
"create auth table"
run_expect_success "$OP" "create unique index if not exists comdb2_simple_auth_ix on comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename)" \
"create auth index"
# Wildcard rule: allow everyone everything via IAM
run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', '*', '*', '*') on conflict do nothing" \
"insert wildcard IAM rule"
run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', 'Connect', '*', '*') on conflict do nothing" \
"insert Connect rule"
# OP needs explicit IAM access to auth table after wildcard removal
run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', 'op', '*', 'Write', 'table', 'comdb2_simple_auth') on conflict do nothing" \
"grant op IAM write on auth table"
run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', 'op', '*', 'Read', 'table', 'comdb2_simple_auth') on conflict do nothing" \
"grant op IAM read on auth table"

#---------------------------------------------------------------
echo "Test: Create test table and enable password auth"
#---------------------------------------------------------------
run_expect_success "$OP" "create table if not exists t1(i int)" \
"create test table t1"
# Create password users: 'default' (non-OP) for data tests,
# 'admin_user' (OP) for management via admin connection.
run_expect_success "$OP" "put password '' for 'default'" \
"create default password user"
run_expect_success "$OP" "put password 'adminpass' for 'admin_user'" \
"create admin password user"
run_expect_success "$OP" "grant op to 'admin_user'" \
"grant OP to admin_user"
# Grant table-level password ACLs to default user
run_expect_success "$OP" "grant read on t1 to 'default'" \
"grant password read on t1 to default"
run_expect_success "$OP" "grant write on t1 to 'default'" \
"grant password write on t1 to default"
# Enable password authentication -- from this point both IAM and
# password checks must pass.
# Note: put authentication on uses comdb2AuthenticateOpPassword which
# requires password-based OP credentials (ignores IAM), so we must
# use admin_sql with admin_user/adminpass rather than simpleauth_test.
admin_sql "put authentication on"
echo " password authentication enabled"

#---------------------------------------------------------------
echo "Test: Both IAM and password pass"
#---------------------------------------------------------------
# simpleauth_test provides IAM identity (wildcard allows everything).
# Password user is 'default' with correct password and table ACLs.
run_expect_success "$MOHIT" "select * from t1" \
"read t1: IAM pass + password pass"
run_expect_success "$MOHIT" "insert into t1 values(1)" \
"write t1: IAM pass + password pass"

#---------------------------------------------------------------
echo "Test: IAM passes but password table ACL denied"
#---------------------------------------------------------------
# Revoke password-based table ACLs for default user.
# IAM wildcard still allows, but bdb_check_user_tbl_access will fail
# because 'default' is not OP and has no table grants.
admin_sql "revoke read on t1 from 'default'"
admin_sql "revoke write on t1 from 'default'"
run_expect_failure "$MOHIT" "select * from t1" \
"read t1: IAM pass + password ACL denied"
run_expect_failure "$MOHIT" "insert into t1 values(2)" \
"write t1: IAM pass + password ACL denied"
# Restore password ACLs
admin_sql "grant read on t1 to 'default'"
admin_sql "grant write on t1 to 'default'"
# Verify restored
run_expect_success "$MOHIT" "select * from t1" \
"read t1 after restore: both pass"

#---------------------------------------------------------------
echo "Test: IAM denied but password passes"
#---------------------------------------------------------------
# Remove wildcard IAM rule. Keep the Connect rule so MakeRequest
# still passes (allows connection but not table-level access).
admin_sql "delete from comdb2_simple_auth where cluster='*' and user='*' and bpkg='*' and verb='*' and resourcetype='*' and resourcename='*'"
# MOHIT has no IAM Read/Write rule for t1 -> IAM table check fails
# before reaching the password ACL check.
run_expect_failure "$MOHIT" "select * from t1" \
"read t1: IAM denied + password pass (fails at IAM)"
run_expect_failure "$MOHIT" "insert into t1 values(3)" \
"write t1: IAM denied + password pass (fails at IAM)"
# Restore wildcard IAM rule
admin_sql "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', '*', '*', '*') on conflict do nothing"
# Verify restored
run_expect_success "$MOHIT" "select * from t1" \
"read t1 after IAM restore: both pass"

#---------------------------------------------------------------
echo "Test: Neither IAM nor password passes"
#---------------------------------------------------------------
admin_sql "delete from comdb2_simple_auth where cluster='*' and user='*' and bpkg='*' and verb='*' and resourcetype='*' and resourcename='*'"
admin_sql "revoke read on t1 from 'default'"
admin_sql "revoke write on t1 from 'default'"
run_expect_failure "$MOHIT" "select * from t1" \
"read t1: IAM denied + password denied"
run_expect_failure "$MOHIT" "insert into t1 values(4)" \
"write t1: IAM denied + password denied"

#---------------------------------------------------------------
echo "Test: Cleanup"
#---------------------------------------------------------------
admin_sql "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', '*', '*', '*') on conflict do nothing"
admin_sql "drop table if exists t1"
admin_sql "delete from comdb2_simple_auth where user != '*'"

echo ""
echo "Results: $passed passed, $failed failed"

if [ $failed -gt 0 ] ; then
failexit "$failed tests failed"
fi

echo "Success"
exit 0
1 change: 1 addition & 0 deletions tests/tunables.test/t00_all_tunables.expected
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@
(name='partition_retroactively_verbose', description='Disable/enable data routing debugging for retroactively time partitioning (Default: OFF)', type='BOOLEAN', value='OFF', read_only='N')
(name='partition_sc_reorder', description='If the schema change is serialized for a partition, run current shard last', type='BOOLEAN', value='ON', read_only='N')
(name='partitioned_table_enabled', description='Allow syntax create/alter table ... partitioned by ...', type='BOOLEAN', value='ON', read_only='N')
(name='passwords_with_externalauth', description='Check password auth in addition to externalauth (Default: off)', type='BOOLEAN', value='OFF', read_only='N')
(name='pause_moveto', description='pause_moveto', type='BOOLEAN', value='OFF', read_only='N')
(name='pbkdf2_iterations', description='Number of iterations of PBKDF2 algorithm for password hashing.', type='INTEGER', value='4096', read_only='N')
(name='penaltyincpercent', description='', type='INTEGER', value='20', read_only='Y')
Expand Down
Loading