| 1 | |
| 2 | |
| 3 | * AppArmor security module
|
| 4 | *
|
| 5 | * This file contains AppArmor network mediation
|
| 6 | *
|
| 7 | * Copyright (C) 1998-2008 Novell/SUSE
|
| 8 | * Copyright 2009-2017 Canonical Ltd.
|
| 9 |
|
| 10 |
|
| 11 | #include "include/af_unix.h"
|
| 12 | #include "include/af_inet.h"
|
| 13 | #include "include/apparmor.h"
|
| 14 | #include "include/audit.h"
|
| 15 | #include "include/cred.h"
|
| 16 | #include "include/label.h"
|
| 17 | #include "include/net.h"
|
| 18 | #include "include/policy.h"
|
| 19 | #include "include/secid.h"
|
| 20 |
|
| 21 | #include "net_names.h"
|
| 22 |
|
| 23 |
|
| 24 | struct aa_sfs_entry aa_sfs_entry_network[] = {
|
| 25 | AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
|
| 26 | AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
|
| 27 | { }
|
| 28 | };
|
| 29 |
|
| 30 | struct aa_sfs_entry aa_sfs_entry_networkv9[] = {
|
| 31 | AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
|
| 32 | AA_SFS_FILE_BOOLEAN("af_unix", 1),
|
| 33 | AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
|
| 34 | { }
|
| 35 | };
|
| 36 |
|
| 37 | static const char * const net_mask_names[] = {
|
| 38 | "unknown",
|
| 39 | "send",
|
| 40 | "receive",
|
| 41 | "unknown",
|
| 42 |
|
| 43 | "create",
|
| 44 | "shutdown",
|
| 45 | "connect",
|
| 46 | "unknown",
|
| 47 |
|
| 48 | "setattr",
|
| 49 | "getattr",
|
| 50 | "setcred",
|
| 51 | "getcred",
|
| 52 |
|
| 53 | "chmod",
|
| 54 | "chown",
|
| 55 | "chgrp",
|
| 56 | "lock",
|
| 57 |
|
| 58 | "mmap",
|
| 59 | "mprot",
|
| 60 | "unknown",
|
| 61 | "unknown",
|
| 62 |
|
| 63 | "accept",
|
| 64 | "bind",
|
| 65 | "listen",
|
| 66 | "unknown",
|
| 67 |
|
| 68 | "setopt",
|
| 69 | "getopt",
|
| 70 | "unknown",
|
| 71 | "unknown",
|
| 72 |
|
| 73 | "unknown",
|
| 74 | "unknown",
|
| 75 | "unknown",
|
| 76 | "unknown",
|
| 77 | };
|
| 78 |
|
| 79 | static void audit_unix_addr(struct audit_buffer *ab, const char *str,
|
| 80 | struct sockaddr_un *addr, int addrlen)
|
| 81 | {
|
| 82 | int len = unix_addr_len(addrlen);
|
| 83 |
|
| 84 | if (!addr || len <= 0) {
|
| 85 | audit_log_format(ab, " %s=none", str);
|
| 86 | } else if (addr->sun_path[0]) {
|
| 87 | audit_log_format(ab, " %s=", str);
|
| 88 | audit_log_untrustedstring(ab, addr->sun_path);
|
| 89 | } else {
|
| 90 | audit_log_format(ab, " %s=\"@", str);
|
| 91 | if (audit_string_contains_control(&addr->sun_path[1], len - 1))
|
| 92 | audit_log_n_hex(ab, &addr->sun_path[1], len - 1);
|
| 93 | else
|
| 94 | audit_log_format(ab, "%.*s", len - 1,
|
| 95 | &addr->sun_path[1]);
|
| 96 | audit_log_format(ab, "\"");
|
| 97 | }
|
| 98 | }
|
| 99 |
|
| 100 | static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str,
|
| 101 | const struct sock *sk)
|
| 102 | {
|
| 103 | const struct unix_sock *u = unix_sk(sk);
|
| 104 |
|
| 105 | if (u && u->addr) {
|
| 106 | int addrlen;
|
| 107 | struct sockaddr_un *addr = aa_sunaddr(u, &addrlen);
|
| 108 |
|
| 109 | audit_unix_addr(ab, str, addr, addrlen);
|
| 110 | } else {
|
| 111 | audit_unix_addr(ab, str, NULL, 0);
|
| 112 |
|
| 113 | }
|
| 114 | }
|
| 115 |
|
| 116 |
|
| 117 | void audit_net_cb(struct audit_buffer *ab, void *va)
|
| 118 | {
|
| 119 | struct common_audit_data *sa = va;
|
| 120 | struct apparmor_audit_data *ad = aad(sa);
|
| 121 |
|
| 122 | if (address_family_names[ad->common.u.net->family])
|
| 123 | audit_log_format(ab, " family=\"%s\"",
|
| 124 | address_family_names[ad->common.u.net->family]);
|
| 125 | else
|
| 126 | audit_log_format(ab, " family=\"unknown(%d)\"",
|
| 127 | ad->common.u.net->family);
|
| 128 | if (sock_type_names[ad->net.type])
|
| 129 | audit_log_format(ab, " sock_type=\"%s\"",
|
| 130 | sock_type_names[ad->net.type]);
|
| 131 | else
|
| 132 | audit_log_format(ab, " sock_type=\"unknown(%d)\"",
|
| 133 | ad->net.type);
|
| 134 | audit_log_format(ab, " protocol=%d", ad->net.protocol);
|
| 135 |
|
| 136 | if (ad->request & NET_PERMS_MASK) {
|
| 137 | audit_log_format(ab, " requested=");
|
| 138 | aa_audit_perm_mask(ab, ad->request, NULL, 0,
|
| 139 | net_mask_names, NET_PERMS_MASK);
|
| 140 |
|
| 141 | if (ad->denied & NET_PERMS_MASK) {
|
| 142 | audit_log_format(ab, " denied=");
|
| 143 | aa_audit_perm_mask(ab, ad->denied, NULL, 0,
|
| 144 | net_mask_names, NET_PERMS_MASK);
|
| 145 | }
|
| 146 | }
|
| 147 | if (ad->common.u.net->family == PF_UNIX) {
|
| 148 | if (ad->net.addr || !ad->common.u.net->sk)
|
| 149 | audit_unix_addr(ab, "addr",
|
| 150 | unix_addr(ad->net.addr),
|
| 151 | ad->net.addrlen);
|
| 152 | else
|
| 153 | audit_unix_sk_addr(ab, "addr", ad->common.u.net->sk);
|
| 154 | if (ad->request & NET_PEER_MASK) {
|
| 155 | audit_unix_addr(ab, "peer_addr",
|
| 156 | unix_addr(ad->net.peer.addr),
|
| 157 | ad->net.peer.addrlen);
|
| 158 | }
|
| 159 | }
|
| 160 | if (ad->peer) {
|
| 161 | audit_log_format(ab, " peer=");
|
| 162 | aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
|
| 163 | FLAGS_NONE, GFP_ATOMIC);
|
| 164 | }
|
| 165 | }
|
| 166 |
|
| 167 |
|
| 168 | int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy,
|
| 169 | aa_state_t state, u32 request,
|
| 170 | const struct aa_perms *p, struct apparmor_audit_data *ad)
|
| 171 | {
|
| 172 | struct aa_perms perms;
|
| 173 |
|
| 174 | AA_BUG(!profile);
|
| 175 | AA_BUG(!policy);
|
| 176 |
|
| 177 |
|
| 178 | if (state || !p)
|
| 179 | p = aa_lookup_perms(policy, state);
|
| 180 | perms = *p;
|
| 181 | aa_apply_modes_to_perms(profile, &perms);
|
| 182 | return aa_check_perms(profile, &perms, request, ad,
|
| 183 | audit_net_cb);
|
| 184 | }
|
| 185 |
|
| 186 | |
| 187 | * insufficient current perms at current state
|
| 188 | * indicates there are more perms in later state
|
| 189 | * Returns: perms struct if early match
|
| 190 |
|
| 191 | static struct aa_perms *early_match(struct aa_policydb *policy,
|
| 192 | aa_state_t state, u32 request)
|
| 193 | {
|
| 194 | struct aa_perms *p;
|
| 195 |
|
| 196 | p = aa_lookup_perms(policy, state);
|
| 197 | if (((p->allow & request) != request) && (p->allow & AA_CONT_MATCH))
|
| 198 | return NULL;
|
| 199 | return p;
|
| 200 | }
|
| 201 |
|
| 202 | static aa_state_t aa_dfa_match_be16(const struct aa_dfa *dfa, aa_state_t state,
|
| 203 | u16 data)
|
| 204 | {
|
| 205 | __be16 buffer = cpu_to_be16(data);
|
| 206 |
|
| 207 | return aa_dfa_match_len(dfa, state, (char *) &buffer, 2);
|
| 208 | }
|
| 209 |
|
| 210 | |
| 211 | * aa_match_to_prot - match the af, type, protocol triplet
|
| 212 | * @policy: policy being matched
|
| 213 | * @state: state to start in
|
| 214 | * @request: permissions being requested, ignored if @p == NULL
|
| 215 | * @af: socket address family
|
| 216 | * @type: socket type
|
| 217 | * @protocol: socket protocol
|
| 218 | * @p: output - pointer to permission associated with match
|
| 219 | * @info: output - pointer to string describing failure
|
| 220 | *
|
| 221 | * RETURNS: state match stopped in.
|
| 222 | *
|
| 223 | * If @(p) is assigned a value the returned state will be the
|
| 224 | * corresponding state. Will not set @p on failure or if match completes
|
| 225 | * only if an early match occurs
|
| 226 |
|
| 227 | aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state,
|
| 228 | u32 request, u16 af, int type, int protocol,
|
| 229 | struct aa_perms **p, const char **info)
|
| 230 | {
|
| 231 | state = aa_dfa_match_be16(policy->dfa, state, (u16)af);
|
| 232 | if (!state) {
|
| 233 | *info = "failed af match";
|
| 234 | return state;
|
| 235 | }
|
| 236 | state = aa_dfa_match_be16(policy->dfa, state, (u16)type);
|
| 237 | if (state) {
|
| 238 | if (p)
|
| 239 | *p = early_match(policy, state, request);
|
| 240 | if (!p || !*p) {
|
| 241 | state = aa_dfa_match_be16(policy->dfa, state, (u16)protocol);
|
| 242 | if (!state)
|
| 243 | *info = "failed protocol match";
|
| 244 | }
|
| 245 | } else {
|
| 246 | *info = "failed type match";
|
| 247 | }
|
| 248 |
|
| 249 | return state;
|
| 250 | }
|
| 251 |
|
| 252 |
|
| 253 | int aa_profile_af_perm(struct aa_profile *profile,
|
| 254 | struct apparmor_audit_data *ad, u32 request, u16 family,
|
| 255 | int type, int protocol)
|
| 256 | {
|
| 257 | struct aa_ruleset *rules = profile->label.rules[0];
|
| 258 | struct aa_perms *p = NULL;
|
| 259 | aa_state_t state;
|
| 260 |
|
| 261 | AA_BUG(family >= AF_MAX);
|
| 262 | AA_BUG(type < 0 || type >= SOCK_MAX);
|
| 263 | AA_BUG(profile_unconfined(profile));
|
| 264 |
|
| 265 | state = RULE_MEDIATES_NET(rules);
|
| 266 | if (state) {
|
| 267 | state = aa_match_to_prot(rules->policy, state, request, family,
|
| 268 | type, protocol, &p, &ad->info);
|
| 269 | return aa_do_perms(profile, rules->policy, state, request, p,
|
| 270 | ad);
|
| 271 | }
|
| 272 |
|
| 273 | return 0;
|
| 274 | }
|
| 275 |
|
| 276 | int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
|
| 277 | const char *op, u32 request, u16 family, int type, int protocol)
|
| 278 | {
|
| 279 | struct aa_profile *profile;
|
| 280 | DEFINE_AUDIT_NET(ad, op, subj_cred, NULL, family, type, protocol);
|
| 281 |
|
| 282 | return fn_for_each_confined(label, profile,
|
| 283 | aa_profile_af_perm(profile, &ad, request, family,
|
| 284 | type, protocol));
|
| 285 | }
|
| 286 |
|
| 287 | int aa_label_sk_perm(const struct cred *subj_cred, struct aa_label *label,
|
| 288 | const char *op, u32 request, const struct sock *sk)
|
| 289 | {
|
| 290 | struct aa_sk_ctx *ctx = aa_sock(sk);
|
| 291 | int error = 0;
|
| 292 |
|
| 293 | AA_BUG(!label);
|
| 294 | AA_BUG(!sk);
|
| 295 |
|
| 296 | if (rcu_access_pointer(ctx->label) != kernel_t && !unconfined(label)) {
|
| 297 | struct aa_profile *profile;
|
| 298 | DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
|
| 299 |
|
| 300 | ad.subj_cred = subj_cred;
|
| 301 | error = fn_for_each_confined(label, profile,
|
| 302 | aa_profile_af_perm(profile, &ad, request, sk->sk_family,
|
| 303 | sk->sk_type, sk->sk_protocol));
|
| 304 | }
|
| 305 |
|
| 306 | return error;
|
| 307 | }
|
| 308 |
|
| 309 | int aa_sk_perm(const char *op, u32 request, const struct sock *sk)
|
| 310 | {
|
| 311 | struct aa_label *label;
|
| 312 | bool needput;
|
| 313 | int error;
|
| 314 |
|
| 315 | AA_BUG(!sk);
|
| 316 | AA_BUG(in_interrupt());
|
| 317 |
|
| 318 |
|
| 319 | label = begin_current_label_crit_section(&needput);
|
| 320 | error = aa_label_sk_perm(current_cred(), label, op, request, sk);
|
| 321 | end_current_label_crit_section(label, needput);
|
| 322 |
|
| 323 | return error;
|
| 324 | }
|
| 325 |
|
| 326 |
|
| 327 | int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
|
| 328 | const char *op, u32 request, struct file *file)
|
| 329 | {
|
| 330 | struct socket *sock = (struct socket *) file->private_data;
|
| 331 |
|
| 332 | AA_BUG(!label);
|
| 333 |
|
| 334 |
|
| 335 | if (!sock || !sock->sk)
|
| 336 | return 0;
|
| 337 |
|
| 338 | switch (sock->sk->sk_family) {
|
| 339 | case PF_UNIX:
|
| 340 | return aa_unix_file_perm(subj_cred, label, op, request, file);
|
| 341 | case PF_INET:
|
| 342 | case PF_INET6:
|
| 343 | return aa_inet_file_perm(subj_cred, label, op, request, sock);
|
| 344 | }
|
| 345 | return aa_label_sk_perm(subj_cred, label, op, request, sock->sk);
|
| 346 | }
|
| 347 |
|
| 348 | #ifdef CONFIG_NETWORK_SECMARK
|
| 349 | static int apparmor_secmark_init(struct aa_secmark *secmark)
|
| 350 | {
|
| 351 | struct aa_label *label;
|
| 352 |
|
| 353 | if (secmark->label[0] == '*') {
|
| 354 | secmark->secid = AA_SECID_WILDCARD;
|
| 355 | return 0;
|
| 356 | }
|
| 357 |
|
| 358 | label = aa_label_strn_parse(&root_ns->unconfined->label,
|
| 359 | secmark->label, strlen(secmark->label),
|
| 360 | GFP_ATOMIC, false, false);
|
| 361 |
|
| 362 | if (IS_ERR(label))
|
| 363 | return PTR_ERR(label);
|
| 364 |
|
| 365 | secmark->secid = label->secid;
|
| 366 | aa_put_label(label);
|
| 367 |
|
| 368 | return 0;
|
| 369 | }
|
| 370 |
|
| 371 | static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
|
| 372 | struct apparmor_audit_data *ad)
|
| 373 | {
|
| 374 | int i, ret;
|
| 375 | struct aa_perms perms = { };
|
| 376 | struct aa_ruleset *rules = profile->label.rules[0];
|
| 377 |
|
| 378 | if (rules->secmark_count == 0)
|
| 379 | return 0;
|
| 380 |
|
| 381 | for (i = 0; i < rules->secmark_count; i++) {
|
| 382 | if (!rules->secmark[i].secid) {
|
| 383 | ret = apparmor_secmark_init(&rules->secmark[i]);
|
| 384 | if (ret)
|
| 385 | return ret;
|
| 386 | }
|
| 387 |
|
| 388 | if (rules->secmark[i].secid == secid ||
|
| 389 | rules->secmark[i].secid == AA_SECID_WILDCARD) {
|
| 390 | if (rules->secmark[i].deny)
|
| 391 | perms.deny = ALL_PERMS_MASK;
|
| 392 | else
|
| 393 | perms.allow = ALL_PERMS_MASK;
|
| 394 |
|
| 395 | if (rules->secmark[i].audit)
|
| 396 | perms.audit = ALL_PERMS_MASK;
|
| 397 | }
|
| 398 | }
|
| 399 |
|
| 400 | aa_apply_modes_to_perms(profile, &perms);
|
| 401 |
|
| 402 | return aa_check_perms(profile, &perms, request, ad, audit_net_cb);
|
| 403 | }
|
| 404 |
|
| 405 | int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
|
| 406 | u32 secid, const struct sock *sk)
|
| 407 | {
|
| 408 | struct aa_profile *profile;
|
| 409 | DEFINE_AUDIT_SK(ad, op, NULL, sk);
|
| 410 |
|
| 411 | return fn_for_each_confined(label, profile,
|
| 412 | aa_secmark_perm(profile, request, secid,
|
| 413 | &ad));
|
| 414 | }
|
| 415 | #endif
|