diff --git a/cf-serverd/server_classic.c b/cf-serverd/server_classic.c index 8fc439cb10b..5fa1194aefa 100644 --- a/cf-serverd/server_classic.c +++ b/cf-serverd/server_classic.c @@ -406,7 +406,6 @@ static int LiteralAccessControl(EvalContext *ctx, char *in, ServerConnectionStat static Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; - int access = false; char client_regex[CF_BUFSIZE]; Item *ip, *matches = NULL; @@ -422,6 +421,9 @@ static Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionSt /* Does the class match the regex that the agent requested? */ if (StringMatchFull(client_regex, ip->name)) { + /* Each class must be granted by its own access rule. */ + int access = false; + for (ap = SERVER_ACCESS.varadmit; ap != NULL; ap = ap->next) { /* Does the class match any of the regex in ACLs? */ diff --git a/tests/unit/protocol_test.c b/tests/unit/protocol_test.c index 29b30d31a5d..549950e0f2c 100644 --- a/tests/unit/protocol_test.c +++ b/tests/unit/protocol_test.c @@ -5,9 +5,36 @@ #include #include #include +#include +#include +#include /* xsnprintf */ #include /* GetCommandClassic */ +char CFWORKDIR[CF_BUFSIZE]; + +void tests_setup(void) +{ + static char env[] = /* Needs to be static for putenv() */ + "CFENGINE_TEST_OVERRIDE_WORKDIR=/tmp/CFENGINE_protocol_test.XXXXXX"; + char *workdir = strchr(env, '='); + assert(workdir && workdir[1] == '/'); + workdir++; + + mkdtemp(workdir); + strlcpy(CFWORKDIR, workdir, CF_BUFSIZE); + putenv(env); + + mkdir(GetStateDir(), 0766); +} + +void tests_teardown(void) +{ + char cmd[CF_BUFSIZE]; + xsnprintf(cmd, CF_BUFSIZE, "rm -rf '%s'", CFWORKDIR); + system(cmd); +} + /* * The protocol consists of the following commands: @@ -450,14 +477,59 @@ static void test_user_name(void) assert_true(IsUserNameValid(valid_user_name)); } +static void test_context_access_control_per_class(void) +{ + /* Two persistent classes, only "zz_allowed" is admitted to the client. */ + { + CF_DB *dbp; + PersistentClassInfo info = { .expires = UINT_MAX, + .policy = CONTEXT_STATE_POLICY_PRESERVE }; + assert_true(OpenDB(&dbp, dbid_state)); + assert_true(WriteDB(dbp, "aa_denied", &info, sizeof(info))); + assert_true(WriteDB(dbp, "zz_allowed", &info, sizeof(info))); + CloseDB(dbp); + } + + Auth *ap = xcalloc(1, sizeof(Auth)); + ap->path = xstrdup("zz_allowed"); + ap->classpattern = true; + PrependItem(&ap->accesslist, "127.0.0.1", NULL); + SERVER_ACCESS.varadmit = ap; + + ServerConnectionState conn = { 0 }; + strlcpy(conn.ipaddr, "127.0.0.1", sizeof(conn.ipaddr)); + strlcpy(conn.hostname, "client.example.com", sizeof(conn.hostname)); + + EvalContext *ctx = EvalContextNew(); + char request[] = "CONTEXT .*"; + Item *classes = ContextAccessControl(ctx, request, &conn, false); + + assert_true(IsItemIn(classes, "zz_allowed")); + assert_false(IsItemIn(classes, "aa_denied")); + + DeleteItemList(classes); + EvalContextDestroy(ctx); + SERVER_ACCESS.varadmit = NULL; + DeleteItemList(ap->accesslist); + free(ap->path); + free(ap); +} + int main() { PRINT_TEST_BANNER(); + tests_setup(); + const UnitTest tests[] = { unit_test(test_command_parser), - unit_test(test_user_name) + unit_test(test_user_name), + unit_test(test_context_access_control_per_class) }; - return run_tests(tests); + int ret = run_tests(tests); + + tests_teardown(); + + return ret; }