Skip to content

Commit 34b768d

Browse files
EnsureFilePermissions compliance check module tests
1 parent 2975436 commit 34b768d

File tree

14 files changed

+582
-47
lines changed

14 files changed

+582
-47
lines changed

.github/workflows/module-test-run.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ jobs:
5050
working-directory: ${{ steps.download.outputs.download-path }}/modules/test
5151
run: |
5252
sudo chmod +x ./moduletest
53+
sudo cat /etc/passwd
54+
sudo cat /etc/sudoers
55+
id
5356
5457
result=0
55-
recipes=$(ls -d ../../src/modules/test/recipes/*.json)
58+
recipes=$(find ../../src/modules/test/recipes/ -name "*.json")
5659
5760
for recipe in $recipes; do
5861
if [ ! -f ../../src/tests/e2e-test-recipes/$(basename $recipe) ]; then
@@ -68,6 +71,9 @@ jobs:
6871
echo failed
6972
result=1
7073
echo "::warning file=$name.log::Error(s) in module-test for '$name'"
74+
cat /etc/passwd
75+
cat /etc/sudoers
76+
id
7177
fi
7278
7379
echo "$output"

src/common/commonutils/FileUtils.c

+3
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,8 @@ int ReplaceMarkedLinesInFile(const char* fileName, const char* marker, const cha
10231023
status = (0 == errno) ? EPERM : errno;
10241024
OsConfigLogInfo(log, "ReplaceMarkedLinesInFile: cannot write to temporary file '%s' (%d)", tempFileName, status);
10251025
}
1026+
} else {
1027+
OsConfigLogInfo(log, "Skipping line: %s", line);
10261028
}
10271029

10281030
memset(line, 0, lineMax + 1);
@@ -1074,6 +1076,7 @@ int ReplaceMarkedLinesInFile(const char* fileName, const char* marker, const cha
10741076

10751077
if (0 == status)
10761078
{
1079+
OsConfigLogInfo(log, "ReplaceMarkedLinesInFile: replacing '%s' with '%s' in '%s'", marker, newline, fileName);
10771080
if (preserveAccess)
10781081
{
10791082
if (0 != (status = RenameFileWithOwnerAndAccess(tempFileName, fileName, log)))

src/common/commonutils/UserUtils.c

+5
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ static int CopyUserEntry(SimplifiedUser* destination, struct passwd* source, OsC
121121

122122
if (0 != status)
123123
{
124+
OsConfigLogError(log, "CopyUserEntry: failed to copy user entry for '%s' (uid: %u, gid: %u)", source->pw_name, source->pw_uid, source->pw_gid);
124125
ResetUserEntry(destination);
125126
}
126127

@@ -358,6 +359,7 @@ int EnumerateUsers(SimplifiedUser** userList, unsigned int* size, char** reason,
358359

359360
if (0 != (*size = GetNumberOfLinesInFile(passwdFile)))
360361
{
362+
OsConfigLogInfo(log, "EnumerateUsers: %u lines in '%s'", *size, passwdFile);
361363
listSize = (*size) * sizeof(SimplifiedUser);
362364
if (NULL != (*userList = malloc(listSize)))
363365
{
@@ -367,6 +369,7 @@ int EnumerateUsers(SimplifiedUser** userList, unsigned int* size, char** reason,
367369

368370
while ((NULL != (userEntry = getpwent())) && (i < *size))
369371
{
372+
OsConfigLogInfo(log, "EnumerateUsers: user %u: '%s' (%u, %u) in '%s'", i, userEntry->pw_name, userEntry->pw_uid, userEntry->pw_gid, passwdFile);
370373
if (0 != (status = CopyUserEntry(&((*userList)[i]), userEntry, log)))
371374
{
372375
OsConfigLogError(log, "EnumerateUsers: failed making copy of user entry (%d)", status);
@@ -833,6 +836,8 @@ int RemoveUser(SimplifiedUser* user, bool removeHome, OsConfigLogHandle log)
833836
return EPERM;
834837
}
835838

839+
OsConfigLogError(log, "RemoveUser: attempting to delete user '%s' (%u, %u) with home directory '%s'",
840+
user->username, user->userId, user->groupId, user->home);
836841
if (NULL != (command = FormatAllocateString(commandTemplate, removeHome ? "-f -r" : "-f", user->username)))
837842
{
838843
if (0 == (status = ExecuteCommand(NULL, command, false, false, 0, 0, NULL, NULL, log)))

src/modules/compliance/src/lib/Base64.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include "Result.h"
77

88
#include <string>
9-
109
namespace compliance
1110
{
1211
static inline char Base64Char(const unsigned char c)

src/modules/compliance/src/lib/ComplianceInterface.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,25 @@ int ComplianceMmiSet(MMI_HANDLE clientSession, const char* componentName, const
142142

143143
try
144144
{
145+
OsConfigLogInfo(engine.log(), "MmiSet(%p, %s, %s, %.*s, %d)", clientSession, componentName, objectName, payloadSizeBytes, payload, payloadSizeBytes);
145146
std::string payloadStr(payload, payloadSizeBytes);
146147
auto object = parseJSON(payloadStr.c_str());
147-
if (NULL == object || JSONString != json_value_get_type(object.get()))
148+
if (NULL == object || (JSONString != json_value_get_type(object.get()) && JSONObject != json_value_get_type(object.get())))
148149
{
149150
OsConfigLogError(engine.log(), "ComplianceMmiSet failed: Failed to parse JSON string");
150151
return EINVAL;
151152
}
152-
std::string realPayload = json_value_get_string(object.get());
153+
std::string realPayload;
154+
if (json_value_get_type(object.get()) == JSONString)
155+
{
156+
realPayload = json_value_get_string(object.get());
157+
}
158+
else if (json_value_get_type(object.get()) == JSONObject)
159+
{
160+
char* tmp = json_serialize_to_string(object.get());
161+
realPayload = tmp;
162+
// json_free_serialized_string(tmp);
163+
}
153164
auto result = engine.mmiSet(objectName, realPayload);
154165
if (!result.has_value())
155166
{

src/modules/compliance/src/lib/Engine.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,13 @@ Optional<Error> Engine::setProcedure(const std::string& ruleName, const std::str
119119
auto ruleJSON = decodeB64JSON(payload);
120120
if (!ruleJSON.has_value())
121121
{
122-
return ruleJSON.error();
122+
// Fall back to plain JSON, both formats are supported
123+
ruleJSON = compliance::parseJSON(payload.c_str());
124+
if (!ruleJSON.has_value())
125+
{
126+
OsConfigLogError(log(), "Failed to parse JSON: %s", ruleJSON.error().message.c_str());
127+
return ruleJSON.error();
128+
}
123129
}
124130

125131
auto object = json_value_get_object(ruleJSON.value().get());

src/modules/compliance/src/lib/Evaluator.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ Result<Status> Evaluator::ExecuteRemediation()
4545
return result.error();
4646
}
4747

48+
OsConfigLogInfo(mLog, "Remediation result: %s", result.value() == Status::Compliant ? "Compliant" : "Non-compliant");
49+
OsConfigLogInfo(mLog, "Remediation log: %s", mLogstream.str().c_str());
4850
return result;
4951
}
5052

src/modules/compliance/src/lib/procedures/ensureFilePermissions.cpp

+53-38
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace compliance
1313
{
14+
namespace
15+
{
16+
const mode_t supportedMask = 0x1FF;
17+
} // anonymous namespace
1418

1519
AUDIT_FN(ensureFilePermissions)
1620
{
@@ -19,6 +23,11 @@ AUDIT_FN(ensureFilePermissions)
1923
{
2024
return Error("No filename provided");
2125
}
26+
if (args.find("permissions") != args.end() && args.find("mask") != args.end())
27+
{
28+
return Error("Cannot specify both permissions and mask");
29+
}
30+
2231
logstream << "ensureFilePermissions for '" << args["filename"] << "' ";
2332

2433
if (0 != stat(args["filename"].c_str(), &statbuf))
@@ -37,7 +46,7 @@ AUDIT_FN(ensureFilePermissions)
3746
}
3847
if (args["user"] != pwd->pw_name)
3948
{
40-
logstream << "Invalid user - is " << pwd->pw_name << " should be " << args["user"];
49+
logstream << "Invalid user - is '" << pwd->pw_name << "' should be '" << args["user"] << "'";
4150
return false;
4251
}
4352
}
@@ -63,42 +72,42 @@ AUDIT_FN(ensureFilePermissions)
6372
}
6473
if (!groupOk)
6574
{
66-
logstream << "Invalid group - is '" << grp->gr_name << "' should be '" << args["group"] << "' ";
75+
logstream << "Invalid group - is '" << grp->gr_name << "' should be '" << args["group"] << "'";
6776
return false;
6877
}
6978
}
7079

71-
unsigned short perms = 0xFFF;
72-
unsigned short mask = 0xFFF;
73-
bool has_perms_or_mask = false;
80+
const mode_t supportedMask = 0x1FF;
7481
if (args.find("permissions") != args.end())
7582
{
7683
char* endptr;
77-
perms = strtol(args["permissions"].c_str(), &endptr, 8);
78-
if ('\0' != *endptr)
84+
mode_t perms = strtol(args["permissions"].c_str(), &endptr, 8);
85+
if (('\0' != *endptr) || ((perms & supportedMask) != perms))
86+
{
87+
return Error("Invalid permissions parameter");
88+
}
89+
if (perms != (statbuf.st_mode & supportedMask))
7990
{
80-
logstream << "Invalid permissions: " << args["permissions"];
91+
logstream << "Invalid permissions - are " << std::oct << (statbuf.st_mode & supportedMask) << " should be " << std::oct << perms << std::dec;
8192
return false;
8293
}
83-
has_perms_or_mask = true;
8494
}
8595
if (args.find("mask") != args.end())
8696
{
8797
char* endptr;
88-
mask = strtol(args["mask"].c_str(), &endptr, 8);
89-
if ('\0' != *endptr)
98+
mode_t mask = strtol(args["mask"].c_str(), &endptr, 8);
99+
if (('\0' != *endptr) || ((mask & supportedMask) != mask))
90100
{
91-
logstream << "Invalid permissions mask: " << args["mask"];
101+
return Error("Invalid mask parameter");
102+
}
103+
if (((statbuf.st_mode & supportedMask) & mask) != 0)
104+
{
105+
logstream << "Invalid permissions - are " << std::oct << (statbuf.st_mode & supportedMask) << " should be " << std::oct
106+
<< ((statbuf.st_mode & supportedMask) & (~mask)) << " with mask " << std::oct << mask << std::dec;
92107
return false;
93108
}
94-
has_perms_or_mask = true;
95-
}
96-
if (has_perms_or_mask && ((perms & mask) != (statbuf.st_mode & mask)))
97-
{
98-
logstream << "Invalid permissions - are " << std::oct << statbuf.st_mode << " should be " << std::oct << perms << " with mask " << std::oct
99-
<< mask << std::dec;
100-
return false;
101109
}
110+
102111
return true;
103112
}
104113

@@ -109,6 +118,10 @@ REMEDIATE_FN(ensureFilePermissions)
109118
logstream << "ERROR: No filename provided";
110119
return Error("No filename provided");
111120
}
121+
if (args.find("permissions") != args.end() && args.find("mask") != args.end())
122+
{
123+
return Error("Cannot specify both permissions and mask");
124+
}
112125

113126
struct stat statbuf;
114127
if (stat(args["filename"].c_str(), &statbuf) < 0)
@@ -126,7 +139,7 @@ REMEDIATE_FN(ensureFilePermissions)
126139
if (pwd == nullptr)
127140
{
128141
logstream << "ERROR: No user with name " << args["user"];
129-
return Error("No user with name " + args["user"]);
142+
return false;
130143
}
131144
uid = pwd->pw_uid;
132145
owner_changed = true;
@@ -161,7 +174,7 @@ REMEDIATE_FN(ensureFilePermissions)
161174
if (grp == nullptr)
162175
{
163176
logstream << "ERROR: No group with name " << args["group"];
164-
return Error("No group with name " + args["group"]);
177+
return false;
165178
}
166179
gid = grp->gr_gid;
167180
owner_changed = true;
@@ -172,42 +185,44 @@ REMEDIATE_FN(ensureFilePermissions)
172185
if (0 != chown(args["filename"].c_str(), uid, gid))
173186
{
174187
logstream << "ERROR: Chown error " << strerror(errno);
175-
return Error("Chown error");
188+
return Error("Chown error", errno);
176189
}
177190
}
178191

179-
unsigned short perms = 0xFFF;
180-
unsigned short mask = 0xFFF;
181-
bool has_perms_or_mask = false;
182192
if (args.find("permissions") != args.end())
183193
{
184194
char* endptr;
185-
perms = strtol(args["permissions"].c_str(), &endptr, 8);
186-
if ('\0' != *endptr)
195+
const mode_t perms = strtol(args["permissions"].c_str(), &endptr, 8);
196+
if (('\0' != *endptr) || ((perms & supportedMask) != perms))
187197
{
188198
logstream << "ERROR: Invalid permissions: " << args["permissions"];
189199
return Error("Invalid permissions: " + args["permissions"]);
190200
}
191-
has_perms_or_mask = true;
201+
if (perms != (statbuf.st_mode & supportedMask))
202+
{
203+
if (chmod(args["filename"].c_str(), perms) < 0)
204+
{
205+
logstream << "ERROR: Chmod error " << strerror(errno);
206+
return false;
207+
}
208+
}
192209
}
193210
if (args.find("mask") != args.end())
194211
{
195212
char* endptr;
196-
mask = strtol(args["mask"].c_str(), &endptr, 8);
197-
if ('\0' != *endptr)
213+
const mode_t mask = strtol(args["mask"].c_str(), &endptr, 8);
214+
if (('\0' != *endptr) || ((mask & supportedMask) != mask))
198215
{
199216
logstream << "ERROR: Invalid permissions mask: " << args["mask"];
200217
return Error("Invalid permissions mask: " + args["mask"]);
201218
}
202-
has_perms_or_mask = true;
203-
}
204-
unsigned short new_perms = (statbuf.st_mode & ~mask) | (perms & mask);
205-
if (has_perms_or_mask && (new_perms != statbuf.st_mode))
206-
{
207-
if (chmod(args["filename"].c_str(), new_perms) < 0)
219+
if (((statbuf.st_mode & supportedMask) & mask) != 0)
208220
{
209-
logstream << "ERROR: Chmod error";
210-
return Error("Chmod error");
221+
if (chmod(args["filename"].c_str(), statbuf.st_mode & (~mask)) < 0)
222+
{
223+
logstream << "ERROR: Chmod error " << strerror(errno);
224+
return false;
225+
}
211226
}
212227
}
213228

0 commit comments

Comments
 (0)