-
Notifications
You must be signed in to change notification settings - Fork 291
argument to change uid/gid #927
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
Changes from 4 commits
7cd0f6b
72fbe25
87fa0a8
cb29747
a2f35a3
fb587b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris | ||
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris | ||
|
||
package main | ||
|
||
import "errors" | ||
|
||
func chuser(user string) error { | ||
return errors.New("setting uid/gid is not supported on this platform") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris | ||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
osuser "os/user" | ||
"strconv" | ||
"strings" | ||
"syscall" | ||
) | ||
|
||
func chuser(user string) error { | ||
group := "" | ||
if i := strings.IndexByte(user, ':'); i >= 0 { | ||
user, group = user[:i], user[i+1:] | ||
} | ||
|
||
u := (*osuser.User)(nil) | ||
g := (*osuser.Group)(nil) | ||
|
||
if user != "" { | ||
if _, err := strconv.ParseUint(user, 10, 32); err == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why parse it yourself? You can try a name lookup, fallback to UID on error and return if that fails, too, just like getpwnam(3) behaves:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this PR introduced as replacement for one that only used numeric IDs so I preserved ability to do chown/chgrp without passwords database file being present in system. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nvm we do LookupId after this anyway so it's a moot point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. parsing before potentially IO hitting pw database lookup is probably what i thought back when i wrote this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Do these systems exist? Are they relevant?
The next line uses that function, so this did not go as planned. So you might as well clean it up, imho. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as I've mentioned in following comments, yes, this needs pw database anyways, but i prefer minimizing potentially latency introducing tasks (like opening/closing passwd file) even if they're done at init time, so it's guarded by fast parseuint check. it's a matter of taste, eliminating that goes into category of nitpicking IMO. |
||
u, err = osuser.LookupId(user) | ||
if err != nil { | ||
return fmt.Errorf("failed to lookup user by id %q: %v", user, err) | ||
} | ||
} else { | ||
u, err = osuser.Lookup(user) | ||
if err != nil { | ||
return fmt.Errorf("failed to lookup user by name %q: %v", user, err) | ||
} | ||
} | ||
} | ||
if group != "" { | ||
if _, err := strconv.ParseUint(group, 10, 32); err == nil { | ||
g, err = osuser.LookupGroupId(group) | ||
if err != nil { | ||
return fmt.Errorf("failed to lookup group by id %q: %v", user, err) | ||
} | ||
} else { | ||
g, err = osuser.LookupGroup(group) | ||
if err != nil { | ||
return fmt.Errorf("failed to lookup group by name %q: %v", user, err) | ||
} | ||
} | ||
} | ||
|
||
if g != nil { | ||
gid, _ := strconv.ParseUint(g.Gid, 10, 32) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Questionable for this API to return the ID for a known-to-exist group as string... |
||
var err error | ||
if gid < math.MaxInt { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. os/user returns ID strings that were "validated" with strconv.Atoi() which returns There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. had to dig deep to confirm this but indeed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Either that or a clean(er) Point being: Complexity hides bugs, so code ought to be as simple as possible. To my eye, multiple Lookup*() and Parse*() are not as trivial to reason about as it could be. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. without digging deep into the source of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also uint32 vs int is potentially hell on its own due to int being 64bit on 64bit systems and strconv.Atoi() ending up verifying different on 32bit vs 64bit systems but honestly systems just don't use ints as large as these for UIDs/GIDs anyways so i can't really care abt this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 87fa0a8 apparently was someone's else's change too but I can't argue one way or another abt it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to satisfy faulty Code scanning / CodeQL warnings out of all the things There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This looks related over at #1202: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I did fix these now in #1203. |
||
err = syscall.Setgid(int(gid)) | ||
|
||
} else { | ||
err = errors.New("gid too big") | ||
} | ||
|
||
if err != nil { | ||
return fmt.Errorf("failed to setgid %d: %v", gid, err) | ||
} | ||
} else if u != nil { | ||
gid, _ := strconv.ParseUint(u.Gid, 10, 32) | ||
err := syscall.Setgid(int(uint32(gid))) | ||
|
||
if err != nil { | ||
return fmt.Errorf("failed to setgid %d: %v", gid, err) | ||
} | ||
} | ||
|
||
if u != nil { | ||
uid, _ := strconv.ParseUint(u.Uid, 10, 32) | ||
var err error | ||
if uid < math.MaxInt { | ||
err = syscall.Setuid(int(uid)) | ||
|
||
} else { | ||
err = errors.New("uid too big") | ||
} | ||
|
||
if err != nil { | ||
return fmt.Errorf("failed to setuid %d: %v", uid, err) | ||
} | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ func main() { | |
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet") | ||
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key") | ||
loglevel := flag.String("loglevel", "info", "loglevel to enable") | ||
chuserto := flag.String("user", "", "user (and, optionally, group) to set UID/GID to") | ||
flag.Parse() | ||
|
||
done := make(chan struct{}) | ||
|
@@ -280,6 +281,14 @@ func main() { | |
<-done | ||
}) | ||
|
||
// Change user if requested | ||
if *chuserto != "" { | ||
err = chuser(*chuserto) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main goroutine changing user/group to drop privileges is racing against others creating a tun(4) interface, raw socket for routing and UNIX socket as control interface, i.e. startup can fail now as yggdrasil may drop to an unprivileged user before having completed all privileged tasks.
Rerun this a few times; sometimes the control socket gets created as
|
||
// Block until we are told to shut down. | ||
<-ctx.Done() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just
strings.Split(user, ':')
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IndexByte
is probably speediest asSplit
allocates slice and uses string for splitting.Cut
would probably be more fitting here API niceness wise, but I'm not sure if really worth changing at this point.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code runs once, it is not a hot path.
Sure, either way looks cleaner than manual indexing.