forked from checkpoint-restore/criu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
security.c
184 lines (149 loc) · 4.14 KB
/
security.c
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "crtools.h"
#include "proc_parse.h"
#include "log.h"
#include "xmalloc.h"
#include "bug.h"
#include "protobuf/creds.pb-c.h"
/*
* UID, GID and groups of user requesting for C/R
*/
static unsigned int cr_uid, cr_gid;
static unsigned int cr_ngroups, *cr_groups;
/*
* Setup what user is requesting for dump (via rpc or using
* suid bit on crtools). Later we would deny to dump/restore
* a task, to which the original user doesn't have the direct
* access to. (Or implement some trickier security policy).
*/
int restrict_uid(unsigned int uid, unsigned int gid)
{
struct passwd *pwd;
unsigned int buf[NGROUPS_MAX];
int nbuf;
pr_info("Restrict C/R with %u:%u uid:gid\n", uid, gid);
cr_uid = uid;
cr_gid = gid;
/* skip obtaining additional groups for root, as they don't matter */
if (cr_uid == 0 && cr_gid == 0)
return 0;
pwd = getpwuid(uid);
if (!pwd) {
pr_perror("Can't get password file entry");
return -1;
}
nbuf = NGROUPS_MAX;
if (getgrouplist(pwd->pw_name, pwd->pw_gid, buf, &nbuf) < 0) {
pr_perror("Can't get group list");
return -1;
}
cr_ngroups = nbuf;
cr_groups = xmalloc(cr_ngroups*sizeof(*cr_groups));
if (!cr_groups)
return -1;
memcpy(cr_groups, buf, cr_ngroups*sizeof(*cr_groups));
return 0;
}
static bool check_uids(unsigned int rid, unsigned int eid, unsigned int sid)
{
if (cr_uid == 0)
return true;
if (cr_uid == rid && cr_uid == eid && cr_uid == sid)
return true;
pr_err("UID mismatch %u != (%u,%u,%u)\n", cr_uid, rid, eid, sid);
return false;
}
static bool contains(unsigned int *crgids, unsigned int crgids_num, unsigned int gid)
{
int i;
for (i = 0; i < crgids_num; ++i) {
if (crgids[i] == gid)
return true;
}
return false;
}
static bool check_gids(unsigned int rid, unsigned int eid, unsigned int sid)
{
if (cr_gid == 0)
return true;
if ((contains(cr_groups, cr_ngroups, rid) || cr_gid == rid) &&
(contains(cr_groups, cr_ngroups, eid) || cr_gid == eid) &&
(contains(cr_groups, cr_ngroups, sid) || cr_gid == sid))
return true;
pr_err("GID mismatch. User is absent in (%u,%u,%u)\n", rid, eid, sid);
return false;
}
/*
* There is no need to check groups on dump, because if uids and gids match
* then groups will match too. Btw, getting groups on dump is problematic.
* We can't parse proc, as it contains only first 32 groups. And we can't use
* getgrouplist, as it reads /etc/group which depends on the namespace.
*
* On restore we're getting groups from imgs and can check if user didn't add
* wrong groups by modifying images.
*/
static bool check_groups(unsigned int *groups, unsigned int ngroups)
{
int i;
if (cr_gid == 0)
return true;
for (i = 0; i < ngroups; ++i) {
if (!contains(cr_groups, cr_ngroups, groups[i])) {
pr_err("GID mismatch. User is absent in %u group\n",
groups[i]);
return false;
}
}
return true;
}
static bool check_caps(u32 *inh, u32 *eff, u32 *prm)
{
int i;
/*
* Impose the most strict requirements for now.
* "Real" root user can use any caps, other users may
* use none. Later we will implement more sophisticated
* security model.
*/
if (cr_uid == 0 && cr_gid == 0)
return true;
for (i = 0; i < CR_CAP_SIZE; i++) {
if (inh[i] != 0 || eff[i] != 0 || prm[i] != 0) {
pr_err("CAPs not allowed for non-root user\n");
return false;
}
}
return true;
}
bool cr_user_is_root()
{
return cr_uid == 0 && cr_gid == 0;
}
bool may_dump(struct proc_status_creds *creds)
{
return check_uids(creds->uids[0], creds->uids[1], creds->uids[2]) &&
check_gids(creds->gids[0], creds->gids[1], creds->gids[2]) &&
check_caps(creds->cap_inh, creds->cap_eff, creds->cap_prm);
}
bool may_restore(CredsEntry *creds)
{
return check_uids(creds->uid, creds->euid, creds->suid) &&
check_gids(creds->gid, creds->egid, creds->sgid) &&
check_groups(creds->groups, creds->n_groups) &&
check_caps(creds->cap_inh, creds->cap_eff, creds->cap_prm);
}
int cr_fchown(int fd)
{
if (cr_user_is_root())
return 0;
if (fchown(fd, cr_uid, cr_gid)) {
pr_perror("Can't chown to (%u,%u)", cr_uid, cr_gid);
return -1;
}
return 0;
}