Skip to content

ENT-10961, CFE-1840: Files promise can now modify immutable bit in file system attributes #5752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 38 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d7e8ff3
Added utility functions to override immutable bit
larsewi Mar 28, 2025
4ebc7b2
Added body syntax for controlling file system attributes
larsewi Jan 28, 2025
5243590
Files promise can now modify immutable bit in file system attributes
larsewi Feb 6, 2025
1e74204
Added argument to NOOP OverrideImmutable functions
larsewi Apr 1, 2025
c07955f
Combined OverrideImmutable[Commit|Abort] functions
larsewi Apr 1, 2025
d5ada41
Content attribute can now override immutable bit
larsewi Apr 1, 2025
c9ba31d
Store override_immutable variable in EvalContext
larsewi Apr 3, 2025
0c53319
Added function to override immutable bit and rename file
larsewi Apr 3, 2025
83e0ed8
copy_from attribute can now override immutable bit
larsewi Apr 3, 2025
b69401b
Added function to override immutable bit and delete file
larsewi Apr 3, 2025
f8db2b0
delete attribute can now override immutable bit
larsewi Apr 3, 2025
1e6b326
edit_line and edit_xml attributes can now override immutable bit
larsewi Apr 4, 2025
d1f222a
override_fsattrs.c: refactored code
larsewi May 22, 2025
2cf862f
Touch attribute can now override immutable bit
larsewi May 22, 2025
048e9a6
Made functions for temporarily clear/reset immutable bit public
larsewi May 23, 2025
d5394a0
Transformer attribute can now override immutable bit
larsewi May 23, 2025
81bc75e
OverrideImmutableRename() new file inherits immutable flag of old
larsewi May 26, 2025
2db3cf9
Rename attribute can now override immutable bit
larsewi May 26, 2025
ec88ef0
Perms attribute can now override immutable bit
larsewi May 26, 2025
1823cff
acl attribute can now override immutable bit
larsewi May 26, 2025
dab2d99
evalfunction.c: Removed trailing whitespace
larsewi May 28, 2025
983faa4
Added policy function to get ACLs
larsewi May 28, 2025
4b6c26e
Added acceptance test for getacls()
larsewi Jun 2, 2025
4053710
Added acceptance test to set immutable bit
larsewi Jun 13, 2025
c99718d
Added acceptance test to clear immutable bit
larsewi Jun 13, 2025
5b13fea
Added acceptance test for immutable with content
larsewi Jun 13, 2025
841a313
Added acceptance test for immutable with copy_from
larsewi Jun 13, 2025
f75ea3b
Added acceptance test for immutable with delete
larsewi Jun 13, 2025
8e85661
Added acceptance test for immutable with edit_line
larsewi Jun 13, 2025
9f2b689
Added acceptance test for immutable with edit_xml
larsewi Jun 13, 2025
23b6519
Added acceptance test for immutable with perms
larsewi Jun 13, 2025
6b0b70d
Added acceptance test for immutable with touch
larsewi Jun 13, 2025
587ed51
Added acceptance test for immutable with edit_template
larsewi Jun 13, 2025
4369f2f
Added acceptance test for immutable with acl
larsewi Jun 13, 2025
40a4547
Added acceptance test for immutable with transformer
larsewi Jun 13, 2025
b0ce49b
Added acceptance test for immutable with rename
larsewi Jun 13, 2025
c61fe03
Added context to log messages in OverrideImmutableCommit
larsewi Jun 13, 2025
7dc51ff
Make it more clear what the override argument does
larsewi Jun 13, 2025
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 libpromises/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ libpromises_la_SOURCES = \
modes.c \
monitoring_read.c monitoring_read.h \
ornaments.c ornaments.h \
override_fsattrs.c override_fsattrs.h \
policy.c policy.h \
parser.c parser.h \
parser_helpers.h \
Expand Down
140 changes: 140 additions & 0 deletions libpromises/override_fsattrs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include <override_fsattrs.h>
#include <platform.h>
#include <fsattrs.h>
#include <logging.h>
#include <stdlib.h>
#include <files_copy.h>
#include <cf3.defs.h>
#include <string_lib.h>

bool OverrideImmutableBegin(const char *orig, char *copy, size_t copy_len)
{
srand(time(NULL)); /* Seed random number generator */
int rand_number = rand() % 999999;
assert(rand_number >= 0);

/* Inspired by mkstemp(3) */
int ret = snprintf(copy, copy_len, "%s.%06d" CF_NEW, orig, rand_number);
if (ret < 0 || (size_t) ret >= copy_len)
{
Log(LOG_LEVEL_ERR,
"Failed to generate name for temporary copy of '%s': Filename is too long (%d >= %zu)",
orig,
ret,
copy_len);
return false;
}

/* We'll match the original file permissions on commit */
if (!CopyRegularFileDiskPerms(orig, copy, 0600))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should change the orig file to immutable just before this copy. Not 100% atomic but pretty close so that between this Begin() and the Commit() no one else can "easily" modify the file without also modifying the immutable bit of course.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is a good idea. Let's discuss in digital F2F format :)

{
Log(LOG_LEVEL_ERR,
"Failed to copy file '%s' to temporary file '%s'",
orig,
copy);
return false;
}

return true;
}

