1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
/*
* 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)
// - Keyring Operations (keyring.go)
// - Privilege manipulation (privileges.go)
// - Maintaining the link between the root and user keyrings.
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
//
// Also we need to wrap the libc functions in our own C functions rather than
// calling them directly because in the glibc headers (but not necessarily in
// the headers for other C libraries that may be used on Linux) they are
// declared to take __uid_t and __gid_t arguments rather than uid_t and gid_t.
// And while these are typedef'ed to the same underlying type, before Go 1.10,
// cgo maps them to different Go types.
/*
#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 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()
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
}
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")
}
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
}
func getUids() (int, int, int) {
var ruid, euid, suid C.uid_t
C.getresuid(&ruid, &euid, &suid)
return int(ruid), int(euid), int(suid)
}
|