aboutsummaryrefslogtreecommitdiff
path: root/security/privileges.go
blob: cdabe713efa785d47d34abf5f3ef25f7b09af1da (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
/*
 * privileges.go - Handles raising and dropping user 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:
//  - Keyring Operations (keyring.go)
//  - Privilege manipulation (privileges.go)
//  - Maintaining the link between the root and user keyrings.
package security

import (
	"log"

	"github.com/pkg/errors"
	"golang.org/x/sys/unix"

	"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")
)

// 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) (*Privileges, error) {
	var 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")
	}

	// 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 nil, err
	}
	if err = keyringLink(privilegedUserKeyringID, unix.KEY_SPEC_THREAD_KEYRING); err != nil {
		return nil, 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 {
		return nil, errors.Wrapf(err, "dropping egid to %d", egid)
	}
	if err = unix.Setgroups([]int{egid}); err != nil {
		return nil, errors.Wrapf(err, "dropping groups")
	}
	if err = unix.Setreuid(-1, euid); err != nil {
		return nil, errors.Wrapf(err, "dropping euid to %d", euid)
	}
	log.Printf("privileges dropped to euid=%d, egid=%d", euid, egid)

	// If the link already exists, this linking does nothing and succeeds.
	droppedUserKeyringID, err := getUserKeyringID()
	if err != nil {
		return nil, err
	}
	if err = keyringLink(droppedUserKeyringID, privilegedUserKeyringID); err != nil {
		return nil, err
	}
	log.Printf("user keyring (%d) linked into root user keyring (%d)",
		droppedUserKeyringID, privilegedUserKeyringID)

	return privs, nil
}

// 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)
	}
	if err := unix.Setgroups(privs.groups); err != nil {
		return errors.Wrapf(err, "raising groups")
	}

	log.Printf("privileges raised to euid=%d, egid=%d", privs.euid, privs.egid)
	return nil
}