Skip to content

Commit b8e4c70

Browse files
authored
Merge pull request #5832 from larsewi/getacls
CFE-4529: Added policy function getacls()
2 parents 133fe5e + 99a7fb2 commit b8e4c70

File tree

4 files changed

+185
-0
lines changed

4 files changed

+185
-0
lines changed

libpromises/acl_tools.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
#ifndef CFENGINE_ACL_TOOLS_H
2626
#define CFENGINE_ACL_TOOLS_H
2727

28+
#include <stdbool.h>
29+
#include <rlist.h>
30+
31+
32+
/**
33+
* @brief Get ACLs from a file or directory
34+
* @param Path to file or directory
35+
* @param access Get access ACLs if true, otherwise default ACLs
36+
* @return List of ACLs. On error, NULL is returned and errno is set to
37+
* indicate the error.
38+
*/
39+
Rlist *GetACLs(const char *path, bool access);
40+
2841
bool CopyACLs(const char *src, const char *dst, bool *change);
2942

3043
/**

libpromises/acl_tools_posix.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,39 @@ bool AllowAccessForUsers(const char *path, StringSet *users, bool allow_writes,
259259
return true;
260260
}
261261

262+
Rlist *GetACLs(const char *path, bool access)
263+
{
264+
assert(path != NULL);
265+
266+
acl_t acl = acl_get_file(path, access ? ACL_TYPE_ACCESS : ACL_TYPE_DEFAULT);
267+
if (acl == NULL)
268+
{
269+
return NULL;
270+
}
271+
272+
char *text = acl_to_any_text(acl, NULL, ',', 0);
273+
if (text == NULL)
274+
{
275+
acl_free(acl);
276+
return NULL;
277+
}
278+
279+
Rlist *lst = RlistFromSplitString(text, ',');
280+
281+
acl_free(text);
282+
acl_free(acl);
283+
return lst;
284+
}
285+
262286
#elif !defined(__MINGW32__) /* !HAVE_LIBACL */
263287

