diff options
| author | Joe Richey <joerichey@google.com> | 2017-08-30 17:51:05 -0700 |
|---|---|---|
| committer | Joe Richey <joerichey@google.com> | 2017-08-30 17:51:05 -0700 |
| commit | 7888645ab68ed0510ff66121f35630b11976a09f (patch) | |
| tree | e125fbb48b11744ea7c2b89ed2b65ebddb844866 /security/privileges.go | |
| parent | e37a80cd9b601adc16894d3b6fb526ae8f4c846b (diff) | |
security: Rewrite of keryings and permissions
The keyring lookup functions no longer read from /proc/keys. Now they
simply spawn a thread, drop privs, and check with GetKeyringID and
KEY_SPEC_USER_KEYRING. See userKeyringID() for more info.
The privileges functions have also been changed. Now the concept of
setting privileges is seperate form the concept of setting up the
keyrings.
Diffstat (limited to 'security/privileges.go')
| -rw-r--r-- | security/privileges.go | 140 |
1 files changed, 57 insertions, 83 deletions
diff --git a/security/privileges.go b/security/privileges.go index aff41a7..2a1bdae 100644 --- a/security/privileges.go +++ b/security/privileges.go @@ -1,5 +1,5 @@ /* - * privileges.go - Handles raising and dropping user privileges. + * privileges.go - Functions for managing users and privileges. * * Copyright 2017 Google Inc. * Author: Joe Richey (joerichey@google.com) @@ -26,6 +26,7 @@ package security import ( "log" + "os/user" "github.com/pkg/errors" "golang.org/x/sys/unix" @@ -33,101 +34,74 @@ import ( "github.com/google/fscrypt/util" ) -// Package security error values -var ( - ErrReadingKeyList = util.SystemError("could not read keys from " + keyListFilename) - ErrFindingKeyring = util.SystemError("could not find user keyring") - ErrKeyringInsert = util.SystemError("could not insert key into the keyring") - ErrKeyringSearch = errors.New("could not find key with descriptor") - ErrKeyringDelete = util.SystemError("could not delete key from the keyring") - ErrKeyringLink = util.SystemError("could not link keyring") - ErrKeyringUnlink = util.SystemError("could not unlink keyring") -) - -// Privileges contains the state needed to restore a user's original privileges. -type Privileges struct { - euid int - egid int - groups []int -} - -// DropThreadPrivileges temporarily drops the privileges of the current thread -// to have the euid and egid specified. The returned opaque Privileges structure -// should later be passed to RestoreThreadPrivileges. -// Due to golang/go#1435, these privileges are only dropped for a single thread. -// This function also makes sure that the appropriate user keyrings are linked. -// This ensures that the user's keys are visible from commands like sudo. -func DropThreadPrivileges(euid int, egid int) (privs *Privileges, err error) { - privs = &Privileges{ - euid: unix.Geteuid(), - egid: unix.Getegid(), - } - if privs.groups, err = unix.Getgroups(); err != nil { - return nil, errors.Wrapf(err, "getting groups") +// SetThreadPrivileges drops drops the privileges of the current thread to have +// the uid/gid of the target user. If permanent is true, this operation cannot +// be reversed in the thread (the real and effective IDs are set). If +// permanent is false, only the effective IDs are set, allowing the privileges +// to be changed again with another call to SetThreadPrivileges. +func SetThreadPrivileges(target *user.User, permanent bool) error { + euid := util.AtoiOrPanic(target.Uid) + egid := util.AtoiOrPanic(target.Gid) + var ruid, rgid int + if permanent { + log.Printf("Permanently dropping to user %q", target.Username) + ruid, rgid = euid, egid + } else { + log.Printf("Temporarily dropping to user %q", target.Username) + // Real IDs of -1 mean they will not be changed. + ruid, rgid = -1, -1 } - // We link the privileged keyring into the thread keyring so that we - // can still modify it after dropping privileges. - privilegedUserKeyringID, err := getUserKeyringID() - if err != nil { - return - } - if err = keyringLink(privilegedUserKeyringID, unix.KEY_SPEC_THREAD_KEYRING); err != nil { - return + // 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(ruid, euid); err != nil { + return err + } } - defer keyringUnlink(privilegedUserKeyringID, unix.KEY_SPEC_THREAD_KEYRING) - // Drop euid last so we have permissions to drop the others. - if err = unix.Setregid(-1, egid); err != nil { - err = errors.Wrapf(err, "dropping egid to %d", egid) - return - } - if err = unix.Setgroups([]int{egid}); err != nil { - err = errors.Wrapf(err, "dropping groups") - return + if err := setGids(rgid, egid); err != nil { + return err } - if err = unix.Setreuid(-1, euid); err != nil { - err = errors.Wrapf(err, "dropping euid to %d", euid) - return + + if err := setGroups(target); err != nil { + return err } - log.Printf("privileges dropped to euid=%d, egid=%d", euid, egid) - // Failure should undo the privilege modification - defer func() { - if err != nil { - raiseErr := RaiseThreadPrivileges(privs) - log.Printf("when reverting privilege changes: %v", raiseErr) + // If not setting privs to root, we want to avoid dropping the uid + // util the very end. + if euid != 0 { + if err := setUids(ruid, euid); err != nil { + return err } - }() - - // If the link already exists, this linking does nothing and succeeds. - droppedUserKeyringID, err := getUserKeyringID() - if err != nil { - return - } - if err = keyringLink(droppedUserKeyringID, privilegedUserKeyringID); err != nil { - return } - log.Printf("user keyring (%d) linked into root user keyring (%d)", - droppedUserKeyringID, privilegedUserKeyringID) + return nil +} - return privs, nil +func setUids(ruid, euid int) error { + err := unix.Setreuid(ruid, euid) + log.Printf("Setreuid(%d, %d) = %v", ruid, euid, err) + return errors.Wrapf(err, "setting uids") } -// RaiseThreadPrivileges returns the state of a threads privileges to what it -// was before the call to DropThreadPrivileges. -func RaiseThreadPrivileges(privs *Privileges) error { - // Raise euid last so we have permissions to raise the others. - if err := unix.Setreuid(-1, privs.euid); err != nil { - return errors.Wrapf(err, "raising euid to %d", privs.euid) - } - if err := unix.Setregid(-1, privs.egid); err != nil { - return errors.Wrapf(err, "raising egid to %d", privs.egid) +func setGids(rgid, egid int) error { + err := unix.Setregid(rgid, egid) + log.Printf("Setregid(%d, %d) = %v", rgid, egid, err) + return errors.Wrapf(err, "setting gids") +} + +func setGroups(target *user.User) error { + groupStrings, err := target.GroupIds() + if err != nil { + return util.SystemError(err.Error()) } - if err := unix.Setgroups(privs.groups); err != nil { - return errors.Wrapf(err, "raising groups") + + gids := make([]int, len(groupStrings)) + for i, groupString := range groupStrings { + gids[i] = util.AtoiOrPanic(groupString) } - log.Printf("privileges raised to euid=%d, egid=%d", privs.euid, privs.egid) - return nil + err = unix.Setgroups(gids) + log.Printf("Setgroups(%v) = %v", gids, err) + return errors.Wrapf(err, "setting groups") } |