diff options
Diffstat (limited to 'security/keyring.go')
| -rw-r--r-- | security/keyring.go | 214 |
1 files changed, 0 insertions, 214 deletions
diff --git a/security/keyring.go b/security/keyring.go deleted file mode 100644 index 3236775..0000000 --- a/security/keyring.go +++ /dev/null @@ -1,214 +0,0 @@ -/* - * keyring.go - Handles inserting/removing into user keyrings. - * - * Copyright 2017 Google Inc. - * Author: Joe Richey (joerichey@google.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package security - -import ( - "fmt" - "log" - "os/user" - "runtime" - - "github.com/pkg/errors" - "golang.org/x/sys/unix" - - "github.com/google/fscrypt/util" -) - -// KeyType is always logon as required by filesystem encryption. -const KeyType = "logon" - -// Keyring related error values -var ( - ErrKeySearch = errors.New("could not find key with descriptor") - ErrKeyRemove = util.SystemError("could not remove key from the keyring") - ErrKeyInsert = util.SystemError("could not insert key into the keyring") - ErrSessionUserKeying = errors.New("user keyring not linked into session keyring") - ErrAccessUserKeyring = errors.New("could not access user keyring") - ErrLinkUserKeyring = util.SystemError("could not link user keyring into root keyring") -) - -// FindKey tries to locate a key in the kernel keyring with the provided -// description. The key ID is returned if we can find the key. An error is -// returned if the key does not exist. -func FindKey(description string, targetUser *user.User) (int, error) { - runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring - defer runtime.UnlockOSThread() - - keyringID, err := UserKeyringID(targetUser, false) - if err != nil { - return 0, err - } - - keyID, err := unix.KeyctlSearch(keyringID, KeyType, description, 0) - log.Printf("KeyctlSearch(%d, %s, %s) = %d, %v", keyringID, KeyType, description, keyID, err) - if err != nil { - return 0, errors.Wrap(ErrKeySearch, err.Error()) - } - return keyID, err -} - -// RemoveKey tries to remove a policy key from the kernel keyring with the -// provided description. An error is returned if the key does not exist. -func RemoveKey(description string, targetUser *user.User) error { - runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring - defer runtime.UnlockOSThread() - - keyID, err := FindKey(description, targetUser) - if err != nil { - return err - } - - // We use KEYCTL_INVALIDATE instead of KEYCTL_REVOKE because - // invalidating a key immediately removes it. - _, err = unix.KeyctlInt(unix.KEYCTL_INVALIDATE, keyID, 0, 0, 0) - log.Printf("KeyctlInvalidate(%d) = %v", keyID, err) - if err != nil { - return errors.Wrap(ErrKeyRemove, err.Error()) - } - return nil -} - -// InsertKey puts the provided data into the kernel keyring with the provided -// description. -func InsertKey(data []byte, description string, targetUser *user.User) error { - runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring - defer runtime.UnlockOSThread() - - keyringID, err := UserKeyringID(targetUser, true) - if err != nil { - return err - } - - keyID, err := unix.AddKey(KeyType, description, data, keyringID) - log.Printf("KeyctlAddKey(%s, %s, <data>, %d) = %d, %v", - KeyType, description, keyringID, keyID, err) - if err != nil { - return errors.Wrap(ErrKeyInsert, err.Error()) - } - return nil -} - -// UserKeyringID returns the key id of the target user's user keyring. We also -// ensure that the keyring will be accessible by linking it into the thread -// keyring and linking it into the root user keyring (permissions allowing). If -// checkSession is true, an error is returned if a normal user requests their -// user keyring, but it is not in the current session keyring. -func UserKeyringID(targetUser *user.User, checkSession bool) (int, error) { - runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring - defer runtime.UnlockOSThread() - - uid := util.AtoiOrPanic(targetUser.Uid) - targetKeyring, err := userKeyringIDLookup(uid) - if err != nil { - return 0, errors.Wrap(ErrAccessUserKeyring, err.Error()) - } - - if !util.IsUserRoot() { - // Make sure the returned keyring will be accessible by checking - // that it is in the session keyring. - if checkSession && !isUserKeyringInSession(uid) { - return 0, ErrSessionUserKeying - } - return targetKeyring, nil - } - - // Make sure the returned keyring will be accessible by linking it into - // the root user's user keyring (which will not be garbage collected). - rootKeyring, err := userKeyringIDLookup(0) - if err != nil { - return 0, errors.Wrap(ErrLinkUserKeyring, err.Error()) - } - - if rootKeyring != targetKeyring { - if err = keyringLink(targetKeyring, rootKeyring); err != nil { - return 0, errors.Wrap(ErrLinkUserKeyring, err.Error()) - } - } - return targetKeyring, nil -} - -func userKeyringIDLookup(uid int) (keyringID int, err error) { - - // Our goals here are to: - // - Find the user keyring (for the provided uid) - // - Link it into the current thread keyring (so we can use it) - // - Make no permanent 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 - } - }() - } - - // 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 keyring after our privileges are reset. So - // we link it into the thread keyring, preventing a loss of access. - // - // We must be under LockOSThread() for this to work reliably. Note that - // we can't just use the process keyring, since it doesn't work reliably - // in Go programs, due to the Go runtime creating threads before the - // program starts and has a chance to create the process keyring. - if err = keyringLink(keyringID, unix.KEY_SPEC_THREAD_KEYRING); err != nil { - return 0, err - } - - return keyringID, nil -} - -// isUserKeyringInSession tells us if the user's uid keyring is in the current -// session keyring. -func isUserKeyringInSession(uid int) bool { - // We cannot use unix.KEY_SPEC_SESSION_KEYRING directly as that might - // create a session keyring if one does not exist. - sessionKeyring, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, false) - log.Printf("keyringID(session) = %d, %v", sessionKeyring, err) - if err != nil { - return false - } - - description := fmt.Sprintf("_uid.%d", uid) - id, err := unix.KeyctlSearch(sessionKeyring, "keyring", description, 0) - log.Printf("KeyctlSearch(%d, keyring, %s) = %d, %v", sessionKeyring, description, id, err) - return err == nil -} - -func keyringLink(keyID int, keyringID int) error { - _, err := unix.KeyctlInt(unix.KEYCTL_LINK, keyID, keyringID, 0, 0) - log.Printf("KeyctlLink(%d, %d) = %v", keyID, keyringID, err) - return err -} |