Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion vpn/ipc/conn_nonwindows.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,48 @@ func getConnPeer(conn net.Conn) (p usr, err error) {
}

uidStr := strconv.FormatUint(uint64(uid), 10)
peer, err := getPeerUser(uid, uidStr)
if err != nil {
return p, err
}
return peer, nil
}

func linuxUserInControlGroup(u *user.User) (bool, error) {
group, err := user.LookupGroup(controlGroup)
if err != nil {
return false, fmt.Errorf("lookup %s group: %w", controlGroup, err)
}
Comment on lines +94 to +98
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conn_nonwindows.go is built on Android/iOS (//go:build !windows), but the newly added Linux group-check code depends on controlGroupGID() / controlGroupGIDInt() which are only defined in control_group_nonwindows.go (!android && !ios && !windows). This will cause Android/iOS builds of package ipc to fail with an undefined symbol error. Consider moving the Linux-specific helpers (linuxUserInControlGroup and the runtime.GOOS == "linux" branch) into a //go:build linux file, or widening/providing stub implementations for controlGroupGID* on mobile builds.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above sounds right to me!

gids, err := u.GroupIds()
if err != nil {
return false, fmt.Errorf("lookup groups for %s: %w", u.Username, err)
}
for _, gid := range gids {
if gid == group.Gid {
return true, nil
}
}
return false, nil
}
Comment thread
atavism marked this conversation as resolved.

func getPeerUser(uid uint32, uidStr string) (usr, error) {
u, err := user.LookupId(uidStr)
if err != nil {
return p, fmt.Errorf("lookup user id %v: %w", uid, err)
return usr{}, fmt.Errorf("lookup user id %v: %w", uid, err)
}

if runtime.GOOS == "linux" {
inControlGroup, err := linuxUserInControlGroup(u)
if err != nil {
return usr{}, err
}
return usr{
uid: uidStr,
uname: u.Username,
inControlGroup: inControlGroup,
}, nil
}

return usr{
uid: uidStr,
uname: u.Username,
Expand Down
2 changes: 1 addition & 1 deletion vpn/ipc/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ func authPeer(next http.Handler) http.Handler {
}

func peerCanAccess(peer usr) bool {
return peer.isAdmin
return peer.isAdmin || peer.inControlGroup
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change unintentionally blocks root from accessing the IPC API on Linux: getPeerUser() doesn’t set isAdmin on Linux, and root typically isn’t in the lantern group, so peerCanAccess() will return false even though the socket permissions allow root. Consider explicitly allowing uid 0 (e.g., peer.uid == \"0\") or setting isAdmin for uid 0 in the Linux path.

Suggested change
return peer.isAdmin || peer.inControlGroup
return peer.isAdmin || peer.inControlGroup || peer.uid == "0"

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds right!

}
16 changes: 14 additions & 2 deletions vpn/ipc/socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strconv"
)

const controlGroup = "lantern"

// use a var so it can be overridden in tests
var _socketPath = "/var/run/lantern/lanternd.sock"

Expand All @@ -25,8 +27,18 @@ func socketPath() string {
func setPermissions() error {
path := socketPath()
if runtime.GOOS == "linux" {
// we'll check if user is sudoer to restrict access
return os.Chmod(socketPath(), 0666)
group, err := user.LookupGroup(controlGroup)
if err != nil {
return fmt.Errorf("lookup %s group: %w", controlGroup, err)
}
gid, err := strconv.Atoi(group.Gid)
if err != nil {
return fmt.Errorf("convert %s gid %s: %w", controlGroup, group.Gid, err)
}
if err := os.Chown(path, 0, gid); err != nil {
return fmt.Errorf("chown %s: %w", path, err)
}
return os.Chmod(path, 0660)
Comment thread
atavism marked this conversation as resolved.
Outdated
}

// chown admin group and let the OS restrict access
Expand Down
7 changes: 4 additions & 3 deletions vpn/ipc/usr.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
type usrKey struct{}

type usr struct {
uid string
uname string
isAdmin bool
uid string
uname string
isAdmin bool
inControlGroup bool
}

func contextWithUsr(ctx context.Context, u usr) context.Context {
Expand Down
Loading