mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-24 07:10:20 +00:00
157 lines
5.1 KiB
Go
157 lines
5.1 KiB
Go
|
/*
|
||
|
* privileges.go - Functions for managing users and privileges.
|
||
|
*
|
||
|
* 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 manages:
|
||
|
// - Cache clearing (cache.go)
|
||
|
// - Privilege manipulation (privileges.go)
|
||
|
package security
|
||
|
|
||
|
// Use the libc versions of setreuid, setregid, and setgroups instead of the
|
||
|
// "sys/unix" versions. The "sys/unix" versions use the raw syscalls which
|
||
|
// operate on the calling thread only, whereas the libc versions operate on the
|
||
|
// whole process. And we need to operate on the whole process, firstly for
|
||
|
// pam_fscrypt to prevent the privileges of Go worker threads from diverging
|
||
|
// from the PAM stack's "main" thread, violating libc's assumption and causing
|
||
|
// an abort() later in the PAM stack; and secondly because Go code may migrate
|
||
|
// between OS-level threads while it's running.
|
||
|
//
|
||
|
// See also: https://github.com/golang/go/issues/1435
|
||
|
|
||
|
/*
|
||
|
#define _GNU_SOURCE // for getresuid and setresuid
|
||
|
#include <sys/types.h>
|
||
|
#include <unistd.h> // getting and setting uids and gids
|
||
|
#include <grp.h> // setgroups
|
||
|
*/
|
||
|
import "C"
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"os/user"
|
||
|
"syscall"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
|
||
|
"github.com/google/fscrypt/util"
|
||
|
)
|
||
|
|
||
|
// Privileges encapsulate 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()
|
||
|
|
||
|
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]
|
||
|
}
|
||
|
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 default 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)),
|
||
|
}
|
||
|
userGroups, err := user.GroupIds()
|
||
|
if err != nil {
|
||
|
return nil, util.SystemError(err.Error())
|
||
|
}
|
||
|
privs.groups = make([]C.gid_t, len(userGroups))
|
||
|
for i, group := range userGroups {
|
||
|
privs.groups[i] = C.gid_t(util.AtoiOrPanic(group))
|
||
|
}
|
||
|
return privs, 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)
|
||
|
|
||
|
// Separately 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]
|
||
|
}
|
||
|
|
||
|
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")
|
||
|
}
|
||
|
ProcessPrivileges()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetUids sets the process's real, effective, and saved UIDs.
|
||
|
func SetUids(ruid, euid, suid int) error {
|
||
|
log.Printf("Setting ruid=%d euid=%d suid=%d", ruid, euid, suid)
|
||
|
// We elevate 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")
|
||
|
}
|
||
|
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 nil
|
||
|
}
|
||
|
|
||
|
// GetUids gets the process's real, effective, and saved UIDs.
|
||
|
func GetUids() (int, int, int) {
|
||
|
var ruid, euid, suid C.uid_t
|
||
|
C.getresuid(&ruid, &euid, &suid)
|
||
|
return int(ruid), int(euid), int(suid)
|
||
|
}
|