]> Git Repo - qemu.git/blob - hw/virtio-9p-local.c
virtio-9p: Security model for create/open2
[qemu.git] / hw / virtio-9p-local.c
1 /*
2  * Virtio 9p Posix callback
3  *
4  * Copyright IBM, Corp. 2010
5  *
6  * Authors:
7  *  Anthony Liguori   <[email protected]>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  *
12  */
13 #include "virtio.h"
14 #include "virtio-9p.h"
15 #include <arpa/inet.h>
16 #include <pwd.h>
17 #include <grp.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <attr/xattr.h>
21
22 static const char *rpath(FsContext *ctx, const char *path)
23 {
24     /* FIXME: so wrong... */
25     static char buffer[4096];
26     snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
27     return buffer;
28 }
29
30
31 static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
32 {
33     int err;
34     err =  lstat(rpath(fs_ctx, path), stbuf);
35     if (err) {
36         return err;
37     }
38     if (fs_ctx->fs_sm == SM_MAPPED) {
39         /* Actual credentials are part of extended attrs */
40         uid_t tmp_uid;
41         gid_t tmp_gid;
42         mode_t tmp_mode;
43         dev_t tmp_dev;
44         if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
45                     sizeof(uid_t)) > 0) {
46             stbuf->st_uid = tmp_uid;
47         }
48         if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
49                     sizeof(gid_t)) > 0) {
50             stbuf->st_gid = tmp_gid;
51         }
52         if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
53                     sizeof(mode_t)) > 0) {
54             stbuf->st_mode = tmp_mode;
55         }
56         if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
57                         sizeof(dev_t)) > 0) {
58                 stbuf->st_rdev = tmp_dev;
59         }
60     }
61     return err;
62 }
63
64 static int local_set_xattr(const char *path, FsCred *credp)
65 {
66     int err;
67     if (credp->fc_uid != -1) {
68         err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
69                 0);
70         if (err) {
71             return err;
72         }
73     }
74     if (credp->fc_gid != -1) {
75         err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
76                 0);
77         if (err) {
78             return err;
79         }
80     }
81     if (credp->fc_mode != -1) {
82         err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
83                 sizeof(mode_t), 0);
84         if (err) {
85             return err;
86         }
87     }
88     if (credp->fc_rdev != -1) {
89         err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
90                 sizeof(dev_t), 0);
91         if (err) {
92             return err;
93         }
94     }
95     return 0;
96 }
97
98 static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
99         FsCred *credp)
100 {
101     if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
102         return -1;
103     }
104     if (chown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
105         return -1;
106     }
107     return 0;
108 }
109
110 static ssize_t local_readlink(FsContext *ctx, const char *path,
111                                 char *buf, size_t bufsz)
112 {
113     return readlink(rpath(ctx, path), buf, bufsz);
114 }
115
116 static int local_close(FsContext *ctx, int fd)
117 {
118     return close(fd);
119 }
120
121 static int local_closedir(FsContext *ctx, DIR *dir)
122 {
123     return closedir(dir);
124 }
125
126 static int local_open(FsContext *ctx, const char *path, int flags)
127 {
128     return open(rpath(ctx, path), flags);
129 }
130
131 static DIR *local_opendir(FsContext *ctx, const char *path)
132 {
133     return opendir(rpath(ctx, path));
134 }
135
136 static void local_rewinddir(FsContext *ctx, DIR *dir)
137 {
138     return rewinddir(dir);
139 }
140
141 static off_t local_telldir(FsContext *ctx, DIR *dir)
142 {
143     return telldir(dir);
144 }
145
146 static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
147 {
148     return readdir(dir);
149 }
150
151 static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
152 {
153     return seekdir(dir, off);
154 }
155
156 static ssize_t local_readv(FsContext *ctx, int fd, const struct iovec *iov,
157                             int iovcnt)
158 {
159     return readv(fd, iov, iovcnt);
160 }
161
162 static off_t local_lseek(FsContext *ctx, int fd, off_t offset, int whence)
163 {
164     return lseek(fd, offset, whence);
165 }
166
167 static ssize_t local_writev(FsContext *ctx, int fd, const struct iovec *iov,
168                             int iovcnt)
169 {
170     return writev(fd, iov, iovcnt);
171 }
172
173 static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
174 {
175     if (fs_ctx->fs_sm == SM_MAPPED) {
176         return local_set_xattr(rpath(fs_ctx, path), credp);
177     } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
178         return chmod(rpath(fs_ctx, path), credp->fc_mode);
179     }
180     return -1;
181 }
182
183 static int local_mknod(FsContext *ctx, const char *path, mode_t mode, dev_t dev)
184 {
185     return mknod(rpath(ctx, path), mode, dev);
186 }
187
188 static int local_mksock(FsContext *ctx2, const char *path)
189 {
190     struct sockaddr_un addr;
191     int s;
192
193     addr.sun_family = AF_UNIX;
194     snprintf(addr.sun_path, 108, "%s", rpath(ctx2, path));
195
196     s = socket(PF_UNIX, SOCK_STREAM, 0);
197     if (s == -1) {
198         return -1;
199     }
200
201     if (bind(s, (struct sockaddr *)&addr, sizeof(addr))) {
202         close(s);
203         return -1;
204     }
205
206     close(s);
207     return 0;
208 }
209
210 static int local_mkdir(FsContext *ctx, const char *path, mode_t mode)
211 {
212     return mkdir(rpath(ctx, path), mode);
213 }
214
215 static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
216 {
217     int err;
218     err = fstat(fd, stbuf);
219     if (err) {
220         return err;
221     }
222     if (fs_ctx->fs_sm == SM_MAPPED) {
223         /* Actual credentials are part of extended attrs */
224         uid_t tmp_uid;
225         gid_t tmp_gid;
226         mode_t tmp_mode;
227         dev_t tmp_dev;
228
229         if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
230             stbuf->st_uid = tmp_uid;
231         }
232         if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
233             stbuf->st_gid = tmp_gid;
234         }
235         if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
236             stbuf->st_mode = tmp_mode;
237         }
238         if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
239                 stbuf->st_rdev = tmp_dev;
240         }
241     }
242     return err;
243 }
244
245 static int local_open2(FsContext *fs_ctx, const char *path, int flags,
246         FsCred *credp)
247 {
248     int fd = -1;
249     int err = -1;
250     int serrno = 0;
251
252     /* Determine the security model */
253     if (fs_ctx->fs_sm == SM_MAPPED) {
254         fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
255         if (fd == -1) {
256             return fd;
257         }
258         credp->fc_mode = credp->fc_mode|S_IFREG;
259         /* Set cleint credentials in xattr */
260         err = local_set_xattr(rpath(fs_ctx, path), credp);
261         if (err == -1) {
262             serrno = errno;
263             goto err_end;
264         }
265     } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
266         fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
267         if (fd == -1) {
268             return fd;
269         }
270         err = local_post_create_passthrough(fs_ctx, path, credp);
271         if (err == -1) {
272             serrno = errno;
273             goto err_end;
274         }
275     }
276     return fd;
277
278 err_end:
279     close(fd);
280     remove(rpath(fs_ctx, path));
281     errno = serrno;
282     return err;
283 }
284
285
286 static int local_symlink(FsContext *ctx, const char *oldpath,
287                             const char *newpath)
288 {
289     return symlink(oldpath, rpath(ctx, newpath));
290 }
291
292 static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
293 {
294     char *tmp = qemu_strdup(rpath(ctx, oldpath));
295     int err, serrno = 0;
296
297     if (tmp == NULL) {
298         return -ENOMEM;
299     }
300
301     err = link(tmp, rpath(ctx, newpath));
302     if (err == -1) {
303         serrno = errno;
304     }
305
306     qemu_free(tmp);
307
308     if (err == -1) {
309         errno = serrno;
310     }
311
312     return err;
313 }
314
315 static int local_truncate(FsContext *ctx, const char *path, off_t size)
316 {
317     return truncate(rpath(ctx, path), size);
318 }
319
320 static int local_rename(FsContext *ctx, const char *oldpath,
321                         const char *newpath)
322 {
323     char *tmp;
324     int err;
325
326     tmp = qemu_strdup(rpath(ctx, oldpath));
327     if (tmp == NULL) {
328         return -1;
329     }
330
331     err = rename(tmp, rpath(ctx, newpath));
332     if (err == -1) {
333         int serrno = errno;
334         qemu_free(tmp);
335         errno = serrno;
336     } else {
337         qemu_free(tmp);
338     }
339
340     return err;
341
342 }
343
344 static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
345 {
346     if (fs_ctx->fs_sm == SM_MAPPED) {
347         return local_set_xattr(rpath(fs_ctx, path), credp);
348     } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
349         return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
350     }
351     return -1;
352 }
353
354 static int local_utime(FsContext *ctx, const char *path,
355                         const struct utimbuf *buf)
356 {
357     return utime(rpath(ctx, path), buf);
358 }
359
360 static int local_remove(FsContext *ctx, const char *path)
361 {
362     return remove(rpath(ctx, path));
363 }
364
365 static int local_fsync(FsContext *ctx, int fd)
366 {
367     return fsync(fd);
368 }
369
370 FileOperations local_ops = {
371     .lstat = local_lstat,
372     .readlink = local_readlink,
373     .close = local_close,
374     .closedir = local_closedir,
375     .open = local_open,
376     .opendir = local_opendir,
377     .rewinddir = local_rewinddir,
378     .telldir = local_telldir,
379     .readdir = local_readdir,
380     .seekdir = local_seekdir,
381     .readv = local_readv,
382     .lseek = local_lseek,
383     .writev = local_writev,
384     .chmod = local_chmod,
385     .mknod = local_mknod,
386     .mksock = local_mksock,
387     .mkdir = local_mkdir,
388     .fstat = local_fstat,
389     .open2 = local_open2,
390     .symlink = local_symlink,
391     .link = local_link,
392     .truncate = local_truncate,
393     .rename = local_rename,
394     .chown = local_chown,
395     .utime = local_utime,
396     .remove = local_remove,
397     .fsync = local_fsync,
398 };
This page took 0.047858 seconds and 4 git commands to generate.