diff options
| author | Joseph Richey <joerichey@google.com> | 2018-08-23 11:05:39 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-08-23 11:05:39 -0700 |
| commit | 6ba94e27031f2372073257222b6e551790b1b0c1 (patch) | |
| tree | ab21e54ce239db9897e9cbe0c078539dfe45dec2 /security | |
| parent | d4d88e16b54eaa9ba2a8dcb07ba545b60f4d4208 (diff) | |
| parent | 11b09739cbcb25e6602267efe3d48eb063233f5a (diff) | |
Merge pull request #103 from google/pam
Cleanup privilege dropping/raising in pam_fscrypt
Diffstat (limited to 'security')
| -rw-r--r-- | security/keyring.go | 60 | ||||
| -rw-r--r-- | security/privileges.go | 159 |
2 files changed, 120 insertions, 99 deletions
diff --git a/security/keyring.go b/security/keyring.go index ab65631..c4603bc 100644 --- a/security/keyring.go +++ b/security/keyring.go @@ -22,7 +22,6 @@ package security import ( "fmt" "log" - "os" "os/user" "sync" @@ -138,42 +137,47 @@ func UserKeyringID(target *user.User, checkSession bool) (int, error) { return targetKeyring, nil } -func userKeyringIDLookup(uid int) (int, error) { +func userKeyringIDLookup(uid int) (keyringID int, err error) { cacheLock.Lock() defer cacheLock.Unlock() - if keyringID, ok := keyringIDCache[uid]; ok { - return keyringID, nil - } - - ruid, euid := os.Getuid(), os.Geteuid() - // If all the ids do not agree, we will have to change them - needSetUids := uid != ruid || uid != euid - - // The value of KEY_SPEC_USER_KEYRING is determined by the real uid, so - // we must set the ruid appropriately. Note that this will also trigger - // the creation of the uid keyring if it does not yet exist. - if needSetUids { - defer setUids(ruid, euid) // Always reset privileges - if err := setUids(uid, 0); err != nil { - return 0, err + var ok bool + if keyringID, ok = keyringIDCache[uid]; ok { + return + } + + // Our goals here are to: + // - Find the user keyring (for the provided uid) + // - Link it into the current process keyring (so we can use it) + // - Make no permenant changes to the process privileges + // Complicating this are the facts that: + // - The value of KEY_SPEC_USER_KEYRING is determined by the ruid + // - Keyring linking permissions use the euid + // So we have to change both the ruid and euid to make this work, + // setting the suid to 0 so that we can later switch back. + ruid, euid, suid := getUids() + if ruid != uid || euid != uid { + if err = setUids(uid, uid, 0); err != nil { + return } + defer func() { + resetErr := setUids(ruid, euid, suid) + if resetErr != nil { + err = resetErr + } + }() } - keyringID, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_USER_KEYRING, true) + + // We get the value of KEY_SPEC_USER_KEYRING. Note that this will also + // trigger the creation of the uid keyring if it does not yet exist. + keyringID, err = unix.KeyctlGetKeyringID(unix.KEY_SPEC_USER_KEYRING, true) log.Printf("keyringID(_uid.%d) = %d, %v", uid, keyringID, err) if err != nil { return 0, err } - // We still want to use this key after our privileges are reset. If we - // link the key into the process keyring, we will possess it and still - // be able to use it. However, the permissions to link are based on the - // effective uid, so we must set the euid appropriately. - if needSetUids { - if err := setUids(0, uid); err != nil { - return 0, err - } - } - if err := keyringLink(keyringID, unix.KEY_SPEC_PROCESS_KEYRING); err != nil { + // We still want to use this keyring after our privileges are reset. So + // we link it into the process keyring, preventing a loss of access. + if err = keyringLink(keyringID, unix.KEY_SPEC_PROCESS_KEYRING); err != nil { return 0, err } diff --git a/security/privileges.go b/security/privileges.go index 24cd345..c9bfde7 100644 --- a/security/privileges.go +++ b/security/privileges.go @@ -43,30 +43,15 @@ package security // cgo maps them to different Go types. /* +#define _GNU_SOURCE // for getresuid and setresuid #include <sys/types.h> -#include <unistd.h> // setreuid, setregid -#include <grp.h> // setgroups - -static int my_setreuid(uid_t ruid, uid_t euid) -{ - return setreuid(ruid, euid); -} - -static int my_setregid(gid_t rgid, gid_t egid) -{ - return setregid(rgid, egid); -} - -static int my_setgroups(size_t size, const gid_t *list) -{ - return setgroups(size, list); -} +#include <unistd.h> // getting and setting uids and gids +#include <grp.h> // setgroups */ import "C" import ( "log" - "os" "os/user" "syscall" @@ -75,72 +60,104 @@ import ( "github.com/google/fscrypt/util" ) -// SetProcessPrivileges temporarily drops the privileges of the current process -// to have the effective uid/gid of the target user. The privileges can be -// changed again with another call to SetProcessPrivileges. -func SetProcessPrivileges(target *user.User) error { - euid := util.AtoiOrPanic(target.Uid) - egid := util.AtoiOrPanic(target.Gid) - if os.Geteuid() == euid { - log.Printf("Privileges already set to %q", target.Username) - return nil - } - log.Printf("Setting privileges to %q", target.Username) +// Privileges encapulate the effective uid/gid and groups of a process. +type Privileges struct { + euid C.uid_t + egid C.gid_t + groups []C.gid_t +} + +// ProcessPrivileges returns the process's current effective privileges. +func ProcessPrivileges() (*Privileges, error) { + ruid := C.getuid() + euid := C.geteuid() + rgid := C.getgid() + egid := C.getegid() - // If setting privs to root, we want to set the uid first, so we will - // then have the necessary permissions to perform the other actions. - if euid == 0 { - if err := setUids(-1, euid); err != nil { - return err + var groups []C.gid_t + n, err := C.getgroups(0, nil) + if n < 0 { + return nil, err + } + // If n == 0, the user isn't in any groups, so groups == nil is fine. + if n > 0 { + groups = make([]C.gid_t, n) + n, err = C.getgroups(n, &groups[0]) + if n < 0 { + return nil, err } + groups = groups[:n] } - if err := setGids(-1, egid); err != nil { - return err + log.Printf("Current privs (real, effective): uid=(%d,%d) gid=(%d,%d) groups=%v", + ruid, euid, rgid, egid, groups) + return &Privileges{euid, egid, groups}, nil +} + +// UserPrivileges returns the defualt privileges for the specified user. +func UserPrivileges(user *user.User) (*Privileges, error) { + privs := &Privileges{ + euid: C.uid_t(util.AtoiOrPanic(user.Uid)), + egid: C.gid_t(util.AtoiOrPanic(user.Gid)), } - if err := setGroups(target); err != nil { - return err + userGroups, err := user.GroupIds() + if err != nil { + return nil, util.SystemError(err.Error()) } - // If not setting privs to root, we want to avoid dropping the uid - // util the very end. - if euid != 0 { - if err := setUids(-1, euid); err != nil { - return err - } + privs.groups = make([]C.gid_t, len(userGroups)) + for i, group := range userGroups { + privs.groups[i] = C.gid_t(util.AtoiOrPanic(group)) } - return nil + return privs, nil } -func setUids(ruid, euid int) error { - res, err := C.my_setreuid(C.uid_t(ruid), C.uid_t(euid)) - log.Printf("setreuid(%d, %d) = %d (errno %v)", ruid, euid, res, err) - if res == 0 { - return nil +// SetProcessPrivileges sets the privileges of the current process to have those +// specified by privs. The original privileges can be obtained by first saving +// the output of ProcessPrivileges, calling SetProcessPrivileges with the +// desired privs, then calling SetProcessPrivileges with the saved privs. +func SetProcessPrivileges(privs *Privileges) error { + log.Printf("Setting euid=%d egid=%d groups=%v", privs.euid, privs.egid, privs.groups) + + // If setting privs as root, we need to set the euid to 0 first, so that + // we will have the necessary permissions to make the other changes to + // the groups/egid/euid, regardless of our original euid. + C.seteuid(0) + + // Seperately handle the case where the user is in no groups. + numGroups := C.size_t(len(privs.groups)) + groupsPtr := (*C.gid_t)(nil) + if numGroups > 0 { + groupsPtr = &privs.groups[0] } - return errors.Wrapf(err.(syscall.Errno), "setting uids") -} -func setGids(rgid, egid int) error { - res, err := C.my_setregid(C.gid_t(rgid), C.gid_t(egid)) - log.Printf("setregid(%d, %d) = %d (errno %v)", rgid, egid, res, err) - if res == 0 { - return nil + if res, err := C.setgroups(numGroups, groupsPtr); res < 0 { + return errors.Wrapf(err.(syscall.Errno), "setting groups") + } + if res, err := C.setegid(privs.egid); res < 0 { + return errors.Wrapf(err.(syscall.Errno), "setting egid") + } + if res, err := C.seteuid(privs.euid); res < 0 { + return errors.Wrapf(err.(syscall.Errno), "setting euid") } - return errors.Wrapf(err.(syscall.Errno), "setting gids") + ProcessPrivileges() + return nil } -func setGroups(target *user.User) error { - groupStrings, err := target.GroupIds() - if err != nil { - return util.SystemError(err.Error()) - } - gids := make([]C.gid_t, len(groupStrings)) - for i, groupString := range groupStrings { - gids[i] = C.gid_t(util.AtoiOrPanic(groupString)) +func setUids(ruid, euid, suid int) error { + log.Printf("Setting ruid=%d euid=%d suid=%d", ruid, euid, suid) + // We elevate the all the privs before setting them. This prevents + // issues with (ruid=1000,euid=1000,suid=0), where just a single call + // to setresuid might fail with permission denied. + if res, err := C.setresuid(0, 0, 0); res < 0 { + return errors.Wrapf(err.(syscall.Errno), "setting uids") } - res, err := C.my_setgroups(C.size_t(len(groupStrings)), &gids[0]) - log.Printf("setgroups(%v) = %d (errno %v)", gids, res, err) - if res == 0 { - return nil + if res, err := C.setresuid(C.uid_t(ruid), C.uid_t(euid), C.uid_t(suid)); res < 0 { + return errors.Wrapf(err.(syscall.Errno), "setting uids") } - return errors.Wrapf(err.(syscall.Errno), "setting groups") + return nil +} + +func getUids() (int, int, int) { + var ruid, euid, suid C.uid_t + C.getresuid(&ruid, &euid, &suid) + return int(ruid), int(euid), int(suid) } |