/** * match_mnt - handle path matching for mount * @profile: the confining profile * @mntpath: for the mntpnt (NOT NULL) * @buffer: buffer to be used to lookup mntpath * @devpath: path devname/src_name (MAYBE NULL) * @devbuffer: buffer to be used to lookup devname/src_name * @type: string for the dev type (MAYBE NULL) * @flags: mount flags to match * @data: fs mount data (MAYBE NULL) * @binary: whether @data is binary * * Returns: 0 on success else error */ static int match_mnt(struct aa_profile *profile, const struct path *path, char *buffer, struct path *devpath, char *devbuffer, const char *type, unsigned long flags, void *data, bool binary) { const char *devname = NULL, *info = NULL; int error = -EACCES; AA_BUG(!profile); AA_BUG(devpath && !devbuffer); if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) return 0; if (devpath) { error = aa_path_name(devpath, path_flags(profile, devpath), devbuffer, &devname, &info, profile->disconnected); if (error) devname = ERR_PTR(error); } return match_mnt_path_str(profile, path, buffer, devname, type, flags, data, binary, info); }
static int profile_umount(struct aa_profile *profile, struct path *path, char *buffer) { struct aa_perms perms = { }; const char *name = NULL, *info = NULL; unsigned int state; int error; AA_BUG(!profile); AA_BUG(!path); if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) return 0; error = aa_path_name(path, path_flags(profile, path), buffer, &name, &info, profile->disconnected); if (error) goto audit; state = aa_dfa_match(profile->policy.dfa, profile->policy.start[AA_CLASS_MOUNT], name); perms = compute_mnt_perms(profile->policy.dfa, state); if (AA_MAY_UMOUNT & ~perms.allow) error = -EACCES; audit: return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); }
/* Generic af perm */ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, u32 request, u16 family, int type) { struct aa_perms perms = { }; unsigned int state; __be16 buffer[2]; AA_BUG(family >= AF_MAX); AA_BUG(type < 0 || type >= SOCK_MAX); if (profile_unconfined(profile)) return 0; state = PROFILE_MEDIATES(profile, AA_CLASS_NET); if (!state) return 0; buffer[0] = cpu_to_be16(family); buffer[1] = cpu_to_be16((u16) type); state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, 4); aa_compute_perms(profile->policy.dfa, state, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_net_cb); }
static int profile_tracee_perm(struct aa_profile *tracee, struct aa_label *tracer, u32 request, struct common_audit_data *sa) { if (profile_unconfined(tracee) || unconfined(tracer) || !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) return 0; return profile_ptrace_perm(tracee, tracer, request, sa); }
static int profile_signal_perm(struct aa_profile *profile, struct aa_profile *peer, u32 request, struct common_audit_data *sa) { struct aa_perms perms; if (profile_unconfined(profile) || !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) return 0; aad(sa)->target = peer->base.hname; profile_match_signal(profile, aa_peer_name(peer), aad(sa)->signal, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); }
/* TODO: conditionals */ static int profile_ptrace_perm(struct aa_profile *profile, struct aa_profile *peer, u32 request, struct common_audit_data *sa) { struct aa_perms perms; /* need because of peer in cross check */ if (profile_unconfined(profile) || !PROFILE_MEDIATES(profile, AA_CLASS_PTRACE)) return 0; aad(sa)->target = peer->base.hname; aa_profile_match_label(profile, aa_peer_name(peer), AA_CLASS_PTRACE, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); }
static int cross_ptrace_perm(struct aa_profile *tracer, struct aa_profile *tracee, u32 request, struct common_audit_data *sa) { if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) return xcheck(profile_ptrace_perm(tracer, tracee, request, sa), profile_ptrace_perm(tracee, tracer, request << PTRACE_PERM_SHIFT, sa)); /* policy uses the old style capability check for ptrace */ if (profile_unconfined(tracer) || tracer == tracee) return 0; aad(sa)->label = &tracer->label; aad(sa)->target = tracee->base.hname; aad(sa)->request = 0; aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1); return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); }
/** * match_mnt_path_str - handle path matching for mount * @profile: the confining profile * @mntpath: for the mntpnt (NOT NULL) * @buffer: buffer to be used to lookup mntpath * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) * @type: string for the dev type (MAYBE NULL) * @flags: mount flags to match * @data: fs mount data (MAYBE NULL) * @binary: whether @data is binary * @devinfo: error str if (IS_ERR(@devname)) * * Returns: 0 on success else error */ static int match_mnt_path_str(struct aa_profile *profile, const struct path *mntpath, char *buffer, const char *devname, const char *type, unsigned long flags, void *data, bool binary, const char *devinfo) { struct aa_perms perms = { }; const char *mntpnt = NULL, *info = NULL; int pos, error; AA_BUG(!profile); AA_BUG(!mntpath); AA_BUG(!buffer); if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) return 0; error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, &mntpnt, &info, profile->disconnected); if (error) goto audit; if (IS_ERR(devname)) { error = PTR_ERR(devname); devname = NULL; info = devinfo; goto audit; } error = -EACCES; pos = do_match_mnt(profile->policy.dfa, profile->policy.start[AA_CLASS_MOUNT], mntpnt, devname, type, flags, data, binary, &perms); if (pos) { info = mnt_info_table[pos]; goto audit; } error = 0; audit: return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, error); }
static int profile_signal_perm(struct aa_profile *profile, struct aa_label *peer, u32 request, struct common_audit_data *sa) { struct aa_perms perms; unsigned int state; if (profile_unconfined(profile) || !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) return 0; aad(sa)->peer = peer; /* TODO: secondary cache check <profile, profile, perm> */ state = aa_dfa_next(profile->policy.dfa, profile->policy.start[AA_CLASS_SIGNAL], aad(sa)->signal); aa_label_match(profile, peer, state, false, request, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); }
static int profile_tracer_perm(struct aa_profile *tracer, struct aa_label *tracee, u32 request, struct common_audit_data *sa) { if (profile_unconfined(tracer)) return 0; if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) return profile_ptrace_perm(tracer, tracee, request, sa); /* profile uses the old style capability check for ptrace */ if (&tracer->label == tracee) return 0; aad(sa)->label = &tracer->label; aad(sa)->peer = tracee; aad(sa)->request = 0; aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, CAP_OPT_NONE); return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); }
int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags) { struct aa_profile *profile; char *buffer = NULL; int error; struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; AA_BUG(!label); AA_BUG(!mnt); get_buffers(buffer); error = fn_for_each_confined(label, profile, profile_umount(profile, &path, buffer)); put_buffers(buffer); return error; } /* helper fn for transition on pivotroot * * Returns: label for transition or ERR_PTR. Does not return NULL */ static struct aa_label *build_pivotroot(struct aa_profile *profile, const struct path *new_path, char *new_buffer, const struct path *old_path, char *old_buffer) { const char *old_name, *new_name = NULL, *info = NULL; const char *trans_name = NULL; struct aa_perms perms = { }; unsigned int state; int error; AA_BUG(!profile); AA_BUG(!new_path); AA_BUG(!old_path); if (profile_unconfined(profile) || !PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) return aa_get_newest_label(&profile->label); error = aa_path_name(old_path, path_flags(profile, old_path), old_buffer, &old_name, &info, profile->disconnected); if (error) goto audit; error = aa_path_name(new_path, path_flags(profile, new_path), new_buffer, &new_name, &info, profile->disconnected); if (error) goto audit; error = -EACCES; state = aa_dfa_match(profile->policy.dfa, profile->policy.start[AA_CLASS_MOUNT], new_name); state = aa_dfa_null_transition(profile->policy.dfa, state); state = aa_dfa_match(profile->policy.dfa, state, old_name); perms = compute_mnt_perms(profile->policy.dfa, state); if (AA_MAY_PIVOTROOT & perms.allow) error = 0; audit: error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name, NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); if (error) return ERR_PTR(error); return aa_get_newest_label(&profile->label); } int aa_pivotroot(struct aa_label *label, const struct path *old_path, const struct path *new_path) { struct aa_profile *profile; struct aa_label *target = NULL; char *old_buffer = NULL, *new_buffer = NULL, *info = NULL; int error; AA_BUG(!label); AA_BUG(!old_path); AA_BUG(!new_path); get_buffers(old_buffer, new_buffer); target = fn_label_build(label, profile, GFP_ATOMIC, build_pivotroot(profile, new_path, new_buffer, old_path, old_buffer)); if (!target) { info = "label build failed"; error = -ENOMEM; goto fail; } else if (!IS_ERR(target)) { error = aa_replace_current_label(target); if (error) { /* TODO: audit target */ aa_put_label(target); goto out; } } else /* already audited error */ error = PTR_ERR(target); out: put_buffers(old_buffer, new_buffer); return error; fail: /* TODO: add back in auditing of new_name and old_name */ error = fn_for_each(label, profile, audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */, NULL /* old_name */, NULL, NULL, 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info, error)); goto out; }