Merge pull request #71 from safing/fix/api-auth-2

Fix and improve authentication retrying and error messages
This commit is contained in:
Patrick Pacher 2020-06-04 18:43:56 +02:00 committed by GitHub
commit f7fbf40cf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -18,6 +18,21 @@ import (
"github.com/safing/portmaster/process" "github.com/safing/portmaster/process"
) )
const (
deniedMsgUnidentified = `%wFailed to identify the requesting process.
You can enable the Development Mode to disable API authentication for development purposes.`
deniedMsgSystem = `%wSystem access to the Portmaster API is not permitted.
You can enable the Development Mode to disable API authentication for development purposes.`
deniedMsgUnauthorized = `%wThe requesting process is not authorized to access the Portmaster API.
Checked process paths:
%s
The authorized root path is %s.
You can enable the Development Mode to disable API authentication for development purposes.`
)
var ( var (
dataRoot *utils.DirStructure dataRoot *utils.DirStructure
@ -58,10 +73,15 @@ func apiAuthenticator(s *http.Server, r *http.Request) (err error) {
return fmt.Errorf("failed to get remote IP/Port: %s", err) return fmt.Errorf("failed to get remote IP/Port: %s", err)
} }
ctx, tracer := log.AddTracer(r.Context())
tracer.Tracef("filter: authenticating API request from %s", r.RemoteAddr)
defer tracer.Submit()
// It is very important that this works, retry extensively (every 250ms for 5s) // It is very important that this works, retry extensively (every 250ms for 5s)
var retry bool
for tries := 0; tries < 20; tries++ { for tries := 0; tries < 20; tries++ {
err = authenticateAPIRequest( retry, err = authenticateAPIRequest(
r.Context(), ctx,
&packet.Info{ &packet.Info{
Inbound: false, // outbound as we are looking for the process of the source address Inbound: false, // outbound as we are looking for the process of the source address
Version: packet.IPv4, Version: packet.IPv4,
@ -72,8 +92,8 @@ func apiAuthenticator(s *http.Server, r *http.Request) (err error) {
DstPort: localPort, DstPort: localPort,
}, },
) )
if err != nil { if !retry {
return nil return err
} }
// wait a little // wait a little
@ -83,13 +103,13 @@ func apiAuthenticator(s *http.Server, r *http.Request) (err error) {
return err return err
} }
func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) error { func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bool, err error) {
var procsChecked []string var procsChecked []string
// get process // get process
proc, _, err := process.GetProcessByConnection(ctx, pktInfo) proc, _, err := process.GetProcessByConnection(ctx, pktInfo)
if err != nil { if err != nil {
return fmt.Errorf("failed to get process: %s", err) return true, fmt.Errorf("failed to get process: %s", err)
} }
originalPid := proc.Pid originalPid := proc.Pid
@ -102,43 +122,38 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) error {
default: // normal process default: // normal process
// check if the requesting process is in database root / updates dir // check if the requesting process is in database root / updates dir
if strings.HasPrefix(proc.Path, dataRoot.Path) { if strings.HasPrefix(proc.Path, dataRoot.Path) {
return nil return false, nil
} }
} }
// add checked process to list // add checked process to list
procsChecked = append(procsChecked, proc.Path) procsChecked = append(procsChecked, proc.Path)
if i < 2 { if i < 4 {
// get parent process // get parent process
proc, err = process.GetOrFindProcess(context.Background(), proc.ParentPid) proc, err = process.GetOrFindProcess(ctx, proc.ParentPid)
if err != nil { if err != nil {
return fmt.Errorf("failed to get process: %s", err) return true, fmt.Errorf("failed to get process: %s", err)
} }
} }
} }
switch originalPid { switch originalPid {
case process.UnidentifiedProcessID: case process.UnidentifiedProcessID:
log.Warningf("filter: denying api access: failed to identify process") log.Tracer(ctx).Warningf("filter: denying api access: failed to identify process")
return fmt.Errorf("%wFailed to identify the requesting process. You can enable the Development Mode to disable API authentication for development purposes.", api.ErrAPIAccessDeniedMessage) return true, fmt.Errorf(deniedMsgUnidentified, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user
case process.SystemProcessID: case process.SystemProcessID:
log.Warningf("filter: denying api access: request by system") log.Tracer(ctx).Warningf("filter: denying api access: request by system")
return fmt.Errorf("%wSystem access to the Portmaster API is not permitted. You can enable the Development Mode to disable API authentication for development purposes.", api.ErrAPIAccessDeniedMessage) return false, fmt.Errorf(deniedMsgSystem, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user
default: // default: // normal process
log.Warningf("filter: denying api access to %s - also checked %s (trusted root is %s)", procsChecked[0], strings.Join(procsChecked[1:], " "), dataRoot.Path) log.Tracer(ctx).Warningf("filter: denying api access to %s - also checked %s (trusted root is %s)", procsChecked[0], strings.Join(procsChecked[1:], " "), dataRoot.Path)
return fmt.Errorf( return false, fmt.Errorf( //nolint:stylecheck // message for user
`%wThe requesting process is not authorized to access the Portmaster API. deniedMsgUnauthorized,
Checked process paths:
%s
The authorithed root path is %s.
You can enable the Development Mode to disable API authentication for development purposes.`,
api.ErrAPIAccessDeniedMessage, api.ErrAPIAccessDeniedMessage,
dataRoot.Path,
strings.Join(procsChecked, "\n"), strings.Join(procsChecked, "\n"),
dataRoot.Path,
) )
} }
} }