1 /*
2  * Copyright (C) 2011-2014 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * License along with this library;  If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 #include <config.h>
23 
24 #include <time.h>
25 
26 #include <selinux/selinux.h>
27 #include <selinux/context.h>
28 #include <sys/xattr.h>
29 
30 #include "internal.h"
31 #include "testutils.h"
32 #include "testutilsqemu.h"
33 #include "qemu/qemu_domain.h"
34 #include "viralloc.h"
35 #include "virerror.h"
36 #include "virfile.h"
37 #include "virlog.h"
38 #include "security/security_manager.h"
39 #include "virstring.h"
40 
41 #define VIR_FROM_THIS VIR_FROM_NONE
42 
43 VIR_LOG_INIT("tests.securityselinuxlabeltest");
44 
45 static virQEMUDriver driver;
46 
47 static virSecurityManager *mgr;
48 
49 typedef struct testSELinuxFile testSELinuxFile;
50 
51 struct testSELinuxFile {
52     char *file;
53     char *context;
54 };
55 
56 static int
testUserXattrEnabled(void)57 testUserXattrEnabled(void)
58 {
59     int ret = -1;
60     ssize_t len;
61     const char *con_value = "system_u:object_r:svirt_image_t:s0:c41,c264";
62     g_autofree char *path = NULL;
63     path = g_strdup_printf("%s/securityselinuxlabeldata/testxattr", abs_builddir);
64 
65     if (g_mkdir_with_parents(abs_builddir "/securityselinuxlabeldata", 0777) < 0 ||
66         virFileTouch(path, 0600) < 0)
67         goto cleanup;
68 
69     len = setxattr(path, "user.libvirt.selinux", con_value,
70                    strlen(con_value), 0);
71     if (len < 0) {
72         if (errno == EOPNOTSUPP)
73             ret = 0;
74         goto cleanup;
75     }
76 
77     ret = 1;
78 
79  cleanup:
80     unlink(path);
81     rmdir(abs_builddir "/securityselinuxlabeldata");
82     return ret;
83 }
84 
85 static int
testSELinuxMungePath(char ** path)86 testSELinuxMungePath(char **path)
87 {
88     char *tmp;
89 
90     tmp = g_strdup_printf("%s/securityselinuxlabeldata%s", abs_builddir, *path);
91 
92     VIR_FREE(*path);
93     *path = tmp;
94     return 0;
95 }
96 
97 static int
testSELinuxLoadFileList(const char * testname,testSELinuxFile ** files,size_t * nfiles)98 testSELinuxLoadFileList(const char *testname,
99                         testSELinuxFile **files,
100                         size_t *nfiles)
101 {
102     g_autofree char *path = NULL;
103     g_autoptr(FILE) fp = NULL;
104     g_autofree char *line = NULL;
105 
106     *files = NULL;
107     *nfiles = 0;
108 
109     path = g_strdup_printf("%s/securityselinuxlabeldata/%s.txt", abs_srcdir,
110                            testname);
111 
112     if (!(fp = fopen(path, "r")))
113         return -1;
114 
115     line = g_new0(char, 1024);
116 
117     while (!feof(fp)) {
118         char *file = NULL, *context = NULL, *tmp;
119         if (!fgets(line, 1024, fp)) {
120             if (!feof(fp))
121                 return -1;
122             break;
123         }
124 
125         tmp = strchr(line, ';');
126         if (!tmp) {
127             virReportError(VIR_ERR_INTERNAL_ERROR,
128                            "unexpected format for line '%s'",
129                            line);
130             return -1;
131         }
132         *tmp = '\0';
133         tmp++;
134 
135         file = g_strdup_printf("%s/securityselinuxlabeldata%s", abs_builddir,
136                                line);
137         if (*tmp != '\0' && *tmp != '\n') {
138             context = g_strdup(tmp);
139 
140             tmp = strchr(context, '\n');
141             if (tmp)
142                 *tmp = '\0';
143         }
144 
145         VIR_EXPAND_N(*files, *nfiles, 1);
146         (*files)[(*nfiles)-1].file = file;
147         (*files)[(*nfiles)-1].context = context;
148     }
149 
150     return 0;
151 }
152 
153 
154 static virDomainDef *
testSELinuxLoadDef(const char * testname)155 testSELinuxLoadDef(const char *testname)
156 {
157     char *xmlfile = NULL;
158     virDomainDef *def = NULL;
159     size_t i;
160 
161     xmlfile = g_strdup_printf("%s/securityselinuxlabeldata/%s.xml", abs_srcdir,
162                               testname);
163 
164     if (!(def = virDomainDefParseFile(xmlfile, driver.xmlopt,
165                                       NULL, 0)))
166         goto cleanup;
167 
168     for (i = 0; i < def->ndisks; i++) {
169         if (def->disks[i]->src->type != VIR_STORAGE_TYPE_FILE &&
170             def->disks[i]->src->type != VIR_STORAGE_TYPE_BLOCK)
171             continue;
172 
173         if (testSELinuxMungePath(&def->disks[i]->src->path) < 0)
174             goto cleanup;
175     }
176 
177     for (i = 0; i < def->nserials; i++) {
178         if (def->serials[i]->source->type != VIR_DOMAIN_CHR_TYPE_FILE &&
179             def->serials[i]->source->type != VIR_DOMAIN_CHR_TYPE_PIPE &&
180             def->serials[i]->source->type != VIR_DOMAIN_CHR_TYPE_DEV &&
181             def->serials[i]->source->type != VIR_DOMAIN_CHR_TYPE_UNIX)
182             continue;
183 
184         if (def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX) {
185             if (testSELinuxMungePath(&def->serials[i]->source->data.nix.path) < 0)
186                 goto cleanup;
187         } else {
188             if (testSELinuxMungePath(&def->serials[i]->source->data.file.path) < 0)
189                 goto cleanup;
190         }
191     }
192 
193     if (def->os.kernel &&
194         testSELinuxMungePath(&def->os.kernel) < 0)
195         goto cleanup;
196     if (def->os.initrd &&
197         testSELinuxMungePath(&def->os.initrd) < 0)
198         goto cleanup;
199 
200  cleanup:
201     VIR_FREE(xmlfile);
202     return def;
203 }
204 
205 
206 static int
testSELinuxCreateDisks(testSELinuxFile * files,size_t nfiles)207 testSELinuxCreateDisks(testSELinuxFile *files, size_t nfiles)
208 {
209     size_t i;
210 
211     if (g_mkdir_with_parents(abs_builddir "/securityselinuxlabeldata/nfs", 0777) < 0)
212         return -1;
213 
214     for (i = 0; i < nfiles; i++) {
215         if (virFileTouch(files[i].file, 0600) < 0)
216             return -1;
217     }
218     return 0;
219 }
220 
221 static int
testSELinuxDeleteDisks(testSELinuxFile * files,size_t nfiles)222 testSELinuxDeleteDisks(testSELinuxFile *files, size_t nfiles)
223 {
224     size_t i;
225 
226     for (i = 0; i < nfiles; i++) {
227         if (unlink(files[i].file) < 0)
228             return -1;
229     }
230     if (rmdir(abs_builddir "/securityselinuxlabeldata/nfs") < 0)
231         return -1;
232     /* Ignore failure to remove non-empty directory with in-tree build */
233     rmdir(abs_builddir "/securityselinuxlabeldata");
234     return 0;
235 }
236 
237 static int
testSELinuxCheckLabels(testSELinuxFile * files,size_t nfiles)238 testSELinuxCheckLabels(testSELinuxFile *files, size_t nfiles)
239 {
240     size_t i;
241 
242     for (i = 0; i < nfiles; i++) {
243         g_autofree char *ctx = NULL;
244         if (getfilecon(files[i].file, &ctx) < 0) {
245             if (errno == ENODATA) {
246                 /* nothing to do */
247             } else if (errno == EOPNOTSUPP) {
248                 ctx = g_strdup("EOPNOTSUPP");
249             } else {
250                 virReportSystemError(errno,
251                                      "Cannot read label on %s",
252                                      files[i].file);
253                 return -1;
254             }
255         }
256         if (STRNEQ_NULLABLE(files[i].context, ctx)) {
257             virReportError(VIR_ERR_INTERNAL_ERROR,
258                            "File %s context '%s' did not match expected '%s'",
259                            files[i].file, ctx, files[i].context);
260             return -1;
261         }
262     }
263     return 0;
264 }
265 
266 static int
testSELinuxLabeling(const void * opaque)267 testSELinuxLabeling(const void *opaque)
268 {
269     const char *testname = opaque;
270     int ret = -1;
271     testSELinuxFile *files = NULL;
272     size_t nfiles = 0;
273     size_t i;
274     g_autoptr(virDomainDef) def = NULL;
275 
276     if (testSELinuxLoadFileList(testname, &files, &nfiles) < 0)
277         goto cleanup;
278 
279     if (testSELinuxCreateDisks(files, nfiles) < 0)
280         goto cleanup;
281 
282     if (!(def = testSELinuxLoadDef(testname)))
283         goto cleanup;
284 
285     if (virSecurityManagerSetAllLabel(mgr, def, NULL, false, false) < 0)
286         goto cleanup;
287 
288     if (testSELinuxCheckLabels(files, nfiles) < 0)
289         goto cleanup;
290 
291     ret = 0;
292 
293  cleanup:
294     if (testSELinuxDeleteDisks(files, nfiles) < 0)
295         VIR_WARN("unable to fully clean up");
296 
297     for (i = 0; i < nfiles; i++) {
298         VIR_FREE(files[i].file);
299         VIR_FREE(files[i].context);
300     }
301     VIR_FREE(files);
302     if (ret < 0)
303         VIR_TEST_VERBOSE("%s", virGetLastErrorMessage());
304     return ret;
305 }
306 
307 
308 
309 static int
mymain(void)310 mymain(void)
311 {
312     int ret = 0;
313     int rc = testUserXattrEnabled();
314     g_autoptr(virQEMUCaps) qemuCaps = NULL;
315 
316     if (rc < 0) {
317         VIR_TEST_VERBOSE("failed to determine xattr support");
318         return EXIT_FAILURE;
319     }
320 
321     if (rc == 0) {
322         VIR_TEST_VERBOSE("xattr unsupported");
323         return EXIT_AM_SKIP;
324     }
325 
326     if (!(mgr = virSecurityManagerNew("selinux", "QEMU",
327                                       VIR_SECURITY_MANAGER_DEFAULT_CONFINED |
328                                       VIR_SECURITY_MANAGER_PRIVILEGED))) {
329         VIR_TEST_VERBOSE("Unable to initialize security driver: %s",
330                          virGetLastErrorMessage());
331         return EXIT_FAILURE;
332     }
333 
334     if (qemuTestDriverInit(&driver) < 0)
335         return EXIT_FAILURE;
336 
337     if (!(qemuCaps = virQEMUCapsNew()))
338         return EXIT_FAILURE;
339 
340     virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA);
341     virQEMUCapsSet(qemuCaps, QEMU_CAPS_VNC);
342 
343     if (qemuTestCapsCacheInsert(driver.qemuCapsCache, qemuCaps) < 0)
344         return EXIT_FAILURE;
345 
346 #define DO_TEST_LABELING(name) \
347     if (virTestRun("Labelling " # name, testSELinuxLabeling, name) < 0) \
348         ret = -1;
349 
350     setcon("system_r:system_u:libvirtd_t:s0:c0.c1023");
351 
352     DO_TEST_LABELING("disks");
353     DO_TEST_LABELING("kernel");
354     DO_TEST_LABELING("chardev");
355     DO_TEST_LABELING("nfs");
356 
357     qemuTestDriverFree(&driver);
358 
359     return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
360 }
361 
362 VIR_TEST_MAIN_PRELOAD(mymain,
363                       VIR_TEST_MOCK("domaincaps"),
364                       abs_builddir "/libsecurityselinuxhelper.so")
365