Skip to content

Commit a1781a9

Browse files
authored
Merge pull request #99 from stacknil/stacknil/privilege-auth-signals
feat(signal): expose privilege auth audit signals
2 parents 419c8ff + a4b6aaa commit a1781a9

3 files changed

Lines changed: 62 additions & 4 deletions

File tree

src/signal.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,28 @@ std::optional<SignalMapping> signal_mapping_for_event(const Event& event, const
6969
false};
7070
}
7171
return std::nullopt;
72+
case EventType::SudoAuthFailure:
73+
return SignalMapping{
74+
AuthSignalKind::SudoAuthFailure,
75+
false,
76+
false,
77+
false};
78+
case EventType::SudoPolicyDenied:
79+
return SignalMapping{
80+
AuthSignalKind::SudoPolicyDenied,
81+
false,
82+
false,
83+
false};
84+
case EventType::SuAuthFailure:
85+
return SignalMapping{
86+
AuthSignalKind::SuAuthFailure,
87+
false,
88+
false,
89+
false};
7290
case EventType::Unknown:
7391
case EventType::SshAcceptedPassword:
7492
case EventType::SshAcceptedPublicKey:
7593
case EventType::SshAcceptedKeyboardInteractive:
76-
case EventType::SudoAuthFailure:
77-
case EventType::SudoPolicyDenied:
78-
case EventType::SuAuthFailure:
7994
default:
8095
return std::nullopt;
8196
}

src/signal.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ enum class AuthSignalKind {
1717
SshMaxAuthTries,
1818
PamAuthFailure,
1919
SudoCommand,
20-
SudoSessionOpened
20+
SudoSessionOpened,
21+
SudoAuthFailure,
22+
SudoPolicyDenied,
23+
SuAuthFailure
2124
};
2225

2326
struct AuthSignalBehavior {

tests/test_detector.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,45 @@ void test_sudo_signals_include_command_and_session_opened() {
570570
"did not expect sudo session-opened to count as terminal auth failure");
571571
}
572572

573+
void test_privilege_auth_failures_are_non_counting_signals() {
574+
const auto events = parse_events(
575+
make_syslog_config(),
576+
"Mar 10 08:21:40 example-host sudo: alice : 1 incorrect password attempt ; TTY=pts/0 ; "
577+
"PWD=/home/user/project ; USER=root ; COMMAND=/usr/bin/id\n"
578+
"Mar 10 08:21:45 example-host sudo: bob : user NOT in sudoers ; TTY=pts/1 ; "
579+
"PWD=/home/user/project ; USER=root ; COMMAND=/usr/bin/id\n"
580+
"Mar 10 08:21:50 example-host su[1243]: FAILED SU (to root) carol on pts/2\n");
581+
expect(events.size() == 3, "expected sudo and su auth failures to parse");
582+
expect(events[0].event_type == loglens::EventType::SudoAuthFailure,
583+
"expected sudo password failure event");
584+
expect(events[1].event_type == loglens::EventType::SudoPolicyDenied,
585+
"expected sudo policy denied event");
586+
expect(events[2].event_type == loglens::EventType::SuAuthFailure,
587+
"expected su auth failure event");
588+
589+
const auto signals = loglens::build_auth_signals(events, loglens::DetectorConfig{}.auth_signal_mappings);
590+
expect(signals.size() == 3, "expected privilege auth failures to be visible as signals");
591+
expect(count_signals(signals, loglens::AuthSignalKind::SudoAuthFailure) == 1,
592+
"expected one sudo auth failure signal");
593+
expect(count_signals(signals, loglens::AuthSignalKind::SudoPolicyDenied) == 1,
594+
"expected one sudo policy denied signal");
595+
expect(count_signals(signals, loglens::AuthSignalKind::SuAuthFailure) == 1,
596+
"expected one su auth failure signal");
597+
598+
for (const auto& signal : signals) {
599+
expect(!signal.counts_as_attempt_evidence,
600+
"did not expect privilege auth signals to count as auth attempt evidence");
601+
expect(!signal.counts_as_terminal_auth_failure,
602+
"did not expect privilege auth signals to count as terminal auth failures");
603+
expect(!signal.counts_as_sudo_burst_evidence,
604+
"did not expect privilege auth signals to count toward sudo burst evidence");
605+
}
606+
607+
const loglens::Detector detector;
608+
const auto findings = detector.analyze(events);
609+
expect(findings.empty(), "expected privilege auth signals to stay out of detector findings by default");
610+
}
611+
573612
void test_sudo_burst_behavior_is_preserved_with_signal_layer() {
574613
const auto events = build_sudo_burst_preservation_events();
575614
const loglens::Detector detector;
@@ -743,6 +782,7 @@ int main() {
743782
test_failed_publickey_contributes_to_bruteforce_by_default();
744783
test_accepted_publickey_success_stays_out_of_failure_signals();
745784
test_sudo_signals_include_command_and_session_opened();
785+
test_privilege_auth_failures_are_non_counting_signals();
746786
test_sudo_burst_behavior_is_preserved_with_signal_layer();
747787
test_unsupported_pam_session_close_remains_telemetry_only();
748788
test_pam_auth_failure_does_not_trigger_bruteforce_by_default();

0 commit comments

Comments
 (0)