rewrote it completely
[util-vserver.git] / util-vserver / src / secure-mount.c
1 // $Id$    --*- c++ -*--
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19   // secure-mount <secure-base> <src> <dst>
20   //
21   // mounts <src> into the chroot <secure-base> at destination <dst> in a way
22   // that prevents symlink attacks
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #define VERSION_COPYRIGHT_YEAR          "2003"
30 #define VERSION_COPYRIGHT_AUTHOR        "Enrico Scholz"
31 #include "util.h"
32
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <stdbool.h>
40 #include <sys/mount.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <linux/fs.h>
44
45 #define MNTPOINT        "/etc"
46
47 static void
48 showHelp(int fd, char const *cmd, int res)
49 {
50   WRITE_MSG(fd, "Usage:  ");
51   WRITE_STR(fd, cmd);
52   WRITE_MSG(fd,
53             " <secure-base> <src> <dst>\n\n"
54             "Mounts <src> to <secure-base>/<dst> and assumes <secure-base>\n"
55             "as trustworthy but <dst> as hostile. Therefore, <dst> will be\n"
56             "traversed in a secure way and must not contain symlinks.\n\n"
57             "Please report bugs to " PACKAGE_BUGREPORT "\n");
58
59   exit(res);
60 }
61
62 static void
63 showVersion()
64 {
65   WRITE_MSG(1,
66             "secure-mount " VERSION " -- secure mounting of directories\n"
67             "This program is part of " PACKAGE_STRING "\n"
68             "Copyright (C) 2003 Enrico Scholz\n"
69             VERSION_COPYRIGHT_DISCLAIMER);
70   exit(0);
71 }
72
73 inline static bool
74 isSameObject(struct stat const *lhs,
75              struct stat const *rhs)
76 {
77   return (lhs->st_dev==rhs->st_dev &&
78           lhs->st_ino==rhs->st_ino);
79 }
80
81 static int
82 chdirSecure(char const *dir)
83 {
84   char          tmp[strlen(dir)+1], *ptr;
85   char const    *cur;
86
87   strcpy(tmp, dir);
88   cur = strtok_r(tmp, "/", &ptr);
89   while (cur) {
90     struct stat         pre_stat, post_stat;
91
92     if (lstat(cur, &pre_stat)==-1) return -1;
93     
94     if (!S_ISDIR(pre_stat.st_mode)) {
95       errno = ENOENT;
96       return -1;
97     }
98     if (S_ISLNK(pre_stat.st_mode)) {
99       errno = EINVAL;
100       return -1;
101     }
102
103     if (chdir(cur)==-1)            return -1;
104     if (stat(".", &post_stat)==-1) return -1;
105
106     if (!isSameObject(&pre_stat, &post_stat)) {
107       char      dir[PATH_MAX];
108       
109       WRITE_MSG(2, "Possible symlink race ATTACK at '");
110       WRITE_STR(2, getcwd(dir, sizeof(dir)));
111       WRITE_MSG(2, "'\n");
112
113       errno = EINVAL;
114       return -1;
115     }
116
117     cur = strtok_r(0, "/", &ptr);
118   }
119
120   return 0;
121 }
122
123 static int
124 verifyPosition(char const *mntpoint, char const *dir1, char const *dir2)
125 {
126   struct stat           pre_stat, post_stat;
127
128   if (stat(mntpoint, &pre_stat)==-1)       return -1;
129   if (chroot(dir1)==-1 || chdir(dir2)==-1) return -1;
130   if (stat(".", &post_stat)==-1)           return -1;
131
132   if (!isSameObject(&pre_stat, &post_stat)) {
133     char        dir[PATH_MAX];
134       
135     WRITE_MSG(2, "Possible symlink race ATTACK at '");
136     WRITE_STR(2, getcwd(dir, sizeof(dir)));
137     WRITE_MSG(2, "' within '");
138     WRITE_MSG(2, dir1);
139     WRITE_STR(2, "'\n");
140
141     errno = EINVAL;
142     return -1;
143   }
144
145   return 0;
146 }
147
148 int main(int argc, char *argv[])
149 {
150   if (argc>=2) {
151     if (!strcmp(argv[1], "--help"))    showHelp(1, argv[0], 0);
152     if (!strcmp(argv[1], "--version")) showVersion();
153   }
154   if (argc!=4) {
155     WRITE_MSG(2, "Bad parameter count; use --help for further information.\n");
156     exit(255);
157   }
158
159   if (chdir(argv[1])==-1) {
160     perror("chdir()");
161     return 1;
162   }
163
164   if (chdirSecure(argv[3])==-1) {
165     perror("chdirSecure()");
166     return 1;
167   }
168
169   if (mount(argv[2], ".", "", MS_BIND, 0)==-1) {
170     perror("mount()");
171     return 1;
172   }
173
174     // Check if directories were moved between the chdirSecure() and mount(2)
175   if (verifyPosition(argv[2], argv[1], argv[3])==-1) {
176       // TODO: what is with unmounting?
177     return 1;
178   }
179
180   return 0;
181 }