smbd: Remove superfluous ()
[Samba.git] / source4 / ntvfs / posix / pvfs_sys.c
blobf56704692c4670a2e698bb0b41774fc52a6b84bc
1 /*
2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - pvfs_sys wrappers
6 Copyright (C) Andrew Tridgell 2010
7 Copyright (C) Andrew Bartlett 2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "vfs_posix.h"
25 #include "../lib/util/unix_privs.h"
28 these wrapper functions must only be called when the appropriate ACL
29 has already been checked. The wrappers will override a EACCES result
30 by gaining root privileges if the 'pvfs:perm override' is set on the
31 share (it is enabled by default)
33 Careful use of O_NOFOLLOW and O_DIRECTORY is used to prevent
34 security attacks via symlinks
38 struct pvfs_sys_ctx {
39 struct pvfs_state *pvfs;
40 void *privs;
41 const char *old_wd;
42 struct stat st_orig;
47 we create PVFS_NOFOLLOW and PVFS_DIRECTORY as aliases for O_NOFOLLOW
48 and O_DIRECTORY on systems that have them. On systems that don't
49 have O_NOFOLLOW we are less safe, but the root override code is off
50 by default.
52 #ifdef O_NOFOLLOW
53 #define PVFS_NOFOLLOW O_NOFOLLOW
54 #else
55 #define PVFS_NOFOLLOW 0
56 #endif
57 #ifdef O_DIRECTORY
58 #define PVFS_DIRECTORY O_DIRECTORY
59 #else
60 #define PVFS_DIRECTORY 0
61 #endif
64 return to original directory when context is destroyed
66 static int pvfs_sys_pushdir_destructor(struct pvfs_sys_ctx *ctx)
68 struct stat st;
70 if (ctx->old_wd == NULL) {
71 return 0;
74 if (chdir(ctx->old_wd) != 0) {
75 smb_panic("Failed to restore working directory");
77 if (stat(".", &st) != 0) {
78 smb_panic("Failed to stat working directory");
80 if (st.st_ino != ctx->st_orig.st_ino ||
81 st.st_dev != ctx->st_orig.st_dev) {
82 smb_panic("Working directory changed during call");
85 return 0;
90 chdir() to the directory part of a pathname, but disallow any
91 component with a symlink
93 Note that we can't use O_NOFOLLOW on the whole path as that only
94 prevents links in the final component of the path
96 static int pvfs_sys_chdir_nosymlink(struct pvfs_sys_ctx *ctx, const char *pathname)
98 char *p, *path;
99 size_t base_len = strlen(ctx->pvfs->base_directory);
101 /* don't check for symlinks in the base directory of the share */
102 if (strncmp(ctx->pvfs->base_directory, pathname, base_len) == 0 &&
103 pathname[base_len] == '/') {
104 if (chdir(ctx->pvfs->base_directory) != 0) {
105 return -1;
107 pathname += base_len + 1;
110 path = talloc_strdup(ctx, pathname);
111 if (path == NULL) {
112 return -1;
114 while ((p = strchr(path, '/'))) {
115 int fd;
116 struct stat st1, st2;
117 *p = 0;
118 fd = open(path, PVFS_NOFOLLOW | PVFS_DIRECTORY | O_RDONLY);
119 if (fd == -1) {
120 return -1;
122 if (chdir(path) != 0) {
123 close(fd);
124 return -1;
126 if (stat(".", &st1) != 0 ||
127 fstat(fd, &st2) != 0) {
128 close(fd);
129 return -1;
131 close(fd);
132 if (st1.st_ino != st2.st_ino ||
133 st1.st_dev != st2.st_dev) {
134 DEBUG(0,(__location__ ": Inode changed during chdir in '%s' - symlink attack?",
135 pathname));
136 return -1;
138 path = p + 1;
141 return 0;
146 become root, and change directory to the directory component of a
147 path. Return a talloc context which when freed will move us back
148 to the original directory, and return us to the original uid
150 change the pathname argument to contain just the base component of
151 the path
153 return NULL on error, which could include an attempt to subvert
154 security using symlink tricks
156 static struct pvfs_sys_ctx *pvfs_sys_pushdir(struct pvfs_state *pvfs,
157 const char **pathname)
159 struct pvfs_sys_ctx *ctx;
160 char *cwd, *p, *dirname;
161 int ret;
163 ctx = talloc_zero(pvfs, struct pvfs_sys_ctx);
164 if (ctx == NULL) {
165 return NULL;
167 ctx->pvfs = pvfs;
168 ctx->privs = root_privileges();
169 if (ctx->privs == NULL) {
170 talloc_free(ctx);
171 return NULL;
174 talloc_steal(ctx, ctx->privs);
176 if (!pathname) {
177 /* no pathname needed */
178 return ctx;
181 p = strrchr(*pathname, '/');
182 if (p == NULL) {
183 /* we don't need to change directory */
184 return ctx;
187 /* we keep the old st around, so we can tell that
188 we have come back to the right directory */
189 if (stat(".", &ctx->st_orig) != 0) {
190 talloc_free(ctx);
191 return NULL;
194 cwd = get_current_dir_name();
195 if (cwd == NULL) {
196 talloc_free(ctx);
197 return NULL;
200 ctx->old_wd = talloc_strdup(ctx, cwd);
201 free(cwd);
203 if (ctx->old_wd == NULL) {
204 talloc_free(ctx);
205 return NULL;
208 dirname = talloc_strndup(ctx, *pathname, (p - *pathname));
209 if (dirname == NULL) {
210 talloc_free(ctx);
211 return NULL;
214 ret = pvfs_sys_chdir_nosymlink(ctx, *pathname);
215 if (ret == -1) {
216 talloc_free(ctx);
217 return NULL;
220 talloc_set_destructor(ctx, pvfs_sys_pushdir_destructor);
222 /* return the basename as the filename that should be operated on */
223 (*pathname) = talloc_strdup(ctx, p+1);
224 if (! *pathname) {
225 talloc_free(ctx);
226 return NULL;
229 return ctx;
234 chown a file that we created with a root privileges override
236 static int pvfs_sys_fchown(struct pvfs_state *pvfs, struct pvfs_sys_ctx *ctx, int fd)
238 return fchown(fd, root_privileges_original_uid(ctx->privs), -1);
242 chown a directory that we created with a root privileges override
244 static int pvfs_sys_chown(struct pvfs_state *pvfs, struct pvfs_sys_ctx *ctx, const char *name)
246 /* to avoid symlink hacks, we need to use fchown() on a directory fd */
247 int ret, fd;
248 fd = open(name, PVFS_DIRECTORY | PVFS_NOFOLLOW | O_RDONLY);
249 if (fd == -1) {
250 return -1;
252 ret = pvfs_sys_fchown(pvfs, ctx, fd);
253 close(fd);
254 return ret;
259 wrap open for system override
261 int pvfs_sys_open(struct pvfs_state *pvfs, const char *filename, int flags, mode_t mode, bool allow_override)
263 int fd, ret;
264 struct pvfs_sys_ctx *ctx;
265 int saved_errno, orig_errno;
266 int retries = 5;
268 orig_errno = errno;
270 fd = open(filename, flags, mode);
271 if (fd != -1 ||
272 !allow_override ||
273 errno != EACCES) {
274 return fd;
277 saved_errno = errno;
278 ctx = pvfs_sys_pushdir(pvfs, &filename);
279 if (ctx == NULL) {
280 errno = saved_errno;
281 return -1;
284 /* don't allow permission overrides to follow links */
285 flags |= PVFS_NOFOLLOW;
288 if O_CREAT was specified and O_EXCL was not specified
289 then initially do the open without O_CREAT, as in that case
290 we know that we did not create the file, so we don't have
291 to fchown it
293 if ((flags & O_CREAT) && !(flags & O_EXCL)) {
294 try_again:
295 fd = open(filename, flags & ~O_CREAT, mode);
296 /* if this open succeeded, or if it failed
297 with anything other than ENOENT, then we return the
298 open result, with the original errno */
299 if (fd == -1 && errno != ENOENT) {
300 talloc_free(ctx);
301 errno = saved_errno;
302 return -1;
304 if (fd != -1) {
305 /* the file already existed and we opened it */
306 talloc_free(ctx);
307 errno = orig_errno;
308 return fd;
311 fd = open(filename, flags | O_EXCL, mode);
312 if (fd == -1 && errno != EEXIST) {
313 talloc_free(ctx);
314 errno = saved_errno;
315 return -1;
317 if (fd != -1) {
318 /* we created the file, we need to set the
319 right ownership on it */
320 ret = pvfs_sys_fchown(pvfs, ctx, fd);
321 if (ret == -1) {
322 close(fd);
323 unlink(filename);
324 talloc_free(ctx);
325 errno = saved_errno;
326 return -1;
328 talloc_free(ctx);
329 errno = orig_errno;
330 return fd;
333 /* the file got created between the two times
334 we tried to open it! Try again */
335 if (retries-- > 0) {
336 goto try_again;
339 talloc_free(ctx);
340 errno = saved_errno;
341 return -1;
344 fd = open(filename, flags, mode);
345 if (fd == -1) {
346 talloc_free(ctx);
347 errno = saved_errno;
348 return -1;
351 /* if we have created a file then fchown it */
352 if (flags & O_CREAT) {
353 ret = pvfs_sys_fchown(pvfs, ctx, fd);
354 if (ret == -1) {
355 close(fd);
356 unlink(filename);
357 talloc_free(ctx);
358 errno = saved_errno;
359 return -1;
363 talloc_free(ctx);
364 return fd;
369 wrap unlink for system override
371 int pvfs_sys_unlink(struct pvfs_state *pvfs, const char *filename, bool allow_override)
373 int ret;
374 struct pvfs_sys_ctx *ctx;
375 int saved_errno, orig_errno;
377 orig_errno = errno;
379 ret = unlink(filename);
380 if (ret != -1 ||
381 !allow_override ||
382 errno != EACCES) {
383 return ret;
386 saved_errno = errno;
388 ctx = pvfs_sys_pushdir(pvfs, &filename);
389 if (ctx == NULL) {
390 errno = saved_errno;
391 return -1;
394 ret = unlink(filename);
395 if (ret == -1) {
396 talloc_free(ctx);
397 errno = saved_errno;
398 return -1;
401 talloc_free(ctx);
402 errno = orig_errno;
403 return ret;
407 static bool contains_symlink(const char *path)
409 int fd = open(path, PVFS_NOFOLLOW | O_RDONLY);
410 int posix_errno = errno;
411 if (fd != -1) {
412 close(fd);
413 return false;
416 #if defined(ENOTSUP) && defined(OSF1)
417 /* handle special Tru64 errno */
418 if (errno == ENOTSUP) {
419 posix_errno = ELOOP;
421 #endif /* ENOTSUP */
423 #ifdef EFTYPE
424 /* fix broken NetBSD errno */
425 if (errno == EFTYPE) {
426 posix_errno = ELOOP;
428 #endif /* EFTYPE */
430 /* fix broken FreeBSD errno */
431 if (errno == EMLINK) {
432 posix_errno = ELOOP;
435 return (posix_errno == ELOOP);
439 wrap rename for system override
441 int pvfs_sys_rename(struct pvfs_state *pvfs, const char *name1, const char *name2, bool allow_override)
443 int ret;
444 struct pvfs_sys_ctx *ctx;
445 int saved_errno, orig_errno;
447 orig_errno = errno;
449 ret = rename(name1, name2);
450 if (ret != -1 ||
451 !allow_override ||
452 errno != EACCES) {
453 return ret;
456 saved_errno = errno;
458 ctx = pvfs_sys_pushdir(pvfs, &name1);
459 if (ctx == NULL) {
460 errno = saved_errno;
461 return -1;
464 /* we need the destination as an absolute path */
465 if (name2[0] != '/') {
466 name2 = talloc_asprintf(ctx, "%s/%s", ctx->old_wd, name2);
467 if (name2 == NULL) {
468 talloc_free(ctx);
469 errno = saved_errno;
470 return -1;
474 /* make sure the destination isn't a symlink beforehand */
475 if (contains_symlink(name2)) {
476 talloc_free(ctx);
477 errno = saved_errno;
478 return -1;
481 ret = rename(name1, name2);
482 if (ret == -1) {
483 talloc_free(ctx);
484 errno = saved_errno;
485 return -1;
488 /* make sure the destination isn't a symlink afterwards */
489 if (contains_symlink(name2)) {
490 DEBUG(0,(__location__ ": Possible symlink attack in rename to '%s' - unlinking\n", name2));
491 unlink(name2);
492 talloc_free(ctx);
493 errno = saved_errno;
494 return -1;
497 talloc_free(ctx);
498 errno = orig_errno;
499 return ret;
504 wrap mkdir for system override
506 int pvfs_sys_mkdir(struct pvfs_state *pvfs, const char *dirname, mode_t mode, bool allow_override)
508 int ret;
509 struct pvfs_sys_ctx *ctx;
510 int saved_errno, orig_errno;
512 orig_errno = errno;
514 ret = mkdir(dirname, mode);
515 if (ret != -1 ||
516 !allow_override ||
517 errno != EACCES) {
518 return ret;
521 saved_errno = errno;
522 ctx = pvfs_sys_pushdir(pvfs, &dirname);
523 if (ctx == NULL) {
524 errno = saved_errno;
525 return -1;
528 ret = mkdir(dirname, mode);
529 if (ret == -1) {
530 talloc_free(ctx);
531 errno = saved_errno;
532 return -1;
535 ret = pvfs_sys_chown(pvfs, ctx, dirname);
536 if (ret == -1) {
537 rmdir(dirname);
538 talloc_free(ctx);
539 errno = saved_errno;
540 return -1;
543 talloc_free(ctx);
544 errno = orig_errno;
545 return ret;
550 wrap rmdir for system override
552 int pvfs_sys_rmdir(struct pvfs_state *pvfs, const char *dirname, bool allow_override)
554 int ret;
555 struct pvfs_sys_ctx *ctx;
556 int saved_errno, orig_errno;
558 orig_errno = errno;
560 ret = rmdir(dirname);
561 if (ret != -1 ||
562 !allow_override ||
563 errno != EACCES) {
564 return ret;
567 saved_errno = errno;
569 ctx = pvfs_sys_pushdir(pvfs, &dirname);
570 if (ctx == NULL) {
571 errno = saved_errno;
572 return -1;
575 ret = rmdir(dirname);
576 if (ret == -1) {
577 talloc_free(ctx);
578 errno = saved_errno;
579 return -1;
582 talloc_free(ctx);
583 errno = orig_errno;
584 return ret;
588 wrap fchmod for system override
590 int pvfs_sys_fchmod(struct pvfs_state *pvfs, int fd, mode_t mode, bool allow_override)
592 int ret;
593 struct pvfs_sys_ctx *ctx;
594 int saved_errno, orig_errno;
596 orig_errno = errno;
598 ret = fchmod(fd, mode);
599 if (ret != -1 ||
600 !allow_override ||
601 errno != EACCES) {
602 return ret;
605 saved_errno = errno;
607 ctx = pvfs_sys_pushdir(pvfs, NULL);
608 if (ctx == NULL) {
609 errno = saved_errno;
610 return -1;
613 ret = fchmod(fd, mode);
614 if (ret == -1) {
615 talloc_free(ctx);
616 errno = saved_errno;
617 return -1;
620 talloc_free(ctx);
621 errno = orig_errno;
622 return ret;
627 wrap chmod for system override
629 int pvfs_sys_chmod(struct pvfs_state *pvfs, const char *filename, mode_t mode, bool allow_override)
631 int ret;
632 struct pvfs_sys_ctx *ctx;
633 int saved_errno, orig_errno;
635 orig_errno = errno;
637 ret = chmod(filename, mode);
638 if (ret != -1 ||
639 !allow_override ||
640 errno != EACCES) {
641 return ret;
644 saved_errno = errno;
646 ctx = pvfs_sys_pushdir(pvfs, &filename);
647 if (ctx == NULL) {
648 errno = saved_errno;
649 return -1;
652 ret = chmod(filename, mode);
653 if (ret == -1) {
654 talloc_free(ctx);
655 errno = saved_errno;
656 return -1;
659 talloc_free(ctx);
660 errno = orig_errno;
661 return ret;