288+
Rlist *GetACLs(ARG_UNUSED const char *path, ARG_UNUSED bool access)
289+
{
290+
/* TODO: Handle Windows ACLs (see ENT-13019) */
291+
errno = ENOTSUP;
292+
return NULL;
293+
}
294+
264295
bool CopyACLs(ARG_UNUSED const char *src, ARG_UNUSED const char *dst, bool *change)
265296
{
266297
if (change != NULL)

libpromises/evalfunction.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@
8282
#include <libgen.h>
8383

8484
#include <ctype.h>
85+
#include <cf3.defs.h>
86+
#include <compiler.h>
87+
#include <rlist.h>
88+
#include <acl_tools.h>
8589

8690
#ifdef HAVE_LIBCURL
8791
#include <curl/curl.h>
@@ -654,6 +658,44 @@ static Rlist *GetHostsFromLastseenDB(Seq *host_data, time_t horizon, HostsSeenFi
654658

655659
/*********************************************************************/
656660

661+
static FnCallResult FnCallGetACLs(
662+
ARG_UNUSED EvalContext *ctx,
663+
ARG_UNUSED const Policy *policy,
664+
const FnCall *fp,
665+
const Rlist *final_args)
666+
{
667+
assert(fp != NULL);
668+
assert(final_args != NULL);
669+
assert(final_args->next != NULL);
670+
671+
const char *path = RlistScalarValue(final_args);
672+
const char *type = RlistScalarValue(final_args->next);
673+
assert(StringEqual(type, "default") || StringEqual(type, "access"));
674+
675+
#ifdef _WIN32
676+
/* TODO: Policy function to read Windows ACLs (ENT-13019) */
677+
Rlist *acls = NULL;
678+
errno = ENOTSUP;
679+
#else
680+
Rlist *acls = GetACLs(path, StringEqual(type, "access"));
681+
#endif /* _WIN32 */
682+
if (acls == NULL)
683+
{
684+
Log((errno != ENOTSUP) ? LOG_LEVEL_ERR : LOG_LEVEL_VERBOSE,
685+
"Function %s failed to get ACLs for '%s': %s",
686+
fp->name, path, GetErrorStr());
687+
688+
if (errno != ENOTSUP)
689+
{
690+
return FnFailure();
691+
} /* else we'll just return an empty list instead */
692+
}
693+
694+
return (FnCallResult) { FNCALL_SUCCESS, { acls, RVAL_TYPE_LIST } };
695+
}
696+
697+
/*********************************************************************/
698+
657699
static FnCallResult FnCallAnd(EvalContext *ctx,
658700
ARG_UNUSED const Policy *policy,
659701
ARG_UNUSED const FnCall *fp,
@@ -9776,6 +9818,13 @@ static const FnCallArg AND_ARGS[] =
97769818
{NULL, CF_DATA_TYPE_NONE, NULL}
97779819
};
97789820

9821+
static const FnCallArg GET_ACLS_ARGS[] =
9822+
{
9823+
{CF_ABSPATHRANGE, CF_DATA_TYPE_STRING, "Path to file or directory"},
9824+
{"default,access", CF_DATA_TYPE_OPTION, "Whether to get default or access ACL"},
9825+
{NULL, CF_DATA_TYPE_NONE, NULL},
9826+
};
9827+
97799828
static const FnCallArg AGO_ARGS[] =
97809829
{
97819830
{"0,1000", CF_DATA_TYPE_INT, "Years"},
@@ -10820,6 +10869,8 @@ const FnCallType CF_FNCALL_TYPES[] =
1082010869
FNCALL_OPTION_NONE, FNCALL_CATEGORY_FILES, SYNTAX_STATUS_NORMAL),
1082110870
FnCallTypeNew("accumulated", CF_DATA_TYPE_INT, ACCUM_ARGS, &FnCallAccumulatedDate, "Convert an accumulated amount of time into a system representation",
1082210871
FNCALL_OPTION_NONE, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL),
10872+
FnCallTypeNew("getacls", CF_DATA_TYPE_STRING_LIST, GET_ACLS_ARGS, &FnCallGetACLs, "Get ACLs of a given file",
10873+
FNCALL_OPTION_NONE, FNCALL_CATEGORY_FILES, SYNTAX_STATUS_NORMAL),
1082310874
FnCallTypeNew("ago", CF_DATA_TYPE_INT, AGO_ARGS, &FnCallAgoDate, "Convert a time relative to now to an integer system representation",
1082410875
FNCALL_OPTION_NONE, FNCALL_CATEGORY_DATA, SYNTAX_STATUS_NORMAL),
1082510876
FnCallTypeNew("and", CF_DATA_TYPE_CONTEXT, AND_ARGS, &FnCallAnd, "Calculate whether all arguments evaluate to true",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
##############################################################################
2+
#
3+
# Test policy function getacls()
4+
#
5+
##############################################################################
6+
7+
body common control
8+
{
9+
inputs => { "../../default.cf.sub" };
10+
bundlesequence => { default("$(this.promise_filename)") };
11+
version => "1.0";
12+
}
13+
14+
##############################################################################
15+
16+
body acl user_root_rwx_acl
17+
{
18+
acl_method => "append";
19+
acl_default => "access";
20+
aces => { "user:root:rwx" };
21+
}
22+
23+
##############################################################################
24+
25+
bundle agent init
26+
{
27+
files:
28+
"$(G.testdir)/."
29+
create => "true",
30+
acl => user_root_rwx_acl,
31+
handle => "Default ACLs is set";
32+
"$(G.testdir)/foo"
33+
create => "true",
34+
depends_on => { "Default ACLs is set" },
35+
comment => "Inherits ACLs from parent directory";
36+
}
37+
38+
##############################################################################
39+
40+
bundle agent test
41+
{
42+
meta:
43+
"description" -> { "CFE-4529" }
44+
string => "Test policy function getacls()";
45+
46+
"test_soft_fail"
47+
string => "windows",
48+
meta => { "ENT-13019" };
49+
50+
vars:
51+
"default_acls"
52+
slist => getacls("$(G.testdir)", "default"),
53+
if => fileexists("$(G.testdir)");
54+
"access_acls"
55+
slist => getacls("$(G.testdir)/foo", "access"),
56+
if => fileexists("$(G.testdir)/foo");
57+
}
58+
59+
##############################################################################
60+
61+
bundle agent check
62+
{
63+
classes:
64+
"acls_not_supported"
65+
expression => eval("$(with) == 0", "class", "infix"),
66+
with => length("test.default_acls"),
67+
comment => "getacls() returns empty list if unsupported";
68+
"default_ok"
69+
expression => some("$(expected)", "test.default_acls");
70+
"access_ok"
71+
expression => some("$(expected)", "test.access_acls");
72+
73+
vars:
74+
"expected"
75+
string => ".*user:root:rwx.*";
76+
77+
reports:
78+
acls_not_supported::
79+
"$(this.promise_filename) Skip/unsupported";
80+
default_ok&access_ok::
81+
"$(this.promise_filename) Pass";
82+
!(default_ok&access_ok)::
83+
"$(this.promise_filename) FAIL";
84+
"Expecting one match of '$(expected)' in default ACLs [$(with)]"
85+
with => join(", ", "test.default_acls");
86+
"Expecting one match of '$(expected)' in access ACLs [$(with)]"
87+
with => join(", ", "test.access_acls");
88+
}
89+
90+
##############################################################################

0 commit comments

Comments
 (0)