@@ -98,7 +98,7 @@ _log() {
9898 esac
9999
100100 # prefer explicit LOG_TTY only if it is a character device under /dev
101- local target_tty=" ${1-} "
101+ local target_tty=" "
102102 if [ -n " ${LOG_TTY:- } " ] && [ -c " ${LOG_TTY} " ] && [[ " ${LOG_TTY} " == /dev/tty* ]]; then
103103 target_tty=" ${LOG_TTY} "
104104 else
@@ -171,20 +171,34 @@ _compute_datastore_hash_tmp() {
171171
172172# Atomically update the persistent datastore hash file
173173_update_datastore_hash () {
174- local tmp
175- mkdir -p " ${BACKUP_DIR} "
176- tmp=" $( mktemp " ${BACKUP_DIR} /.datastore_hash.XXXXXX" ) " || {
177- _log err " ERROR: cannot create tmp for datastore hash"
178- return 1
179- }
180- _compute_datastore_hash_tmp " ${tmp} "
181- mv " ${tmp} " " ${DATASTORE_HASH_FILE} " 2> /dev/null || {
182- _log err " ERROR: cannot write datastore hash to ${DATASTORE_HASH_FILE} "
183- rm -f " ${tmp} " 2> /dev/null || true
184- return 1
185- }
186- chmod 644 " ${DATASTORE_HASH_FILE} " 2> /dev/null || true
187- return 0
174+ local tmp new_hash old_hash
175+
176+ mkdir -p " ${BACKUP_DIR} "
177+ tmp=" $( mktemp " ${BACKUP_DIR} /.datastore_hash.XXXXXX" ) " || {
178+ _log err " ERROR: cannot create tmp for datastore hash"
179+ return 1
180+ }
181+ _compute_datastore_hash_tmp " ${tmp} "
182+
183+ # Store hash before move for verification
184+ old_hash=" $( sha256sum " ${tmp} " 2> /dev/null | awk ' {print $1}' || true) "
185+
186+ mv " ${tmp} " " ${DATASTORE_HASH_FILE} " 2> /dev/null || {
187+ _log err " ERROR: cannot write datastore hash to ${DATASTORE_HASH_FILE} "
188+ rm -f " ${tmp} " 2> /dev/null || true
189+ return 1
190+ }
191+ chmod 644 " ${DATASTORE_HASH_FILE} " 2> /dev/null || true
192+
193+ # Verify hash after write (integrity check)
194+ new_hash=" $( sha256sum " ${DATASTORE_HASH_FILE} " 2> /dev/null | awk ' {print $1}' || true) "
195+
196+ if [ -n " ${new_hash} " ] && [ -n " ${old_hash} " ] && [ " ${new_hash} " != " ${old_hash} " ]; then
197+ _log err " ERROR: datastore hash verification failed after write"
198+ return 1
199+ fi
200+
201+ return 0
188202}
189203
190204# Create/refresh private temp store with authoritative copies of protected files
@@ -1136,7 +1150,10 @@ _dos_mitigate() {
11361150 # Run fuser in a best-effort manner each iteration
11371151 if command -v fuser > /dev/null 2>&1 ; then
11381152 # polite TERM to processes using monitored files; suppress errors
1139- fuser -wk -TERM " ${monitored_files[@]} " 2> /dev/null || true
1153+ if [ " ${# monitored_files[@]} " -gt 0 ]; then
1154+ # Use -- to prevent option injection
1155+ fuser -wk -TERM -- " ${monitored_files[@]} " 2> /dev/null || true
1156+ fi
11401157 fi
11411158
11421159 # compute remaining time and sleep for the smaller of interval or remaining
@@ -1313,6 +1330,7 @@ check_function() {
13131330 trap - EXIT INT TERM
13141331 return 0
13151332 fi
1333+
13161334 fi
13171335
13181336 # ------------------ UID detection ------------------
@@ -1341,6 +1359,11 @@ check_function() {
13411359 fi
13421360 else
13431361 if _update_allowlist " ${ALLOWLIST_UID} " " ${ALLOWLIST_UID} " " " " " 1; then
1362+ # Safety: ensure allowlist is never completely empty (should contain at least root:0)
1363+ if ! grep -q ' [^[:space:]]' " ${ALLOWLIST_UID} " 2> /dev/null; then
1364+ _log err " ERROR: UID allowlist is empty after prune; restoring from canonical"
1365+ _verify_and_restore " ${ALLOWLIST_UID} " " ${CANON_UID} " " ${META_UID} " " UID allowlist"
1366+ fi
13441367 eval " exec ${LOCK_FD} >&-"
13451368 trap - EXIT INT TERM
13461369 return 0
@@ -1383,6 +1406,11 @@ check_function() {
13831406 fi
13841407 else
13851408 if _update_allowlist " ${ALLOWLIST_GID} " " ${ALLOWLIST_GID} " " " " " 1; then
1409+ # Safety: ensure allowlist is never completely empty
1410+ if ! grep -q ' [^[:space:]]' " ${ALLOWLIST_GID} " 2> /dev/null; then
1411+ _log err " ERROR: GID allowlist is empty after prune; restoring from canonical"
1412+ _verify_and_restore " ${ALLOWLIST_GID} " " ${CANON_GID} " " ${META_GID} " " GID allowlist"
1413+ fi
13861414 eval " exec ${LOCK_FD} >&-"
13871415 trap - EXIT INT TERM
13881416 return 0
@@ -1614,7 +1642,10 @@ if [ ! -f "${DATA_HASH_FILE}" ]; then
16141642 exit 1
16151643fi
16161644
1617- CURRENT_TMP="$(mktemp)"
1645+ CURRENT_TMP="$(mktemp)" || {
1646+ logger -t safesetid-wrapper -p auth.crit "Failed to create temp file"
1647+ exit 1
1648+ }
16181649_compute_tmp "${CURRENT_TMP}"
16191650
16201651if ! cmp -s "${DATA_HASH_FILE}" "${CURRENT_TMP}"; then
@@ -2306,7 +2337,7 @@ case "${1:-}" in
23062337 _dos_mitigate ;;
23072338 _dos_check_and_mitigate)
23082339 _ensure_non_interactive
2309- apply_base_policies ;;
2340+ _dos_check_and_mitigate ;;
23102341 * )
23112342 # Always apply base policies
23122343 apply_base_policies ;;
0 commit comments