1 /*
2  * pickystat.c
3  *
4  * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
5  * All rights reserved.
6  *
7  * $Id: pickystat.c,v 1.11 2015/03/14 06:11:27 marc Exp marc $
8  *
9  */
10 
11 #include "headers.h"
12 
13 static const char rcsid[] __attribute__ ((used)) = "$Id: pickystat.c,v 1.11 2015/03/14 06:11:27 marc Exp marc $";
14 
pickystat_one(struct context * ctx,struct stat * st,char * file)15 static int pickystat_one(struct context *ctx, struct stat *st, char *file)
16 {
17     struct stat lst;
18 
19     Debug((DEBUG_PROC, "+ %s(%s)\n", __func__, file));
20 
21     if (lstat(file, &lst)) {
22 	/* file doesn't exist */
23 
24 	Debug((DEBUG_PROC, "- %s FAILURE (no such file)\n", __func__));
25 	return -1;
26     }
27 
28     if (S_ISLNK(lst.st_mode)) {
29 	/* symbolic link */
30 
31 	if (stat(file, st)) {
32 	    Debug((DEBUG_PROC, "- %s FAILURE (broken link)\n", __func__));
33 	    return -1;
34 	}
35 
36 	if (!(((ctx->allow_symlinks & SYMLINKS_YES)) ||
37 	      ((ctx->allow_symlinks & SYMLINKS_REAL) && !ctx->anonymous) ||
38 	      ((ctx->allow_symlinks & SYMLINKS_ROOT) && lst.st_uid == 0) || ((ctx->allow_symlinks & SYMLINKS_SAME)
39 									     && lst.st_uid == st->st_uid))) {
40 	    Debug((DEBUG_PROC, "- %s FAILURE (unsafe symlink)\n", __func__));
41 	    return -1;
42 	}
43     } else
44 	*st = lst;
45 
46     if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) {
47 	Debug((DEBUG_PROC, "- %s FAILURE (not a plain file or directory)\n", __func__));
48 	return -1;
49     }
50 
51     if (ctx->picky_uidcheck && !ctx->picky_gidcheck && (st->st_uid != ctx->uid)) {
52 	Debug((DEBUG_PROC, "- %s FAILURE (UID check)\n", __func__));
53 	return -1;
54     }
55 
56     if (ctx->picky_gidcheck && !check_gids(ctx, st->st_gid)) {
57 	Debug((DEBUG_PROC, "- %s FAILURE (GID check)\n", __func__));
58 	return -1;
59     }
60 
61     if (ctx->picky_permcheck && !(st->st_mode & S_IROTH)) {
62 	Debug((DEBUG_PROC, "- %s FAILURE (not world readable)\n", __func__));
63 	return -1;
64     }
65 
66     Debug((DEBUG_PROC, "- %s SUCCESS\n", __func__));
67     return 0;
68 }
69 
pickystat(struct context * ctx,struct stat * st,char * path)70 int pickystat(struct context *ctx, struct stat *st, char *path)
71 {
72     int r;
73 
74     Debug((DEBUG_PROC, "+ %s (%s)\n", __func__, path));
75 
76     if (*path == '/') {
77 	int offset;
78 	char *t;
79 
80 	if (strncmp(path, ctx->cwd, ctx->cwdlen) || (path[ctx->cwdlen] && path[ctx->cwdlen] != '/'))
81 	    offset = ctx->rootlen;
82 	else
83 	    offset = ctx->cwdlen;
84 
85 	offset = offset ? offset : 1;
86 
87 	for (t = path + offset; *t; t++)
88 	    if (*t == '/') {
89 		*t = 0;
90 		r = pickystat_one(ctx, st, path);
91 		*t = '/';
92 		if (r) {
93 		    DebugOut(DEBUG_PROC);
94 		    return r;
95 		}
96 	    }
97     }
98 
99     r = pickystat_one(ctx, st, path);
100     DebugOut(DEBUG_PROC);
101     return r;
102 }
103 
pickystat_path(struct context * ctx,struct stat * st,char * path)104 int pickystat_path(struct context *ctx, struct stat *st, char *path)
105 {
106     char *t;
107     int r;
108 
109     t = strrchr(path, '/');
110     if (t)
111 	*t = 0;
112     r = pickystat(ctx, st, path);
113     if (t)
114 	*t = '/';
115     return r;
116 }
117