Conversation
| } | ||
|
|
||
| // PAM control flags that can cause authentication bypass when misconfigured. | ||
| // Reference: http://www.linux-pam.org/Linux-PAM-html/sag-configuration-file.html |
There was a problem hiding this comment.
I’m getting a 404 when I click the link.
| type Detector struct{} | ||
|
|
||
| // New returns a new PAM misconfiguration detector. | ||
| func New() detector.Detector { |
There was a problem hiding this comment.
The constructor is never called throughout the project. Every plugin should be registered in a list.go file, see: https://github.com/google/osv-scalibr/blob/main/detector/list/list.go
| authEntries = append(authEntries, *entry) | ||
| } | ||
|
|
||
| if lineIssues := analyzePAMLine(filePath, lineNum, line, isLegacyFormat); len(lineIssues) > 0 { |
There was a problem hiding this comment.
Is there a reason why the entry isn't directly passed to the analyzePAMLine function?
| } | ||
|
|
||
| // analyzePAMLine analyzes a single PAM configuration line for security issues. | ||
| func analyzePAMLine(filePath string, lineNum int, line string, isLegacyFormat bool) []string { |
There was a problem hiding this comment.
If it's possible to directly pass the PAM entry to this function as an argument (ref #1705 (comment)), rename this analyzePAMEntry
| control string // required, sufficient, optional, etc. | ||
| modulePath string // e.g., pam_unix.so, pam_permit.so | ||
| args []string // module arguments | ||
| } |
There was a problem hiding this comment.
Move parsePAMLine right below the pamEntry definition.
| return false | ||
| } | ||
|
|
||
| type controlEffect struct { |
There was a problem hiding this comment.
Please, document what a controlEffect is (providing an example)
| isOptionalOnly bool | ||
| } | ||
|
|
||
| func parseControlEffect(control string) controlEffect { |
There was a problem hiding this comment.
Can the parseControlEffect logic be moved inside the parsePAMLine function?
I would expect the pamEntry struct to be something like:
type pamEntry struct {
moduleType string // auth, account, password, session
control controlEffect // required, sufficient, optional, etc.
modulePath string // e.g., pam_unix.so, pam_permit.so
args []string // module arguments
}This way the parsing and checking step could be completely separated.
|
Please, Add the detector to the docs/supported_inventory_types.md. |
|
Hi @mzfr Thanks for the pull request! I’ve reviewed the code and left a few comments for you to address. |
Add misc/pammisconfig to the Linux detector list and supported plugins doc. Refactor PAM parsing to compute control effects once per entry.
detector/list/list.go
Outdated
| var Misc = concat( | ||
| InitMap{ | ||
| cronjobprivesc.Name: {noCFG(cronjobprivesc.New)}, | ||
| dockersocket.Name: {noCFG(dockersocket.New)}, |
There was a problem hiding this comment.
Directly add the plugins registration here (remove list/misc_linux.go and list/misc_other.go):
pammisconfig.Name: {noCFG(pammisconfig.New)},As a result TestDetectorsFromName/Find_misc_detectors will fail, to fix it just add: ""misc/pammisconfig" into the wantDets.
| return "password authentication" | ||
| } | ||
|
|
||
| func checkOptionalOnlyAuth(filePath string, entries []pamEntry) []string { |
There was a problem hiding this comment.
Please, document the checkOptionalOnlyAuth function
| type pamEntry struct { | ||
| moduleType string // auth, account, password, session | ||
| control string // required, sufficient, optional, etc. | ||
| controlEff controlEffect |
There was a problem hiding this comment.
Is the controlEff field always populated? If not make it a pointer to controlEffect (using *controlEffect as type).
|
Hi @mzfr Thanks for the changes. I left a couple more comments for you to address |
Register misc/pammisconfig in the main misc list, update list tests, and document/nil-guard control effects.
| // and when combined with 'sufficient', those users bypass further auth checks. | ||
| // Reference: https://unix.stackexchange.com/a/767197 | ||
| if isModule(entry.modulePath, "pam_succeed_if.so") { | ||
| control := parsedControlEffect(entry.controlEff) |
There was a problem hiding this comment.
Can we add a nil-check for .controlEff and skip the if corpus if it's missing?
I’m also fine with rolling the type back to controlEff (removing the pointer) as long as the default values are coherent
|
Hi @mzfr Thanks for the change, I left a comment for you to check. I see in the code that you are using the Ideally you could check if |
| isSufficientLike bool | ||
| skipNext int | ||
| isOptionalOnly bool | ||
| } |
There was a problem hiding this comment.
Please add the following helper methods to the controllEffect type:
func (c *controlEffect) ShortCircuits() bool {
if c == nil {
return false
}
return c.isSufficientLike || c.skipNext > 0
}
func (c *controlEffect) IsOptionalOnly() bool {
if c == nil {
return false
}
return c.isOptionalOnly
}Feel free to rename the ShortCircuits as you deem corrrect
| // pam_permit.so ALWAYS returns success, so using it with bypass controls | ||
| // means any user can authenticate without a valid password. | ||
| // Reference: https://serverfault.com/questions/890012/pam-accepting-any-password-for-valid-users | ||
| if hasControlEffect && isModule(entry.modulePath, "pam_permit.so") { |
There was a problem hiding this comment.
Use
if entry.controlEff.ShortCircuits() && isModule(entry.modulePath, "pam_permit.so")
here
| // pam_succeed_if.so can match broad user groups (e.g., uid >= 1000), | ||
| // and when combined with 'sufficient', those users bypass further auth checks. | ||
| // Reference: https://unix.stackexchange.com/a/767197 | ||
| if hasControlEffect && isModule(entry.modulePath, "pam_succeed_if.so") { |
There was a problem hiding this comment.
Use
if entry.controlEff.ShortCircuits() && isModule(entry.modulePath, "pam_succeed_if.so")
here
| return false | ||
| } | ||
|
|
||
| func parsedControlEffect(effect *controlEffect) controlEffect { |
There was a problem hiding this comment.
Please, remove this function
|
|
||
| var hasEffectiveNonOptional bool | ||
| var hasPermitOptional bool | ||
| for _, entry := range entries { |
There was a problem hiding this comment.
Please, adjust to use:
entry.controlEff.IsOptionalOnly()|
Hello @mzfr Thanks for the changes, I left a couple of comments regarding the addition of helper function which should simplify the logic and remove some nesting from the code. Thanks again! |
Use nil-safe controlEffect helpers and remove duplicate parsing logic
alessandro-Doyensec
left a comment
There was a problem hiding this comment.
Hello @mzfr
Thanks for the changes! Looks good to merge now.
Fixes #1264
Also please let me know if I should split either the tests or the main detector logic in multiple files