diff options
| author | Joseph Richey <joerichey@google.com> | 2017-08-31 14:51:55 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-08-31 14:51:55 -0700 |
| commit | b04d7ef31dc2e21f055b1b656efb9511e72db6c6 (patch) | |
| tree | 04daee42d94ddda55d956e72f44bafec0ef6dde1 /security/privileges.go | |
| parent | 5285a8c451ef660f932e9f1823ad7da52ad25b74 (diff) | |
| parent | f1bd511fff8e411687001bd8e76e8a41c9f5ff41 (diff) | |
Merge pull request #52 from google/keyrings
Changes to the keyrings interface, corresponding UI changes, and misc changes
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") } |