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