]> xenbits.xensource.com Git - libvirt.git/blob
16de0f26f41689e0c50481120d9f8a59ba1f4073
[libvirt.git] /
1
2 /*
3  * AppArmor security driver for libvirt
4  * Copyright (C) 2009 Canonical Ltd.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * Author:
12  *   Jamie Strandboge <jamie@canonical.com>
13  *   Based on security_selinux.c by James Morris <jmorris@namei.org>
14  *
15  * AppArmor security driver.
16  */
17
18 #include <config.h>
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <sys/apparmor.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <wait.h>
27 #include <stdbool.h>
28
29 #include "internal.h"
30
31 #include "security_driver.h"
32 #include "security_apparmor.h"
33 #include "util.h"
34 #include "memory.h"
35 #include "virterror_internal.h"
36 #include "datatypes.h"
37 #include "uuid.h"
38
39 #define VIR_FROM_THIS VIR_FROM_SECURITY
40 #define SECURITY_APPARMOR_VOID_DOI      "0"
41 #define SECURITY_APPARMOR_NAME          "apparmor"
42 #define VIRT_AA_HELPER BINDIR "/virt-aa-helper"
43
44 /*
45  * profile_status returns '-1' on error, '0' if loaded
46  *
47  * If check_enforcing is set to '1', then returns '-1' on error, '0' if
48  * loaded in complain mode, and '1' if loaded in enforcing mode.
49  */
50 static int
51 profile_status(const char *str, const int check_enforcing)
52 {
53     char *content = NULL;
54     char *tmp = NULL;
55     char *etmp = NULL;
56     int rc = -1;
57
58     /* create string that is '<str> \0' for accurate matching */
59     if (virAsprintf(&tmp, "%s ", str) == -1)
60         return rc;
61
62     if (check_enforcing != 0) {
63         /* create string that is '<str> (enforce)\0' for accurate matching */
64         if (virAsprintf(&etmp, "%s (enforce)", str) == -1) {
65             VIR_FREE(tmp);
66             return rc;
67         }
68     }
69
70     if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
71         virReportSystemError(NULL, errno,
72                              _("Failed to read AppArmor profiles list "
73                              "\'%s\'"), APPARMOR_PROFILES_PATH);
74         if (check_enforcing != 0)
75             VIR_FREE(etmp);
76         goto clean;
77     }
78
79     if (strstr(content, tmp) != NULL)
80         rc = 0;
81     if (check_enforcing != 0) {
82         if (rc == 0 && strstr(content, etmp) != NULL)
83             rc = 1;                 /* return '1' if loaded and enforcing */
84         VIR_FREE(etmp);
85     }
86
87     VIR_FREE(content);
88   clean:
89     VIR_FREE(tmp);
90
91     return rc;
92 }
93
94 static int
95 profile_loaded(const char *str)
96 {
97     return profile_status(str, 0);
98 }
99
100 /*
101  * profile_status_file returns '-1' on error, '0' if file on disk is in
102  * complain mode and '1' if file on disk is in enforcing mode
103  */
104 static int
105 profile_status_file(const char *str)
106 {
107     char profile[PATH_MAX];
108     char *content = NULL;
109     char *tmp = NULL;
110     int rc = -1;
111     int len;
112
113     if (snprintf(profile, PATH_MAX, "%s/%s", APPARMOR_DIR "/libvirt", str)
114        > PATH_MAX - 1) {
115         virSecurityReportError(NULL, VIR_ERR_ERROR,
116                                "%s", _("profile name exceeds maximum length"));
117     }
118
119     if (!virFileExists(profile)) {
120         return rc;
121     }
122
123     if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
124         virReportSystemError(NULL, errno,
125                              _("Failed to read \'%s\'"), profile);
126         return rc;
127     }
128
129     /* create string that is ' <str> flags=(complain)\0' */
130     if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
131         virReportOOMError(NULL);
132         goto clean;
133     }
134
135     if (strstr(content, tmp) != NULL)
136         rc = 0;
137     else
138         rc = 1;
139
140     VIR_FREE(tmp);
141   clean:
142     VIR_FREE(content);
143
144     return rc;
145 }
146
147 /*
148  * load (add) a profile. Will create one if necessary
149  */
150 static int
151 load_profile(virConnectPtr conn, const char *profile, virDomainObjPtr vm,
152              virDomainDiskDefPtr disk)
153 {
154     int rc = -1, status, ret;
155     bool create = true;
156     char *xml = NULL;
157     int pipefd[2];
158     pid_t child;
159
160     if (pipe(pipefd) < -1) {
161         virReportSystemError(conn, errno, "%s", _("unable to create pipe"));
162         return rc;
163     }
164
165     xml = virDomainDefFormat(conn, vm->def, VIR_DOMAIN_XML_SECURE);
166     if (!xml)
167         goto failed;
168
169     if (profile_status_file(profile) >= 0)
170         create = false;
171
172     if (create) {
173         const char *const argv[] = {
174             VIRT_AA_HELPER, "-c", "-u", profile, NULL
175         };
176         ret = virExec(conn, argv, NULL, NULL, &child,
177                       pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
178     } else if (disk && disk->src) {
179         const char *const argv[] = {
180             VIRT_AA_HELPER, "-r", "-u", profile, "-f", disk->src, NULL
181         };
182         ret = virExec(conn, argv, NULL, NULL, &child,
183                       pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
184     } else {
185         const char *const argv[] = {
186             VIRT_AA_HELPER, "-r", "-u", profile, NULL
187         };
188         ret = virExec(conn, argv, NULL, NULL, &child,
189                       pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
190     }
191     if (ret < 0)
192         goto clean;
193
194     /* parent continues here */
195     if (safewrite(pipefd[1], xml, strlen(xml)) < 0) {
196         virReportSystemError(conn, errno, "%s", _("unable to write to pipe"));
197         goto clean;
198     }
199     close(pipefd[1]);
200     rc = 0;
201
202   rewait:
203     if (waitpid(child, &status, 0) != child) {
204         if (errno == EINTR)
205             goto rewait;
206
207         virSecurityReportError(conn, VIR_ERR_ERROR,
208                                _("Unexpected exit status from virt-aa-helper "
209                                "%d pid %lu"),
210                                WEXITSTATUS(status), (unsigned long)child);
211         rc = -1;
212     }
213
214   clean:
215     VIR_FREE(xml);
216
217   failed:
218     if (pipefd[0] > 0)
219         close(pipefd[0]);
220     if (pipefd[1] > 0)
221         close(pipefd[1]);
222
223     return rc;
224 }
225
226 static int
227 remove_profile(const char *profile)
228 {
229     int rc = -1;
230     const char * const argv[] = {
231         VIRT_AA_HELPER, "-R", "-u", profile, NULL
232     };
233
234     if (virRun(NULL, argv, NULL) == 0)
235         rc = 0;
236
237     return rc;
238 }
239
240 static char *
241 get_profile_name(virConnectPtr conn, virDomainObjPtr vm)
242 {
243     char uuidstr[VIR_UUID_STRING_BUFLEN];
244     char *name = NULL;
245
246     virUUIDFormat(vm->def->uuid, uuidstr);
247     if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
248         virReportOOMError(conn);
249         return NULL;
250     }
251
252     return name;
253 }
254
255 /* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
256  * mode and 1 if enforcing. This is required because at present you cannot
257  * aa_change_profile() from a process that is unconfined.
258  */
259 static int
260 use_apparmor(void)
261 {
262     char libvirt_daemon[PATH_MAX];
263     int rc = -1;
264     ssize_t len = 0;
265
266     if ((len = readlink("/proc/self/exe", libvirt_daemon,
267                         PATH_MAX - 1)) < 0) {
268         virSecurityReportError(NULL, VIR_ERR_ERROR,
269                                "%s", _("could not find libvirtd"));
270         return rc;
271     }
272     libvirt_daemon[len] = '\0';
273
274     if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
275         return rc;
276
277     return profile_status(libvirt_daemon, 1);
278 }
279
280 /* Called on libvirtd startup to see if AppArmor is available */
281 static int
282 AppArmorSecurityDriverProbe(void)
283 {
284     char template[PATH_MAX];
285
286     if (use_apparmor() < 0)
287         return SECURITY_DRIVER_DISABLE;
288
289     /* see if template file exists */
290     if (snprintf(template, PATH_MAX, "%s/TEMPLATE",
291                  APPARMOR_DIR "/libvirt") > PATH_MAX - 1) {
292         virSecurityReportError(NULL, VIR_ERR_ERROR,
293                                "%s", _("template too large"));
294         return SECURITY_DRIVER_DISABLE;
295     }
296
297     if (!virFileExists(template)) {
298         virSecurityReportError(NULL, VIR_ERR_ERROR,
299                                _("template \'%s\' does not exist"), template);
300         return SECURITY_DRIVER_DISABLE;
301     }
302
303     return SECURITY_DRIVER_ENABLE;
304 }
305
306 /* Security driver initialization. DOI is for 'Domain of Interpretation' and is
307  * currently not used.
308  */
309 static int
310 AppArmorSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv)
311 {
312     virSecurityDriverSetDOI(conn, drv, SECURITY_APPARMOR_VOID_DOI);
313     return 0;
314 }
315
316 /* Currently called in qemudStartVMDaemon to setup a 'label'. We look for and
317  * use a profile based on the UUID, otherwise create one based on a template.
318  * Keep in mind that this is called on 'start' with RestoreSecurityLabel being
319  * called on shutdown.
320 */
321 static int
322 AppArmorGenSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
323 {
324     int rc = -1;
325     char *profile_name = NULL;
326
327     if ((vm->def->seclabel.label) ||
328         (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
329         virSecurityReportError(conn, VIR_ERR_ERROR,
330                                "%s",
331                                _("security label already defined for VM"));
332         return rc;
333     }
334
335     if ((profile_name = get_profile_name(conn, vm)) == NULL)
336         return rc;
337
338     /* if the profile is not already loaded, then load one */
339     if (profile_loaded(profile_name) < 0) {
340         if (load_profile(conn, profile_name, vm, NULL) < 0) {
341             virSecurityReportError(conn, VIR_ERR_ERROR,
342                                    _("cannot generate AppArmor profile "
343                                    "\'%s\'"), profile_name);
344             goto clean;
345         }
346     }
347
348     vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
349     if (!vm->def->seclabel.label) {
350         virReportOOMError(NULL);
351         goto clean;
352     }
353
354     /* set imagelabel the same as label (but we won't use it) */
355     vm->def->seclabel.imagelabel = strndup(profile_name,
356                                            strlen(profile_name));
357     if (!vm->def->seclabel.imagelabel) {
358         virReportOOMError(NULL);
359         goto err;
360     }
361
362     vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
363     if (!vm->def->seclabel.model) {
364         virReportOOMError(conn);
365         goto err;
366     }
367
368     rc = 0;
369     goto clean;
370
371   err:
372     remove_profile(profile_name);
373     VIR_FREE(vm->def->seclabel.label);
374     VIR_FREE(vm->def->seclabel.imagelabel);
375     VIR_FREE(vm->def->seclabel.model);
376
377   clean:
378     VIR_FREE(profile_name);
379
380     return rc;
381 }
382
383 /* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
384  * running.
385  */
386 static int
387 AppArmorGetSecurityLabel(virConnectPtr conn,
388                          virDomainObjPtr vm, virSecurityLabelPtr sec)
389 {
390     int rc = -1;
391     char *profile_name = NULL;
392
393     if ((profile_name = get_profile_name(conn, vm)) == NULL)
394         return rc;
395
396     if (virStrcpy(sec->label, profile_name,
397         VIR_SECURITY_LABEL_BUFLEN) == NULL) {
398         virSecurityReportError(conn, VIR_ERR_ERROR,
399                                "%s", _("error copying profile name"));
400         goto clean;
401     }
402
403     if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
404         virSecurityReportError(conn, VIR_ERR_ERROR,
405                                "%s", _("error calling profile_status()"));
406         goto clean;
407     }
408     rc = 0;
409
410   clean:
411     VIR_FREE(profile_name);
412
413     return rc;
414 }
415
416 /* Called on VM shutdown and destroy. See AppArmorGenSecurityLabel (above) for
417  * more details. Currently called via qemudShutdownVMDaemon.
418  */
419 static int
420 AppArmorRestoreSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
421 {
422     const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
423     int rc = 0;
424
425     if (secdef->imagelabel) {
426         if ((rc = remove_profile(secdef->label)) != 0) {
427             virSecurityReportError(conn, VIR_ERR_ERROR,
428                                    _("could not remove profile for \'%s\'"),
429                                    secdef->label);
430         }
431         VIR_FREE(secdef->model);
432         VIR_FREE(secdef->label);
433         VIR_FREE(secdef->imagelabel);
434     }
435     return rc;
436 }
437
438 /* Called via virExecWithHook. Output goes to
439  * LOCAL_STATE_DIR/log/libvirt/qemu/<vm name>.log
440  */
441 static int
442 AppArmorSetSecurityLabel(virConnectPtr conn,
443                          virSecurityDriverPtr drv, virDomainObjPtr vm)
444 {
445     const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
446     int rc = -1;
447     char *profile_name = NULL;
448
449     if ((profile_name = get_profile_name(conn, vm)) == NULL)
450         return rc;
451
452     if (STRNEQ(drv->name, secdef->model)) {
453         virSecurityReportError(conn, VIR_ERR_ERROR,
454                                _("security label driver mismatch: "
455                                "\'%s\' model configured for domain, but "
456                                "hypervisor driver is \'%s\'."),
457                                secdef->model, drv->name);
458         if (use_apparmor() > 0)
459             goto clean;
460     }
461
462     if (aa_change_profile(profile_name) < 0) {
463         virSecurityReportError(conn, VIR_ERR_ERROR,
464                                _("error calling aa_change_profile()"));
465         goto clean;
466     }
467     rc = 0;
468
469   clean:
470     VIR_FREE(profile_name);
471
472     return rc;
473 }
474
475
476 /* Called when hotplugging */
477 static int
478 AppArmorRestoreSecurityImageLabel(virConnectPtr conn,
479                                   virDomainObjPtr vm,
480                                   virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
481 {
482     const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
483     int rc = -1;
484     char *profile_name = NULL;
485
486     if (secdef->imagelabel) {
487         if ((profile_name = get_profile_name(conn, vm)) == NULL)
488             return rc;
489
490         /* Update the profile only if it is loaded */
491         if (profile_loaded(secdef->imagelabel) >= 0) {
492             if (load_profile(conn, secdef->imagelabel, vm, NULL) < 0) {
493                 virSecurityReportError(conn, VIR_ERR_ERROR,
494                                        _("cannot update AppArmor profile "
495                                        "\'%s\'"),
496                                        secdef->imagelabel);
497                 goto clean;
498             }
499         }
500     }
501     rc = 0;
502   clean:
503     VIR_FREE(profile_name);
504
505     return rc;
506 }
507
508 /* Called when hotplugging */
509 static int
510 AppArmorSetSecurityImageLabel(virConnectPtr conn,
511                               virDomainObjPtr vm, virDomainDiskDefPtr disk)
512 {
513     const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
514     int rc = -1;
515     char *profile_name;
516
517     if (!disk->src)
518         return 0;
519
520     if (secdef->imagelabel) {
521         /* if the device doesn't exist, error out */
522         if (!virFileExists(disk->src)) {
523             virSecurityReportError(conn, VIR_ERR_ERROR,
524                                    _("\'%s\' does not exist"), disk->src);
525             return rc;
526         }
527
528         if ((profile_name = get_profile_name(conn, vm)) == NULL)
529             return rc;
530
531         /* update the profile only if it is loaded */
532         if (profile_loaded(secdef->imagelabel) >= 0) {
533             if (load_profile(conn, secdef->imagelabel, vm, disk) < 0) {
534                 virSecurityReportError(conn, VIR_ERR_ERROR,
535                                      _("cannot update AppArmor profile "
536                                      "\'%s\'"),
537                                      secdef->imagelabel);
538                 goto clean;
539             }
540         }
541     }
542     rc = 0;
543
544   clean:
545     VIR_FREE(profile_name);
546
547     return rc;
548 }
549
550 static int
551 AppArmorSecurityVerify(virConnectPtr conn, virDomainDefPtr def)
552 {
553     const virSecurityLabelDefPtr secdef = &def->seclabel;
554
555     if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
556         if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
557             virSecurityReportError(conn, VIR_ERR_XML_ERROR,
558                                    _("Invalid security label \'%s\'"),
559                                    secdef->label);
560             return -1;
561         }
562     }
563     return 0;
564 }
565
566 static int
567 AppArmorReserveSecurityLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
568                             virDomainObjPtr vm ATTRIBUTE_UNUSED)
569 {
570     /* NOOP. Nothing to reserve with AppArmor */
571     return 0;
572 }
573
574 static int
575 AppArmorSetSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
576                                 virDomainObjPtr vm ATTRIBUTE_UNUSED,
577                                 virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
578
579 {
580     /* TODO: call load_profile with an update vm->def */
581     return 0;
582 }
583
584 static int
585 AppArmorRestoreSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
586                                     virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
587
588 {
589     /* TODO: call load_profile (needs virDomainObjPtr vm) */
590     return 0;
591 }
592
593 virSecurityDriver virAppArmorSecurityDriver = {
594     .name = SECURITY_APPARMOR_NAME,
595     .probe = AppArmorSecurityDriverProbe,
596     .open = AppArmorSecurityDriverOpen,
597     .domainSecurityVerify = AppArmorSecurityVerify,
598     .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
599     .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
600     .domainGenSecurityLabel = AppArmorGenSecurityLabel,
601     .domainReserveSecurityLabel = AppArmorReserveSecurityLabel,
602     .domainGetSecurityLabel = AppArmorGetSecurityLabel,
603     .domainRestoreSecurityLabel = AppArmorRestoreSecurityLabel,
604     .domainSetSecurityLabel = AppArmorSetSecurityLabel,
605     .domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel,
606     .domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel,
607 };