File: apparmor-support.c

package info (click to toggle)
snapd 2.49-1%2Bdeb11u2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 36,432 kB
  • sloc: ansic: 12,125; sh: 8,453; python: 2,163; makefile: 1,284; exp: 173; xml: 22
file content (149 lines) | stat: -rw-r--r-- 4,684 bytes parent folder | download
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
}