Skip to content
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

Hardening: Fall-back double-checkprocname() vs. current daemon program name (and add NUT_IGNORE_CHECKPROCNAME=true toggle) #2471

Merged
merged 3 commits into from
Jun 15, 2024

Conversation

jimklimov
Copy link
Member

@jimklimov jimklimov commented Jun 13, 2024

Follow-up to issue #2463 and PR #2464

Earlier work introduced a way to check that a running PID's process name (if we can detect it) matches our expectations (upsmon, upsd, driver name) before we send signals - as we did, blindly and naively.

This PR adds a way to disable this feature if it causes problems (e.g. embedded builds tend to have undecipherable NUT version strings to match a firmware name, and I can imagine someone tweaking program names or using symlinks as well... some snmp-ups-dmf comes to mind).

It also adds a fallback ability to optionally check the other PID's process name against the current PID's process name (not requested from upsdrvctl which manages variously-named driver programs, but is requested from drivers, upsd and upsmon) to hopefully alleviate this problem with tweaked names, nip it in the bud.

CC @yoyoma2

CC @arnaudquette-eaton @ericclappier-eaton : this one more internal API change for sendsignal*() methods may impact or benefit your work too, pinging just in case :)

Screenshot with a tweaked upsmon that would check NULL instead of progname - this example looks for name of its own PID:

:; ./clients/upsmon -DDDDDD -c reload -P 140
Network UPS Tools upsmon 2.8.2-333-g82e5932d9
   0.000000     [D3] getprocname: /proc is an accessible directory, investigating
   0.000152     [D3] getprocname: located symlink for PID 46215 at: /proc/46215/exe
   0.000261     [D1] getprocname: determined process name for PID 46215: /home/jim/nut/clients/.libs/upsmon
   0.000345     [D3] getprocname: /proc is an accessible directory, investigating
   0.000479     [D3] getprocname: located symlink for PID 140 at: /proc/140/exe
   0.000581     [D1] getprocname: failed to readlink() from /proc/140/exe: Permission denied
   0.000645     [D5] getprocname: try to parse some files under /proc
   0.001356     [D3] getprocname: try to parse some files under /proc: processing /proc/140/cmdline
   0.001430     [D1] getprocname: determined process name for PID 140: sshd:
   0.001445     [D1] checkprocname: did not find any match of program names for PID 140 of 'sshd:'=>'sshd:' and checked '/home/jim/nut/clients/.libs/upsmon'=>'upsmon'
   0.001512     [D1] sendsignalpid: ran at least one check, and all such checks found a process name for PID 140 and failed to match: expected progname='(null)' (res=-10), current progname='/home/jim/nut/clients/.libs/upsmon' (res=0)
   0.001524     Tried to signal PID 140 which exists but is not of current program '/home/jim/nut/clients/.libs/upsmon'
   0.001530     [D1] Just failed to send signal, no daemon was running
   0.001534     Failed to signal the currently running daemon (if any)
   0.001538     Try 'systemctl reload nut-monitor.service'

...and with upsmon.c tweaked to just look for a bogus-name, so it falls back to getpid() when the first test fails:

:;  ./clients/upsmon -DDDDDD -c reload -P 140
Network UPS Tools upsmon 2.8.2-333-g82e5932d9
   0.000000     [D3] getprocname: /proc is an accessible directory, investigating
   0.000054     [D3] getprocname: located symlink for PID 140 at: /proc/140/exe
   0.000094     [D1] getprocname: failed to readlink() from /proc/140/exe: Permission denied
   0.000123     [D5] getprocname: try to parse some files under /proc
   0.000175     [D3] getprocname: try to parse some files under /proc: processing /proc/140/cmdline
   0.000204     [D1] getprocname: determined process name for PID 140: sshd:
   0.000211     [D1] checkprocname: did not find any match of program names for PID 140 of 'sshd:'=>'sshd:' and checked 'bogus-name'=>'bogus-name'
   0.000245     [D3] getprocname: /proc is an accessible directory, investigating
   0.000284     [D3] getprocname: located symlink for PID 50593 at: /proc/50593/exe
   0.000298     [D1] getprocname: determined process name for PID 50593: /home/jim/nut/clients/.libs/upsmon
   0.000341     [D3] getprocname: /proc is an accessible directory, investigating
   0.000376     [D3] getprocname: located symlink for PID 140 at: /proc/140/exe
   0.000388     [D1] getprocname: failed to readlink() from /proc/140/exe: Permission denied
   0.000415     [D5] getprocname: try to parse some files under /proc
   0.000461     [D3] getprocname: try to parse some files under /proc: processing /proc/140/cmdline
   0.000491     [D1] getprocname: determined process name for PID 140: sshd:
   0.000497     [D1] checkprocname: did not find any match of program names for PID 140 of 'sshd:'=>'sshd:' and checked '/home/jim/nut/clients/.libs/upsmon'=>'upsmon'
   0.000500     [D1] sendsignalpid: ran at least one check, and all such checks found a process name for PID 140 and failed to match: expected progname='bogus-name' (res=0), current progname='/home/jim/nut/clients/.libs/upsmon' (res=0)
   0.000525     Tried to signal PID 140 which exists but is not of expected program 'bogus-name' nor current '/home/jim/nut/clients/.libs/upsmon'
   0.000553     [D1] Just failed to send signal, no daemon was running
   0.000581     Failed to signal the currently running daemon (if any)
   0.000587     Try 'systemctl reload nut-monitor.service'

