/
preloadpatchmanager.c
247 lines (195 loc) · 6.31 KB
/
preloadpatchmanager.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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#define _GNU_SOURCE
//#define NO_INTERCEPT
#define ALLOW_ALL_USERS
#include <dlfcn.h>
#include <stdarg.h>
#include <stdio.h>
typedef int (*orig_open_f_type)(const char *pathname, int flags, ...);
static orig_open_f_type orig_open = NULL;
static orig_open_f_type orig_open64 = NULL;
#ifndef NO_INTERCEPT
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <stdlib.h>
#include <limits.h>
#include <libgen.h>
#include <pwd.h>
#define SERVER_PATH "/tmp/patchmanager-socket"
#define ENV_NO_PRELOAD "NO_PM_PRELOAD"
#define ENV_DEBUG "PM_PRELOAD_DEBUG"
static const char *blacklist_paths_startswith[] = {
"/dev",
"/sys",
"/proc",
"/run",
"/tmp",
};
static const char *blacklist_paths_equal[] = {
"/",
};
static int debug_output() {
static int debug_output_read = 0;
static int debug_output_value = 0;
if (!debug_output_read) {
debug_output_value = getenv(ENV_DEBUG) ? 1 : 0;
debug_output_read = 1;
}
return debug_output_value;
}
static void pm_name(char new_name[]) {
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, SERVER_PATH);
int result = connect(sockfd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
if (result < 0) {
if (debug_output()) {
fprintf(stderr, "[pm_name] error connecting to socket\n");
}
close(sockfd);
return;
}
int sn = write(sockfd, new_name, strlen(new_name));
if (sn <= 0) {
if (debug_output()) {
fprintf(stderr, "[pm_name] error sending to socket\n");
}
close(sockfd);
return;
}
char buf_name[PATH_MAX];
memset(buf_name, 0, sizeof(buf_name));
int rn = read(sockfd, buf_name, sizeof(buf_name) - 1);
if (rn > 0) {
strcpy(new_name, buf_name);
} else {
if (debug_output()) {
fprintf(stderr, "[pm_name] error reading from socket\n");
}
}
close(sockfd);
}
static uid_t nemo_uid()
{
static struct passwd *nemo_pwd;
if (!nemo_pwd) {
nemo_pwd = getpwnam("nemo");
if (!nemo_pwd) {
return 100000;
}
}
return nemo_pwd->pw_uid;
}
static int pm_validate_uid(uid_t uid)
{
#ifdef ALLOW_ALL_USERS
return 1;
#else // #ifdef ALLOW_ALL_USERS
return uid == nemo_uid();
#endif // #ifdef ALLOW_ALL_USERS
}
static int pm_validate_flags(int flags)
{
return (flags & (O_APPEND | O_WRONLY | O_RDWR | O_TRUNC | O_CREAT | O_NOCTTY | O_TMPFILE | O_SYNC | O_DSYNC | O_DIRECTORY | O_DIRECT)) == 0;
}
static int pm_validate_name(const char *name)
{
char dir_name[PATH_MAX];
strcpy(dir_name, name);
dirname(dir_name);
for (unsigned int i = 0; i < sizeof(blacklist_paths_equal) / sizeof(*blacklist_paths_equal); i++) {
const char *blacklisted = blacklist_paths_equal[i];
if (strcmp(blacklisted, dir_name) == 0) {
return 0;
}
}
for (unsigned int i = 0; i < sizeof(blacklist_paths_startswith) / sizeof(*blacklist_paths_startswith); i++) {
const char *blacklisted = blacklist_paths_startswith[i];
if (strncmp(blacklisted, name, strlen(blacklisted)) == 0) {
return 0;
}
}
return 1;
}
static int no_preload() {
static int pm_preload_read = 0;
static int no_pm_preload = 0;
if (!pm_preload_read) {
no_pm_preload = getenv(ENV_NO_PRELOAD) ? 1 : 0;
pm_preload_read = 1;
}
return no_pm_preload;
}
#endif // #ifndef NO_INTERCEPT
int open64(const char *pathname, int flags, ...)
{
if (!orig_open64) {
orig_open64 = (orig_open_f_type)dlsym(RTLD_NEXT, "open64");
}
va_list args;
va_start(args, flags);
int mode = va_arg(args, int);
va_end(args);
#ifndef NO_INTERCEPT
char new_name[PATH_MAX];
realpath(pathname, new_name);
const int d_no_preload = no_preload();
const int d_pm_validate_uid = pm_validate_uid(getuid());
const int d_pm_validate_flags = pm_validate_flags(flags);
const int d_pm_validate_name = pm_validate_name(new_name);
if (debug_output()) {
char dir_name[PATH_MAX];
strcpy(dir_name, new_name);
dirname(dir_name);
fprintf(stderr, "[open64] pid: %d, path: %s (%s), dir: %s, flags: %d, mode: %d, no_preload: %d, validate_uid: %d, validate_flags: %d, validate_name: %d\n",
getpid(), new_name, pathname, dir_name, flags, mode, d_no_preload, d_pm_validate_uid, d_pm_validate_flags, d_pm_validate_name);
}
if (!d_no_preload && d_pm_validate_uid && d_pm_validate_flags && d_pm_validate_name) {
pm_name(new_name);
if (debug_output()) {
fprintf(stderr, "[open64] new_name: %s\n", new_name);
}
return orig_open64(new_name, flags, mode);
}
#endif // #ifndef NO_INTERCEPT
return orig_open64(pathname, flags, mode);
}
int open(const char *pathname, int flags, ...)
{
if (!orig_open) {
orig_open = (orig_open_f_type)dlsym(RTLD_NEXT, "open");
}
va_list args;
va_start(args, flags);
int mode = va_arg(args, int);
va_end(args);
#ifndef NO_INTERCEPT
char new_name[PATH_MAX];
realpath(pathname, new_name);
const int d_no_preload = no_preload();
const int d_pm_validate_uid = pm_validate_uid(getuid());
const int d_pm_validate_flags = pm_validate_flags(flags);
const int d_pm_validate_name = pm_validate_name(new_name);
if (debug_output()) {
char dir_name[PATH_MAX];
strcpy(dir_name, new_name);
dirname(dir_name);
fprintf(stderr, "[open] pid: %d, path: %s (%s), dir: %s, flags: %d, mode: %d, no_preload: %d, validate_uid: %d, validate_flags: %d, validate_name: %d\n",
getpid(), new_name, pathname, dir_name, flags, mode, d_no_preload, d_pm_validate_uid, d_pm_validate_flags, d_pm_validate_name);
}
if (!d_no_preload && d_pm_validate_uid && d_pm_validate_flags && d_pm_validate_name) {
pm_name(new_name);
if (debug_output()) {
fprintf(stderr, "[open] new_name: %s\n", new_name);
}
return orig_open(new_name, flags, mode);
}
#endif // #ifndef NO_INTERCEPT
return orig_open(pathname, flags, mode);
}