bool OverrideImmutableCommit(const char *orig, const char *copy)
{
struct stat sb;
if (lstat(orig, &sb) == -1)
{
Log(LOG_LEVEL_ERR, "Failed to stat file '%s'", orig);
unlink(copy);
return false;
}

if (chmod(copy, sb.st_mode) == -1)
{
Log(LOG_LEVEL_ERR,
"Failed to change mode bits on file '%s' to %04jo: %s",
orig,
(uintmax_t) sb.st_mode,
GetErrorStr());
unlink(copy);
return false;
}

/* If the operations on the file system attributes fails for any reason,
* we can still proceed to try to replace the original file. We will only
* log an actual error in case of an unexpected failure (i.e., when
* FS_ATTRS_FAILURE is returned). Other failures will be logged as verbose
* messages because they can be useful, but are be quite verbose. */

bool is_immutable;
FSAttrsResult res = FSAttrsGetImmutableFlag(orig, &is_immutable);
if (res == FS_ATTRS_SUCCESS)
{
if (is_immutable)
{
res = FSAttrsUpdateImmutableFlag(orig, false);
if (res == FS_ATTRS_SUCCESS)
{
Log(LOG_LEVEL_VERBOSE,
"Temporarily cleared immutable bit for file '%s'",
orig);
}
else
{
Log((res == FS_ATTRS_FAILURE) ? LOG_LEVEL_ERR
: LOG_LEVEL_VERBOSE,
"Failed to temporarily clear immutable bit for file '%s': %s",
orig,
FSAttrsErrorCodeToString(res));
}
}
else
{
Log(LOG_LEVEL_DEBUG,
"The immutable bit is not set on file '%s'",
orig);
}
}
else
{
Log((res == FS_ATTRS_FAILURE) ? LOG_LEVEL_ERR : LOG_LEVEL_VERBOSE,
"Failed to get immutable bit from file '%s': %s",
orig,
FSAttrsErrorCodeToString(res));
}

if (rename(copy, orig) == -1)
{
Log(LOG_LEVEL_ERR,
"Failed to replace original file '%s' with copy '%s'",
orig,
copy);
unlink(copy);
return false;
}

if ((res == FS_ATTRS_SUCCESS) && is_immutable)
{
res = FSAttrsUpdateImmutableFlag(orig, true);
if (res == FS_ATTRS_SUCCESS)
{
Log(LOG_LEVEL_VERBOSE,
"Reset immutable bit after temporarily clearing it from file '%s'",
orig);
}
else
{
Log((res == FS_ATTRS_FAILURE) ? LOG_LEVEL_ERR : LOG_LEVEL_VERBOSE,
"Failed to reset immutable bit after temporarily clearing it from file '%s': %s",
orig,
FSAttrsErrorCodeToString(res));
}
}

return true;
}

bool OverrideImmutableAbort(ARG_UNUSED const char *orig, const char *copy)
{
assert(copy != NULL);
return unlink(copy) == 0;
}
58 changes: 58 additions & 0 deletions libpromises/override_fsattrs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright 2025 Northern.tech AS

This file is part of CFEngine 3 - written and maintained by Northern.tech AS.

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; version 3.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

To the extent this program is licensed as part of the Enterprise
versions of CFEngine, the applicable Commercial Open Source License
(COSL) may apply to this file if you as a licensee so wish it. See
included file COSL.txt.
*/

#ifndef CFENGINE_OVERRIDE_FSATTRS_H
#define CFENGINE_OVERRIDE_FSATTRS_H

#include <stdbool.h>
#include <stddef.h>

/**
* @brief Creates a mutable copy of the original file
* @param orig The original file (may be immutable)
* @param copy Updated to contain the filename of the mutable copy
* @param copy_len The size of the buffer to store the filename of the copy
* @return false in case of failure
*/
bool OverrideImmutableBegin(const char *orig, char *copy, size_t copy_len);

/**
* @brief Temporarily clears the immutable bit of the original file and
* replaces it with the mutated copy
* @param orig The original file (may be immutable)
* @param copy The mutated copy to replace the original
* @return false in case of failure
* @note The immutable bit is reset to it's original state
*/
bool OverrideImmutableCommit(const char *orig, const char *copy);

/**
* @brief Simply unlinks the mutable copy
* @param orig Not used (reserved in for future use)
* @param copy The mutated copy to unlink
* @return false in case of failure (but you probably don't care)
*/
bool OverrideImmutableAbort(const char *orig, const char *copy);

#endif /* CFENGINE_OVERRIDE_FSATTRS_H */