...and as before, by default it uses the built-in progname (upsmon here):

:; ./clients/upsmon -DDDDDD -c reload -P 140
Network UPS Tools upsmon 2.8.2-333-g82e5932d9
   0.000000     [D3] getprocname: /proc is an accessible directory, investigating
   0.000047     [D3] getprocname: located symlink for PID 140 at: /proc/140/exe
   0.000059     [D1] getprocname: failed to readlink() from /proc/140/exe: Permission denied
   0.000063     [D5] getprocname: try to parse some files under /proc
   0.000084     [D3] getprocname: try to parse some files under /proc: processing /proc/140/cmdline
   0.000114     [D1] getprocname: determined process name for PID 140: sshd:
   0.000143     [D1] checkprocname: did not find any match of program names for PID 140 of 'sshd:'=>'sshd:' and checked 'upsmon'=>'upsmon'
   0.000180     [D3] getprocname: /proc is an accessible directory, investigating
   0.000198     [D3] getprocname: located symlink for PID 50840 at: /proc/50840/exe
   0.000235     [D1] getprocname: determined process name for PID 50840: /home/jim/nut/clients/.libs/upsmon
   0.000277     [D3] getprocname: /proc is an accessible directory, investigating
   0.000317     [D3] getprocname: located symlink for PID 140 at: /proc/140/exe
   0.000363     [D1] getprocname: failed to readlink() from /proc/140/exe: Permission denied
   0.000401     [D5] getprocname: try to parse some files under /proc
   0.000473     [D3] getprocname: try to parse some files under /proc: processing /proc/140/cmdline
   0.000506     [D1] getprocname: determined process name for PID 140: sshd:
   0.000520     [D1] checkprocname: did not find any match of program names for PID 140 of 'sshd:'=>'sshd:' and checked '/home/jim/nut/clients/.libs/upsmon'=>'upsmon'
   0.000527     [D1] sendsignalpid: ran at least one check, and all such checks found a process name for PID 140 and failed to match: expected progname='upsmon' (res=0), current progname='/home/jim/nut/clients/.libs/upsmon' (res=0)
   0.000532     Tried to signal PID 140 which exists but is not of expected program 'upsmon' nor current '/home/jim/nut/clients/.libs/upsmon'
   0.000563     [D1] Just failed to send signal, no daemon was running
   0.000571     Failed to signal the currently running daemon (if any)
   0.000573     Try 'systemctl reload nut-monitor.service'

:; echo $?
1

Note the fallback check resolves the name of the other PID again. A small inefficiency on an infrequent code-path, can live with that.

Test of the emergency envvar toggle to disable the PID sanity check (still sending to un-owned sshd so fails):

:; NUT_IGNORE_CHECKPROCNAME=true ./clients/upsmon -DDDDDD -c reload -P 140
Network UPS Tools upsmon 2.8.2-333-g82e5932d9
   0.000000     [D1] checkprocname: skipping because caller set NUT_IGNORE_CHECKPROCNAME
kill: Operation not permitted
   0.000130     [D1] Just failed to send signal, no daemon was running
   0.000180     Failed to signal the currently running daemon (if any)
   0.000235     Try 'systemctl reload nut-monitor.service'

Note that "raw" perror() happens when debug is enabled or internal nut_sendsignal_debug_level is sufficiently high (e.g. toned down where we "just ping"), to not scare casual users. Normally this looks like this:

# With the check
:; ./clients/upsmon  -c reload -P 140
Network UPS Tools upsmon 2.8.2-333-g82e5932d9
Tried to signal PID 140 which exists but is not of expected program 'upsmon' nor current '/home/jim/nut/clients/.libs/upsmon'
Failed to signal the currently running daemon (if any)
Try 'systemctl reload nut-monitor.service'

:; echo $?
1

# Skipped check
:; NUT_IGNORE_CHECKPROCNAME=true ./clients/upsmon  -c reload -P 140
Network UPS Tools upsmon 2.8.2-333-g82e5932d9
Failed to signal the currently running daemon (if any)
Try 'systemctl reload nut-monitor.service'

:; echo $?
1

@jimklimov jimklimov added enhancement service/daemon start/stop General subject for starting and stopping NUT daemons (drivers, server, monitor); also BG/FG/Debug upsmon portability We want NUT to build and run everywhere possible C-str Issues and PRs about C/C++ methods, headers and data types dealing with strings and memory blocks labels Jun 13, 2024
@jimklimov jimklimov added this to the 2.8.3 milestone Jun 13, 2024
@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@jimklimov jimklimov force-pushed the issue-2463 branch 2 times, most recently from e33330d to 3978e6f Compare June 14, 2024 10:30
@jimklimov jimklimov merged commit fd4c549 into networkupstools:master Jun 15, 2024
30 checks passed
@jimklimov jimklimov deleted the issue-2463 branch June 15, 2024 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-str Issues and PRs about C/C++ methods, headers and data types dealing with strings and memory blocks enhancement portability We want NUT to build and run everywhere possible service/daemon start/stop General subject for starting and stopping NUT daemons (drivers, server, monitor); also BG/FG/Debug upsmon
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants