1 |
/* |
2 |
* Copyright (C) 2002 Brad House <brad@mainstreetsoftworks.com> |
3 |
* Distributed under the terms of the GNU General Public License, v2 or later |
4 |
* Author: Brad House <brad@mainstreetsoftworks.com> |
5 |
* |
6 |
* $Header$ |
7 |
* |
8 |
*/ |
9 |
|
10 |
#include <errno.h> |
11 |
#include <fcntl.h> |
12 |
#include <signal.h> |
13 |
#include <stdio.h> |
14 |
#include <stdlib.h> |
15 |
#include <limits.h> |
16 |
#include <string.h> |
17 |
#include <stdarg.h> |
18 |
#include <sys/file.h> |
19 |
#include <sys/stat.h> |
20 |
#include <sys/time.h> |
21 |
#include <sys/types.h> |
22 |
#include <sys/resource.h> |
23 |
#include <sys/wait.h> |
24 |
#include <unistd.h> |
25 |
#include <fcntl.h> |
26 |
|
27 |
#include <grp.h> |
28 |
#include <pwd.h> |
29 |
|
30 |
#include "sandbox.h" |
31 |
#include "config.h" |
32 |
|
33 |
/* BEGIN Prototypes */ |
34 |
int file_security_check(char *filename); |
35 |
/* END Prototypes */ |
36 |
|
37 |
/* glibc modified getcwd() functions */ |
38 |
char *egetcwd(char *, size_t); |
39 |
|
40 |
char *get_sandbox_path(char *argv0) |
41 |
{ |
42 |
char path[255]; |
43 |
char *cwd = NULL; |
44 |
|
45 |
memset(path, 0, sizeof(path)); |
46 |
/* ARGV[0] specifies full path */ |
47 |
if (argv0[0] == '/') { |
48 |
strncpy(path, argv0, sizeof(path) - 1); |
49 |
|
50 |
/* ARGV[0] specifies relative path */ |
51 |
} else { |
52 |
egetcwd(cwd, sizeof(path) - 2); |
53 |
snprintf(path, sizeof(path), "%s/%s", cwd, argv0); |
54 |
if (cwd) |
55 |
free(cwd); |
56 |
cwd = NULL; |
57 |
} |
58 |
|
59 |
/* Return just directory */ |
60 |
return (sb_dirname(path)); |
61 |
} |
62 |
|
63 |
char *get_sandbox_lib(char *sb_path) |
64 |
{ |
65 |
char path[255]; |
66 |
|
67 |
#ifdef SB_HAVE_64BIT_ARCH |
68 |
snprintf(path, sizeof(path), "%s", LIB_NAME); |
69 |
#else |
70 |
snprintf(path, sizeof(path), "%s/%s", LIBSANDBOX_PATH, LIB_NAME); |
71 |
if (file_exist(path, 0) <= 0) { |
72 |
snprintf(path, sizeof(path), "%s%s", sb_path, LIB_NAME); |
73 |
} |
74 |
#endif |
75 |
return (strdup(path)); |
76 |
} |
77 |
|
78 |
char *get_sandbox_pids_file(void) |
79 |
{ |
80 |
if (0 < getenv("SANDBOX_PIDS_FILE")) { |
81 |
return (strdup(getenv("SANDBOX_PIDS_FILE"))); |
82 |
} |
83 |
return (strdup(PIDS_FILE)); |
84 |
} |
85 |
|
86 |
char *get_sandbox_rc(char *sb_path) |
87 |
{ |
88 |
char path[255]; |
89 |
|
90 |
snprintf(path, sizeof(path), "%s/%s", SANDBOX_BASHRC_PATH, BASHRC_NAME); |
91 |
if (file_exist(path, 0) <= 0) { |
92 |
snprintf(path, sizeof(path), "%s%s", sb_path, BASHRC_NAME); |
93 |
} |
94 |
return (strdup(path)); |
95 |
} |
96 |
|
97 |
char *get_sandbox_log() |
98 |
{ |
99 |
char path[255]; |
100 |
char *sandbox_log_env = NULL; |
101 |
|
102 |
/* THIS CHUNK BREAK THINGS BY DOING THIS: |
103 |
* SANDBOX_LOG=/tmp/sandbox-app-admin/superadduser-1.0.7-11063.log |
104 |
*/ |
105 |
|
106 |
sandbox_log_env = getenv(ENV_SANDBOX_LOG); |
107 |
snprintf(path, sizeof(path) - 1, "%s%s%s%d%s", LOG_FILE_PREFIX, |
108 |
(sandbox_log_env == NULL ? "" : sandbox_log_env), |
109 |
(sandbox_log_env == NULL ? "" : "-"), getpid(), LOG_FILE_EXT); |
110 |
return (strdup(path)); |
111 |
} |
112 |
|
113 |
/* Obtain base directory name. Do not allow trailing / */ |
114 |
char *sb_dirname(const char *path) |
115 |
{ |
116 |
char *ret = NULL; |
117 |
char *ptr = NULL; |
118 |
int loc = 0, i; |
119 |
int cut_len = -1; |
120 |
|
121 |
/* don't think NULL will ever be passed, but just in case */ |
122 |
if (NULL == path) |
123 |
return (strdup(".")); |
124 |
|
125 |
/* Grab pointer to last slash */ |
126 |
ptr = strrchr(path, '/'); |
127 |
if (NULL == ptr) { |
128 |
return (strdup(".")); |
129 |
} |
130 |
|
131 |
/* decimal location of pointer */ |
132 |
loc = ptr - path; |
133 |
|
134 |
/* Remove any trailing slash */ |
135 |
for (i = loc - 1; i >= 0; i--) { |
136 |
if (path[i] != '/') { |
137 |
cut_len = i + 1; /* make cut_len the length of the string to keep */ |
138 |
break; |
139 |
} |
140 |
} |
141 |
|
142 |
/* It could have been just a plain /, return a 1byte 0 filled string */ |
143 |
if (-1 == cut_len) |
144 |
return (strdup("")); |
145 |
|
146 |
/* Allocate memory, and return the directory */ |
147 |
ret = (char *)malloc((cut_len + 1) * sizeof(char)); |
148 |
memcpy(ret, path, cut_len); |
149 |
ret[cut_len] = 0; |
150 |
|
151 |
return (ret); |
152 |
} |
153 |
|
154 |
/* |
155 |
char* dirname(const char* path) |
156 |
{ |
157 |
char* base = NULL; |
158 |
unsigned int length = 0; |
159 |
|
160 |
base = strrchr(path, '/'); |
161 |
if (NULL == base) |
162 |
{ |
163 |
return strdup("."); |
164 |
} |
165 |
while (base > path && *base == '/') |
166 |
{ |
167 |
base--; |
168 |
} |
169 |
length = (unsigned int) 1 + base - path; |
170 |
|
171 |
base = malloc(sizeof(char)*(length+1)); |
172 |
memmove(base, path, length); |
173 |
base[length] = 0; |
174 |
|
175 |
return base; |
176 |
}*/ |
177 |
|
178 |
/* Convert text (string) modes to integer values */ |
179 |
int file_getmode(char *mode) |
180 |
{ |
181 |
int mde = 0; |
182 |
if (0 == strcasecmp(mode, "r+")) { |
183 |
mde = O_RDWR | O_CREAT; |
184 |
} else if (0 == strcasecmp(mode, "w+")) { |
185 |
mde = O_RDWR | O_CREAT | O_TRUNC; |
186 |
} else if (0 == strcasecmp(mode, "a+")) { |
187 |
mde = O_RDWR | O_CREAT | O_APPEND; |
188 |
} else if (0 == strcasecmp(mode, "r")) { |
189 |
mde = O_RDONLY; |
190 |
} else if (0 == strcasecmp(mode, "w")) { |
191 |
mde = O_WRONLY | O_CREAT | O_TRUNC; |
192 |
} else if (0 == strcasecmp(mode, "a")) { |
193 |
mde = O_WRONLY | O_APPEND | O_CREAT; |
194 |
} else { |
195 |
mde = O_RDONLY; |
196 |
} |
197 |
return (mde); |
198 |
} |
199 |
|
200 |
/* Get current position in file */ |
201 |
long file_tell(int fp) |
202 |
{ |
203 |
return (lseek(fp, 0L, SEEK_CUR)); |
204 |
} |
205 |
|
206 |
/* lock the file, preferrably the POSIX way */ |
207 |
int file_lock(int fd, int lock, char *filename) |
208 |
{ |
209 |
int err; |
210 |
#ifdef USE_FLOCK |
211 |
if (flock(fd, lock) < 0) { |
212 |
err = errno; |
213 |
fprintf(stderr, ">>> %s flock file lock: %s\n", filename, strerror(err)); |
214 |
return 0; |
215 |
} |
216 |
#else |
217 |
struct flock fl; |
218 |
fl.l_type = lock; |
219 |
fl.l_whence = SEEK_SET; |
220 |
fl.l_start = 0L; |
221 |
fl.l_len = 0L; |
222 |
fl.l_pid = getpid(); |
223 |
if (fcntl(fd, F_SETLKW, &fl) < 0) { |
224 |
err = errno; |
225 |
fprintf(stderr, ">>> %s fcntl file lock: %s\n", filename, strerror(err)); |
226 |
return 0; |
227 |
} |
228 |
#endif |
229 |
return 1; |
230 |
} |
231 |
|
232 |
/* unlock the file, preferrably the POSIX way */ |
233 |
int file_unlock(int fd) |
234 |
{ |
235 |
#ifdef USE_FLOCK |
236 |
if (flock(fd, LOCK_UN) < 0) { |
237 |
perror(">>> flock file unlock"); |
238 |
return 0; |
239 |
} |
240 |
#else |
241 |
struct flock fl; |
242 |
fl.l_type = F_UNLCK; |
243 |
fl.l_whence = SEEK_SET; |
244 |
fl.l_start = 0L; |
245 |
fl.l_len = 0L; |
246 |
fl.l_pid = getpid(); |
247 |
if (fcntl(fd, F_SETLKW, &fl) < 0) { |
248 |
perror(">>> fcntl file unlock"); |
249 |
return 0; |
250 |
} |
251 |
#endif |
252 |
return 1; |
253 |
} |
254 |
|
255 |
/* Auto-determine from how the file was opened, what kind of lock to lock |
256 |
* the file with |
257 |
*/ |
258 |
int file_locktype(char *mode) |
259 |
{ |
260 |
#ifdef USE_FLOCK |
261 |
if (NULL != (strchr(mode, 'w')) || (NULL != strchr(mode, '+')) |
262 |
|| (NULL != strchr(mode, 'a'))) |
263 |
return (LOCK_EX); |
264 |
return (LOCK_SH); |
265 |
#else |
266 |
if (NULL != (strchr(mode, 'w')) || (NULL != strchr(mode, '+')) |
267 |
|| (NULL != strchr(mode, 'a'))) |
268 |
return (F_WRLCK); |
269 |
return (F_RDLCK); |
270 |
#endif |
271 |
} |
272 |
|
273 |
/* Use standard fopen style modes to open the specified file. Also auto-determines and |
274 |
* locks the file either in shared or exclusive mode depending on opening mode |
275 |
*/ |
276 |
int file_open(char *filename, char *mode, int perm_specified, ...) |
277 |
{ |
278 |
int fd; |
279 |
char error[250]; |
280 |
va_list ap; |
281 |
int perm; |
282 |
char *group = NULL; |
283 |
struct group *group_struct; |
284 |
|
285 |
file_security_check(filename); |
286 |
|
287 |
if (perm_specified) { |
288 |
va_start(ap, perm_specified); |
289 |
perm = va_arg(ap, int); |
290 |
group = va_arg(ap, char *); |
291 |
va_end(ap); |
292 |
} |
293 |
fd = open(filename, file_getmode(mode)); |
294 |
file_security_check(filename); |
295 |
if (-1 == fd) { |
296 |
snprintf(error, sizeof(error), ">>> %s file mode: %s open", filename, mode); |
297 |
perror(error); |
298 |
return (fd); |
299 |
} |
300 |
if (perm_specified) { |
301 |
if (fchmod(fd, 0664) && (0 == getuid())) { |
302 |
snprintf(error, sizeof(error), ">>> Could not set mode: %s", filename); |
303 |
perror(error); |
304 |
} |
305 |
} |
306 |
if (NULL != group) { |
307 |
group_struct = getgrnam(group); |
308 |
if (NULL == group) { |
309 |
snprintf(error, sizeof(error), ">>> Could not get grp number: %s", group); |
310 |
perror(error); |
311 |
} else { |
312 |
if (fchown(fd, -1, group_struct->gr_gid) && (0 == getuid())) { |
313 |
snprintf(error, sizeof(error), ">>> Could not set group: %s", filename); |
314 |
perror(error); |
315 |
} |
316 |
} |
317 |
} |
318 |
/* Only lock the file if opening succeeded */ |
319 |
if (-1 != fd) { |
320 |
if (file_security_check(filename) != 0) { |
321 |
/* Security violation occured between the last check and the */ |
322 |
/* creation of the file. As SpanKY pointed out there is a race */ |
323 |
/* condition here, so if there is a problem here we'll mesg and */ |
324 |
/* bail out to avoid it until we can work and test a better fix. */ |
325 |
fprintf(stderr, "\n\nSECURITY RACE CONDITION: Problem recurred after creation!\nBAILING OUT\n\n"); |
326 |
exit(127); |
327 |
} |
328 |
|
329 |
if (0 == file_lock(fd, file_locktype(mode), filename)) { |
330 |
close(fd); |
331 |
return -1; |
332 |
} |
333 |
} else { |
334 |
snprintf(error, sizeof(error), ">>> %s file mode:%s open", filename, mode); |
335 |
perror(error); |
336 |
} |
337 |
return (fd); |
338 |
} |
339 |
|
340 |
/* Close and unlock file */ |
341 |
void file_close(int fd) |
342 |
{ |
343 |
if (-1 != fd) { |
344 |
file_unlock(fd); |
345 |
close(fd); |
346 |
} |
347 |
} |
348 |
|
349 |
/* Return length of file */ |
350 |
long file_length(int fd) |
351 |
{ |
352 |
long pos, len; |
353 |
pos = file_tell(fd); |
354 |
len = lseek(fd, 0L, SEEK_END); |
355 |
lseek(fd, pos, SEEK_SET); |
356 |
return (len); |
357 |
} |
358 |
|
359 |
/* Zero out file */ |
360 |
int file_truncate(int fd) |
361 |
{ |
362 |
lseek(fd, 0L, SEEK_SET); |
363 |
if (ftruncate(fd, 0) < 0) { |
364 |
perror(">>> file truncate"); |
365 |
return 0; |
366 |
} |
367 |
return 1; |
368 |
} |
369 |
|
370 |
/* Check to see if a file exists Return: 1 success, 0 file not found, -1 error */ |
371 |
int file_exist(char *filename, int checkmode) |
372 |
{ |
373 |
struct stat mystat; |
374 |
|
375 |
/* Verify file exists and is regular file (not sym link) */ |
376 |
if (checkmode) { |
377 |
if (-1 == lstat(filename, &mystat)) { |
378 |
/* file doesn't exist */ |
379 |
if (ENOENT == errno) { |
380 |
return 0; |
381 |
} else { /* permission denied or other error */ |
382 |
perror(">>> stat file"); |
383 |
return -1; |
384 |
} |
385 |
} |
386 |
if (!S_ISREG(mystat.st_mode)) |
387 |
return -1; |
388 |
|
389 |
/* Just plain verify the file exists */ |
390 |
} else { |
391 |
if (-1 == stat(filename, &mystat)) { |
392 |
/* file does not exist */ |
393 |
if (ENOENT == errno) { |
394 |
return 0; |
395 |
} else { /* permission denied or other error */ |
396 |
perror(">>> stat file"); |
397 |
return -1; |
398 |
} |
399 |
} |
400 |
} |
401 |
|
402 |
return 1; |
403 |
} |
404 |
|
405 |
int file_security_check(char *filename) |
406 |
{ /* 0 == fine, >0 == problem */ |
407 |
struct stat stat_buf; |
408 |
struct group *group_buf; |
409 |
struct passwd *passwd_buf; |
410 |
|
411 |
passwd_buf = getpwnam("portage"); |
412 |
group_buf = getgrnam("portage"); |
413 |
|
414 |
if ((lstat(filename, &stat_buf) == -1) && (errno == ENOENT)) { |
415 |
/* Doesn't exist. */ |
416 |
return 0; |
417 |
} else { |
418 |
if ((stat_buf.st_nlink) > 1) { /* Security: We are handlinked... */ |
419 |
if (unlink(filename)) { |
420 |
fprintf(stderr, "Unable to delete file in security violation (hardlinked): %s\n", filename); |
421 |
exit(127); |
422 |
} |
423 |
fprintf(stderr, "File in security violation (hardlinked): %s\n", filename); |
424 |
return 1; |
425 |
} else if (S_ISLNK(stat_buf.st_mode)) { /* Security: We are a symlink? */ |
426 |
fprintf(stderr, "File in security violation (symlink): %s\n", filename); |
427 |
exit(127); |
428 |
} else if (0 == S_ISREG(stat_buf.st_mode)) { /* Security: special file */ |
429 |
fprintf(stderr, "File in security violation (not regular): %s\n", filename); |
430 |
exit(127); |
431 |
} else if (stat_buf.st_mode & S_IWOTH) { /* Security: We are o+w? */ |
432 |
if (unlink(filename)) { |
433 |
fprintf(stderr, "Unable to delete file in security violation (world write): %s\n", filename); |
434 |
exit(127); |
435 |
} |
436 |
fprintf(stderr, "File in security violation (world write): %s\n", filename); |
437 |
return 1; |
438 |
} else |
439 |
if (!((stat_buf.st_uid == 0) || (stat_buf.st_uid == getuid()) || |
440 |
((passwd_buf != NULL) && (stat_buf.st_uid == passwd_buf->pw_uid))) || |
441 |
!((stat_buf.st_gid == 0) || (stat_buf.st_gid == getgid()) || |
442 |
((group_buf != NULL) && (stat_buf.st_gid == group_buf->gr_gid)))) { |
443 |
/* Security: Owner/Group isn't right. */ |
444 |
|
445 |
/* uid = 0 or myuid or portage */ |
446 |
/* gid = 0 or mygid or portage */ |
447 |
|
448 |
if (0) { |
449 |
fprintf(stderr, "--1: %d,%d,%d,%d\n--2: %d,%d,%d,%d\n", |
450 |
(stat_buf.st_uid == 0), |
451 |
(stat_buf.st_uid == getuid()), |
452 |
(passwd_buf != NULL), |
453 |
(passwd_buf != NULL) ? (stat_buf.st_uid == passwd_buf->pw_uid) : -1, |
454 |
(stat_buf.st_gid == 0), |
455 |
(stat_buf.st_gid == getgid()), |
456 |
(group_buf != NULL), |
457 |
(group_buf != NULL) ? (stat_buf.st_gid == group_buf->gr_gid) : -1); |
458 |
} |
459 |
|
460 |
/* manpage: "The return value may point to static area" */ |
461 |
/* DO NOT ACTUALLY FREE THIS... It'll segfault. */ |
462 |
/* if(passwd_buf != NULL) { free(passwd_buf); } */ |
463 |
/* if(group_buf != NULL) { free(group_buf); } */ |
464 |
|
465 |
if (unlink(filename)) { |
466 |
fprintf(stderr, "Unable to delete file in security violation (bad owner/group): %s\n", filename); |
467 |
exit(127); |
468 |
} |
469 |
fprintf(stderr, "File in security violation (bad owner/group): %s\n", filename); |
470 |
return 1; |
471 |
} |
472 |
} /* Stat */ |
473 |
return 0; |
474 |
} |
475 |
|
476 |
// vim:noexpandtab noai:cindent ai |