1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
/*
* Copyright (C) 2016 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "apparmor-support.h"
#include "string-utils.h"
#include "utils.h"
#include <string.h>
#include <errno.h>
#ifdef HAVE_APPARMOR
#include <sys/apparmor.h>
#endif // ifdef HAVE_APPARMOR
#include "../libsnap-confine-private/cleanup-funcs.h"
#include "../libsnap-confine-private/utils.h"
// NOTE: Those constants map exactly what apparmor is returning and cannot be
// changed without breaking apparmor functionality.
#define SC_AA_ENFORCE_STR "enforce"
#define SC_AA_COMPLAIN_STR "complain"
#define SC_AA_MIXED_STR "mixed"
#define SC_AA_UNCONFINED_STR "unconfined"
void sc_init_apparmor_support(struct sc_apparmor *apparmor)
{
#ifdef HAVE_APPARMOR
// Use aa_is_enabled() to see if apparmor is available in the kernel and
// enabled at boot time. If it isn't log a diagnostic message and assume
// we're not confined.
if (aa_is_enabled() != true) {
switch (errno) {
case ENOSYS:
debug
("apparmor extensions to the system are not available");
break;
case ECANCELED:
debug
("apparmor is available on the system but has been disabled at boot");
break;
case EPERM:
// NOTE: fall-through
case EACCES:
debug
("insufficient permissions to determine if apparmor is enabled");
// since snap-confine is setuid root this should
// never happen so likely someone is trying to
// manipulate our execution environment - fail hard
// fall-through
case ENOENT:
case ENOMEM:
default:
// this shouldn't happen under normal usage so it
// is possible someone is trying to manipulate our
// execution environment - fail hard
die("aa_is_enabled() failed unexpectedly (%s)",
strerror(errno));
break;
}
apparmor->is_confined = false;
apparmor->mode = SC_AA_NOT_APPLICABLE;
return;
}
// Use aa_getcon() to check the label of the current process and
// confinement type. Note that the returned label must be released with
// free() but the mode is a constant string that must not be freed.
char *label SC_CLEANUP(sc_cleanup_string) = NULL;
char *mode = NULL;
if (aa_getcon(&label, &mode) < 0) {
die("cannot query current apparmor profile");
}
debug("apparmor label on snap-confine is: %s", label);
debug("apparmor mode is: %s", mode);
// expect to be confined by a profile with the name of a valid
// snap-confine binary since if not we may be executed under a
// profile with more permissions than expected
if (label != NULL && sc_streq(mode, SC_AA_ENFORCE_STR) && sc_is_expected_path(label)) {
apparmor->is_confined = true;
} else {
apparmor->is_confined = false;
}
// There are several possible results for the confinement type (mode) that
// are checked for below.
if (mode != NULL && strcmp(mode, SC_AA_COMPLAIN_STR) == 0) {
apparmor->mode = SC_AA_COMPLAIN;
} else if (mode != NULL && strcmp(mode, SC_AA_ENFORCE_STR) == 0) {
apparmor->mode = SC_AA_ENFORCE;
} else if (mode != NULL && strcmp(mode, SC_AA_MIXED_STR) == 0) {
apparmor->mode = SC_AA_MIXED;
} else {
apparmor->mode = SC_AA_INVALID;
}
#else
apparmor->mode = SC_AA_NOT_APPLICABLE;
apparmor->is_confined = false;
#endif // ifdef HAVE_APPARMOR
}
void
sc_maybe_aa_change_onexec(struct sc_apparmor *apparmor, const char *profile)
{
#ifdef HAVE_APPARMOR
if (apparmor->mode == SC_AA_NOT_APPLICABLE) {
return;
}
debug("requesting changing of apparmor profile on next exec to %s",
profile);
if (aa_change_onexec(profile) < 0) {
if (secure_getenv("SNAPPY_LAUNCHER_INSIDE_TESTS") == NULL) {
die("cannot change profile for the next exec call");
}
}
#endif // ifdef HAVE_APPARMOR
}
void
sc_maybe_aa_change_hat(struct sc_apparmor *apparmor,
const char *subprofile, unsigned long magic_token)
{
#ifdef HAVE_APPARMOR
if (apparmor->mode == SC_AA_NOT_APPLICABLE) {
return;
}
if (apparmor->is_confined) {
debug("changing apparmor hat to %s", subprofile);
if (aa_change_hat(subprofile, magic_token) < 0) {
die("cannot change apparmor hat");
}
}
#endif // ifdef HAVE_APPARMOR
}
|