build: commit all the waf build files in the tree
[kai/samba.git] / source4 / ntvfs / posix / pvfs_sys.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - pvfs_sys wrappers
5
6    Copyright (C) Andrew Tridgell 2010
7    Copyright (C) Andrew Bartlett 2010
8
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.
13
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.
18
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/>.
21 */
22
23 #include "includes.h"
24 #include "vfs_posix.h"
25 #include "../lib/util/unix_privs.h"
26
27 /*
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)
32
33   Careful use of O_NOFOLLOW and O_DIRECTORY is used to prevent
34   security attacks via symlinks
35  */
36
37
38 struct pvfs_sys_ctx {
39         struct pvfs_state *pvfs;
40         void *privs;
41         const char *old_wd;
42         struct stat st_orig;
43 };
44
45
46 /*
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.
51  */
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
62
63 /*
64   return to original directory when context is destroyed
65  */
66 static int pvfs_sys_pushdir_destructor(struct pvfs_sys_ctx *ctx)
67 {
68         struct stat st;
69
70         if (ctx->old_wd == NULL) {
71                 return 0;
72         }
73
74         if (chdir(ctx->old_wd) != 0) {
75                 smb_panic("Failed to restore working directory");
76         }
77         if (stat(".", &st) != 0) {
78                 smb_panic("Failed to stat working directory");
79         }
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");
83         }
84
85         return 0;
86 }
87
88
89 /*
90   chdir() to the directory part of a pathname, but disallow any
91   component with a symlink
92
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
95  */
96 static int pvfs_sys_chdir_nosymlink(struct pvfs_sys_ctx *ctx, const char *pathname)
97 {
98         char *p, *path;
99         size_t base_len = strlen(ctx->pvfs->base_directory);
100
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;
106                 }
107                 pathname += base_len + 1;
108         }
109
110         path = talloc_strdup(ctx, pathname);
111         if (path == NULL) {
112                 return -1;
113         }
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;
121                 }
122                 if (chdir(path) != 0) {
123                         close(fd);
124                         return -1;
125                 }
126                 if (stat(".", &st1) != 0 ||
127                     fstat(fd, &st2) != 0) {
128                         close(fd);
129                         return -1;
130                 }
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;
137                 }
138                 path = p + 1;
139         }
140
141         return 0;
142 }
143
144
145 /*
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
149
150   change the pathname argument to contain just the base component of
151   the path
152
153   return NULL on error, which could include an attempt to subvert
154   security using symlink tricks
155  */
156 static struct pvfs_sys_ctx *pvfs_sys_pushdir(struct pvfs_state *pvfs,
157                                              const char **pathname)
158 {
159         struct pvfs_sys_ctx *ctx;
160         char *cwd, *p, *dirname;
161         int ret;
162
163         ctx = talloc_zero(pvfs, struct pvfs_sys_ctx);
164         if (ctx == NULL) {
165                 return NULL;
166         }
167         ctx->pvfs = pvfs;
168         ctx->privs = root_privileges();
169         if (ctx->privs == NULL) {
170                 talloc_free(ctx);
171                 return NULL;
172         }
173
174         talloc_steal(ctx, ctx->privs);
175
176         if (!pathname) {
177                 /* no pathname needed */
178                 return ctx;
179         }
180
181         p = strrchr(*pathname, '/');
182         if (p == NULL) {
183                 /* we don't need to change directory */
184                 return ctx;
185         }
186
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;
192         }
193
194         cwd = get_current_dir_name();
195         if (cwd == NULL) {
196                 talloc_free(ctx);
197                 return NULL;
198         }
199         ctx->old_wd = talloc_strdup(ctx, cwd);
200         if (ctx->old_wd == NULL) {
201                 free(cwd);
202                 talloc_free(ctx);
203                 return NULL;
204         }
205
206         dirname = talloc_strndup(ctx, *pathname, (p - *pathname));
207         if (dirname == NULL) {
208                 talloc_free(ctx);
209                 return NULL;
210         }
211
212         ret = pvfs_sys_chdir_nosymlink(ctx, *pathname);
213         if (ret == -1) {
214                 talloc_free(ctx);
215                 return NULL;
216         }
217
218         talloc_set_destructor(ctx, pvfs_sys_pushdir_destructor);
219
220         /* return the basename as the filename that should be operated on */
221         (*pathname) = talloc_strdup(ctx, p+1);
222         if (! *pathname) {
223                 talloc_free(ctx);
224                 return NULL;
225         }
226
227         return ctx;
228 }
229
230
231 /*
232   chown a file that we created with a root privileges override
233  */
234 static int pvfs_sys_fchown(struct pvfs_state *pvfs, struct pvfs_sys_ctx *ctx, int fd)
235 {
236         return fchown(fd, root_privileges_original_uid(ctx->privs), -1);
237 }
238
239 /*
240   chown a directory that we created with a root privileges override
241  */
242 static int pvfs_sys_chown(struct pvfs_state *pvfs, struct pvfs_sys_ctx *ctx, const char *name)
243 {
244         /* to avoid symlink hacks, we need to use fchown() on a directory fd */
245         int ret, fd;
246         fd = open(name, PVFS_DIRECTORY | PVFS_NOFOLLOW | O_RDONLY);
247         if (fd == -1) {
248                 return -1;
249         }
250         ret = pvfs_sys_fchown(pvfs, ctx, fd);
251         close(fd);
252         return ret;
253 }
254
255
256 /*
257   wrap open for system override
258 */
259 int pvfs_sys_open(struct pvfs_state *pvfs, const char *filename, int flags, mode_t mode)
260 {
261         int fd, ret;
262         struct pvfs_sys_ctx *ctx;
263         int saved_errno, orig_errno;
264         int retries = 5;
265
266         orig_errno = errno;
267
268         fd = open(filename, flags, mode);
269         if (fd != -1 ||
270             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
271             errno != EACCES) {
272                 return fd;
273         }
274
275         saved_errno = errno;
276         ctx = pvfs_sys_pushdir(pvfs, &filename);
277         if (ctx == NULL) {
278                 errno = saved_errno;
279                 return -1;
280         }
281
282         /* don't allow permission overrides to follow links */
283         flags |= PVFS_NOFOLLOW;
284
285         /*
286            if O_CREAT was specified and O_EXCL was not specified
287            then initially do the open without O_CREAT, as in that case
288            we know that we did not create the file, so we don't have
289            to fchown it
290          */
291         if ((flags & O_CREAT) && !(flags & O_EXCL)) {
292         try_again:
293                 fd = open(filename, flags & ~O_CREAT, mode);
294                 /* if this open succeeded, or if it failed
295                    with anything other than ENOENT, then we return the
296                    open result, with the original errno */
297                 if (fd == -1 && errno != ENOENT) {
298                         talloc_free(ctx);
299                         errno = saved_errno;
300                         return -1;
301                 }
302                 if (fd != -1) {
303                         /* the file already existed and we opened it */
304                         talloc_free(ctx);
305                         errno = orig_errno;
306                         return fd;
307                 }
308
309                 fd = open(filename, flags | O_EXCL, mode);
310                 if (fd == -1 && errno != EEXIST) {
311                         talloc_free(ctx);
312                         errno = saved_errno;
313                         return -1;
314                 }
315                 if (fd != -1) {
316                         /* we created the file, we need to set the
317                            right ownership on it */
318                         ret = pvfs_sys_fchown(pvfs, ctx, fd);
319                         if (ret == -1) {
320                                 close(fd);
321                                 unlink(filename);
322                                 talloc_free(ctx);
323                                 errno = saved_errno;
324                                 return -1;
325                         }
326                         talloc_free(ctx);
327                         errno = orig_errno;
328                         return fd;
329                 }
330
331                 /* the file got created between the two times
332                    we tried to open it! Try again */
333                 if (retries-- > 0) {
334                         goto try_again;
335                 }
336
337                 talloc_free(ctx);
338                 errno = saved_errno;
339                 return -1;
340         }
341
342         fd = open(filename, flags, mode);
343         if (fd == -1) {
344                 talloc_free(ctx);
345                 errno = saved_errno;
346                 return -1;
347         }
348
349         /* if we have created a file then fchown it */
350         if (flags & O_CREAT) {
351                 ret = pvfs_sys_fchown(pvfs, ctx, fd);
352                 if (ret == -1) {
353                         close(fd);
354                         unlink(filename);
355                         talloc_free(ctx);
356                         errno = saved_errno;
357                         return -1;
358                 }
359         }
360
361         talloc_free(ctx);
362         return fd;
363 }
364
365
366 /*
367   wrap unlink for system override
368 */
369 int pvfs_sys_unlink(struct pvfs_state *pvfs, const char *filename)
370 {
371         int ret;
372         struct pvfs_sys_ctx *ctx;
373         int saved_errno, orig_errno;
374
375         orig_errno = errno;
376
377         ret = unlink(filename);
378         if (ret != -1 ||
379             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
380             errno != EACCES) {
381                 return ret;
382         }
383
384         saved_errno = errno;
385
386         ctx = pvfs_sys_pushdir(pvfs, &filename);
387         if (ctx == NULL) {
388                 errno = saved_errno;
389                 return -1;
390         }
391
392         ret = unlink(filename);
393         if (ret == -1) {
394                 talloc_free(ctx);
395                 errno = saved_errno;
396                 return -1;
397         }
398
399         talloc_free(ctx);
400         errno = orig_errno;
401         return ret;
402 }
403
404
405 static bool contains_symlink(const char *path)
406 {
407         int fd = open(path, PVFS_NOFOLLOW | O_RDONLY);
408         if (fd != -1) {
409                 close(fd);
410                 return false;
411         }
412         return (errno == ELOOP);
413 }
414
415 /*
416   wrap rename for system override
417 */
418 int pvfs_sys_rename(struct pvfs_state *pvfs, const char *name1, const char *name2)
419 {
420         int ret;
421         struct pvfs_sys_ctx *ctx;
422         int saved_errno, orig_errno;
423
424         orig_errno = errno;
425
426         ret = rename(name1, name2);
427         if (ret != -1 ||
428             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
429             errno != EACCES) {
430                 return ret;
431         }
432
433         saved_errno = errno;
434
435         ctx = pvfs_sys_pushdir(pvfs, &name1);
436         if (ctx == NULL) {
437                 errno = saved_errno;
438                 return -1;
439         }
440
441         /* we need the destination as an absolute path */
442         if (name2[0] != '/') {
443                 name2 = talloc_asprintf(ctx, "%s/%s", ctx->old_wd, name2);
444                 if (name2 == NULL) {
445                         talloc_free(ctx);
446                         errno = saved_errno;
447                         return -1;
448                 }
449         }
450
451         /* make sure the destination isn't a symlink beforehand */
452         if (contains_symlink(name2)) {
453                 talloc_free(ctx);
454                 errno = saved_errno;
455                 return -1;
456         }
457
458         ret = rename(name1, name2);
459         if (ret == -1) {
460                 talloc_free(ctx);
461                 errno = saved_errno;
462                 return -1;
463         }
464
465         /* make sure the destination isn't a symlink afterwards */
466         if (contains_symlink(name2)) {
467                 DEBUG(0,(__location__ ": Possible symlink attack in rename to '%s' - unlinking\n", name2));
468                 unlink(name2);
469                 talloc_free(ctx);
470                 errno = saved_errno;
471                 return -1;
472         }
473
474         talloc_free(ctx);
475         errno = orig_errno;
476         return ret;
477 }
478
479
480 /*
481   wrap mkdir for system override
482 */
483 int pvfs_sys_mkdir(struct pvfs_state *pvfs, const char *dirname, mode_t mode)
484 {
485         int ret;
486         struct pvfs_sys_ctx *ctx;
487         int saved_errno, orig_errno;
488
489         orig_errno = errno;
490
491         ret = mkdir(dirname, mode);
492         if (ret != -1 ||
493             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
494             errno != EACCES) {
495                 return ret;
496         }
497
498         saved_errno = errno;
499         ctx = pvfs_sys_pushdir(pvfs, &dirname);
500         if (ctx == NULL) {
501                 errno = saved_errno;
502                 return -1;
503         }
504
505         ret = mkdir(dirname, mode);
506         if (ret == -1) {
507                 talloc_free(ctx);
508                 errno = saved_errno;
509                 return -1;
510         }
511
512         ret = pvfs_sys_chown(pvfs, ctx, dirname);
513         if (ret == -1) {
514                 rmdir(dirname);
515                 talloc_free(ctx);
516                 errno = saved_errno;
517                 return -1;
518         }
519
520         talloc_free(ctx);
521         return ret;
522 }
523
524
525 /*
526   wrap rmdir for system override
527 */
528 int pvfs_sys_rmdir(struct pvfs_state *pvfs, const char *dirname)
529 {
530         int ret;
531         struct pvfs_sys_ctx *ctx;
532         int saved_errno, orig_errno;
533
534         orig_errno = errno;
535
536         ret = rmdir(dirname);
537         if (ret != -1 ||
538             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
539             errno != EACCES) {
540                 return ret;
541         }
542
543         saved_errno = errno;
544
545         ctx = pvfs_sys_pushdir(pvfs, &dirname);
546         if (ctx == NULL) {
547                 errno = saved_errno;
548                 return -1;
549         }
550
551         ret = rmdir(dirname);
552         if (ret == -1) {
553                 talloc_free(ctx);
554                 errno = saved_errno;
555                 return -1;
556         }
557
558         talloc_free(ctx);
559         errno = orig_errno;
560         return ret;
561 }
562
563 /*
564   wrap fchmod for system override
565 */
566 int pvfs_sys_fchmod(struct pvfs_state *pvfs, int fd, mode_t mode)
567 {
568         int ret;
569         struct pvfs_sys_ctx *ctx;
570         int saved_errno, orig_errno;
571
572         orig_errno = errno;
573
574         ret = fchmod(fd, mode);
575         if (ret != -1 ||
576             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
577             errno != EACCES) {
578                 return ret;
579         }
580
581         saved_errno = errno;
582
583         ctx = pvfs_sys_pushdir(pvfs, NULL);
584         if (ctx == NULL) {
585                 errno = saved_errno;
586                 return -1;
587         }
588
589         ret = fchmod(fd, mode);
590         if (ret == -1) {
591                 talloc_free(ctx);
592                 errno = saved_errno;
593                 return -1;
594         }
595
596         talloc_free(ctx);
597         errno = orig_errno;
598         return ret;
599 }
600
601
602 /*
603   wrap chmod for system override
604 */
605 int pvfs_sys_chmod(struct pvfs_state *pvfs, const char *filename, mode_t mode)
606 {
607         int ret;
608         struct pvfs_sys_ctx *ctx;
609         int saved_errno, orig_errno;
610
611         orig_errno = errno;
612
613         ret = chmod(filename, mode);
614         if (ret != -1 ||
615             !(pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) ||
616             errno != EACCES) {
617                 return ret;
618         }
619
620         saved_errno = errno;
621
622         ctx = pvfs_sys_pushdir(pvfs, &filename);
623         if (ctx == NULL) {
624                 errno = saved_errno;
625                 return -1;
626         }
627
628         ret = chmod(filename, mode);
629         if (ret == -1) {
630                 talloc_free(ctx);
631                 errno = saved_errno;
632                 return -1;
633         }
634
635         talloc_free(ctx);
636         errno = orig_errno;
637         return ret;
638 }