aboutsummaryrefslogtreecommitdiff
path: root/security/privileges.go
blob: 24cd345664ae809a9e53ec975abc34119337022c (plain)
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
/*
 * 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.

/*
#include <sys/types.h>
#include <unistd.h>	// setreuid, setregid
#include <grp.h>	// setgroups

static int my_setreuid(uid_t ruid, uid_t euid)
{
	return setreuid(ruid, euid);
}

static int my_setregid(gid_t rgid, gid_t egid)
{
	return setregid(rgid, egid);
}

static int my_setgroups(size_t size, const gid_t *list)
{
	return setgroups(size, list);
}
*/
import "C"

import (
	"log"
	"os"
	"os/user"
	"syscall"

	"github.com/pkg/errors"

	"github.com/google/fscrypt/util"
)

// SetProcessPrivileges temporarily drops the privileges of the current process
// to have the effective uid/gid of the target user. The privileges can be
// changed again with another call to SetProcessPrivileges.
func SetProcessPrivileges(target *user.User) error {
	euid := util.AtoiOrPanic(target.Uid)
	egid := util.AtoiOrPanic(target.Gid)
	if os.Geteuid() == euid {
		log.Printf("Privileges already set to %q", target.Username)
		return nil
	}
	log.Printf("Setting privileges to %q", target.Username)

	// 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(-1, euid); err != nil {
			return err
		}
	}
	if err := setGids(-1, egid); err != nil {
		return err
	}
	if err := setGroups(target); err != nil {
		return err
	}
	// If not setting privs to root, we want to avoid dropping the uid
	// util the very end.
	if euid != 0 {
		if err := setUids(-1, euid); err != nil {
			return err
		}
	}
	return nil
}

func setUids(ruid, euid int) error {
	res, err := C.my_setreuid(C.uid_t(ruid), C.uid_t(euid))
	log.Printf("setreuid(%d, %d) = %d (errno %v)", ruid, euid, res, err)
	if res == 0 {
		return nil
	}
	return errors.Wrapf(err.(syscall.Errno), "setting uids")
}

func setGids(rgid, egid int) error {
	res, err := C.my_setregid(C.gid_t(rgid), C.gid_t(egid))
	log.Printf("setregid(%d, %d) = %d (errno %v)", rgid, egid, res, err)
	if res == 0 {
		return nil
	}
	return errors.Wrapf(err.(syscall.Errno), "setting gids")
}

func setGroups(target *user.User) error {
	groupStrings, err := target.GroupIds()
	if err != nil {
		return util.SystemError(err.Error())
	}
	gids := make([]C.gid_t, len(groupStrings))
	for i, groupString := range groupStrings {
		gids[i] = C.gid_t(util.AtoiOrPanic(groupString))
	}
	res, err := C.my_setgroups(C.size_t(len(groupStrings)), &gids[0])
	log.Printf("setgroups(%v) = %d (errno %v)", gids, res, err)
	if res == 0 {
		return nil
	}
	return errors.Wrapf(err.(syscall.Errno), "setting groups")
}