/* 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); }
/** * 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); }
int aa_move_mount(struct aa_label *label, const struct path *path, const char *orig_name) { struct aa_profile *profile; char *buffer = NULL, *old_buffer = NULL; struct path old_path; int error; AA_BUG(!label); AA_BUG(!path); if (!orig_name || !*orig_name) return -EINVAL; error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); if (error) return error; get_buffers(buffer, old_buffer); error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, &old_path, old_buffer, NULL, MS_MOVE, NULL, false)); put_buffers(buffer, old_buffer); path_put(&old_path); return error; }
int aa_bind_mount(struct aa_label *label, const struct path *path, const char *dev_name, unsigned long flags) { struct aa_profile *profile; char *buffer = NULL, *old_buffer = NULL; struct path old_path; int error; AA_BUG(!label); AA_BUG(!path); if (!dev_name || !*dev_name) return -EINVAL; flags &= MS_REC | MS_BIND; error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); if (error) return error; get_buffers(buffer, old_buffer); error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, &old_path, old_buffer, NULL, flags, NULL, false)); put_buffers(buffer, old_buffer); path_put(&old_path); return error; }
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); }
static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, struct dentry *dir) { struct aa_ns *ns; int error; AA_BUG(!parent); AA_BUG(!name); AA_BUG(!mutex_is_locked(&parent->lock)); ns = alloc_ns(parent->base.hname, name); if (!ns) return NULL; mutex_lock(&ns->lock); error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name); if (error) { AA_ERROR("Failed to create interface for ns %s\n", ns->base.name); mutex_unlock(&ns->lock); aa_free_ns(ns); return ERR_PTR(error); } ns->parent = aa_get_ns(parent); ns->level = parent->level + 1; list_add_rcu(&ns->base.list, &parent->sub_ns); /* add list ref */ aa_get_ns(ns); mutex_unlock(&ns->lock); return ns; }
static int path_flags(struct aa_profile *profile, const struct path *path) { AA_BUG(!profile); AA_BUG(!path); return profile->path_flags | (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0); }
/** * aa_policy_destroy - free the elements referenced by @policy * @policy: policy that is to have its elements freed (NOT NULL) */ void aa_policy_destroy(struct aa_policy *policy) { AA_BUG(on_list_rcu(&policy->profiles)); AA_BUG(on_list_rcu(&policy->list)); /* don't free name as its a subset of hname */ kzfree(policy->hname); }
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, struct socket *sock) { AA_BUG(!label); AA_BUG(!sock); AA_BUG(!sock->sk); return aa_label_sk_perm(label, op, request, sock->sk); }
/* revaliation, get/set attr, shutdown */ int aa_sock_perm(int op, u32 request, struct socket *sock) { AA_BUG(!sock); AA_BUG(!sock->sk); AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, sock_perm(op, request, sock), aa_sk_perm(op, request, sock->sk)); }
static int aa_sk_perm(int op, u32 request, struct sock *sk) { struct aa_label *label; AA_BUG(!sk); AA_BUG(in_interrupt()); /* TODO: switch to begin_current_label ???? */ label = aa_current_label(); return aa_label_sk_perm(label, op, request, sk); }
int aa_sock_listen_perm(struct socket *sock, int backlog) { AA_BUG(!sock); AA_BUG(!sock->sk); /* TODO: .... */ AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, listen_perm(sock, backlog), aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); }
/* revaliation, get/set attr, opt */ int aa_sock_opt_perm(int op, u32 request, struct socket *sock, int level, int optname) { AA_BUG(!sock); AA_BUG(!sock->sk); AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, opt_perm(op, request, sock, level, optname), aa_sk_perm(op, request, sock->sk)); }
int aa_sock_file_perm(struct aa_label *label, int op, u32 request, struct socket *sock) { AA_BUG(!label); AA_BUG(!sock); AA_BUG(!sock->sk); return af_select(sock->sk->sk_family, file_perm(label, op, request, sock), aa_label_sk_perm(label, op, request, sock->sk)); }
int aa_sock_create_perm(struct aa_label *label, int family, int type, int protocol) { AA_BUG(!label); /* TODO: .... */ AA_BUG(in_interrupt()); return af_select(family, create_perm(label, family, type, protocol), aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, protocol, NULL)); }
/* ability of sock to connect, not peer address binding */ int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) { AA_BUG(!sock); AA_BUG(!sock->sk); AA_BUG(!newsock); /* TODO: .... */ AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, accept_perm(sock, newsock), aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); }
/* sendmsg, recvmsg */ int aa_sock_msg_perm(int op, u32 request, struct socket *sock, struct msghdr *msg, int size) { AA_BUG(!sock); AA_BUG(!sock->sk); AA_BUG(!msg); /* TODO: .... */ AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, msg_perm(op, request, sock, msg, size), aa_sk_perm(op, request, sock->sk)); }
int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, int addrlen) { AA_BUG(!sock); AA_BUG(!sock->sk); AA_BUG(!address); /* TODO: .... */ AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, bind_perm(sock, address, addrlen), aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); }
int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, int addrlen) { AA_BUG(!sock); AA_BUG(!sock->sk); AA_BUG(!address); /* TODO: .... */ AA_BUG(in_interrupt()); return af_select(sock->sk->sk_family, connect_perm(sock, address, addrlen), aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); }
int aa_new_mount(struct aa_label *label, const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data) { struct aa_profile *profile; char *buffer = NULL, *dev_buffer = NULL; bool binary = true; int error; int requires_dev = 0; struct path tmp_path, *dev_path = NULL; AA_BUG(!label); AA_BUG(!path); if (type) { struct file_system_type *fstype; fstype = get_fs_type(type); if (!fstype) return -ENODEV; binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; put_filesystem(fstype); if (requires_dev) { if (!dev_name || !*dev_name) return -ENOENT; error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path); if (error) return error; dev_path = &tmp_path; } } get_buffers(buffer, dev_buffer); if (dev_path) { error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, dev_path, dev_buffer, type, flags, data, binary)); } else { error = fn_for_each_confined(label, profile, match_mnt_path_str(profile, path, buffer, dev_name, type, flags, data, binary, NULL)); } put_buffers(buffer, dev_buffer); if (dev_path) path_put(dev_path); return error; }
static inline int aa_af_mask_perm(struct aa_profile *profile, u16 family, int type) { u16 family_mask; AA_BUG(family >= AF_MAX); AA_BUG(type < 0 && type >= SOCK_MAX); if (profile_unconfined(profile)) return 0; family_mask = profile->net.allow[family]; return (family_mask & (1 << type)) ? 0 : -EACCES; }
int aa_sk_perm(const char *op, u32 request, struct sock *sk) { struct aa_label *label; int error; AA_BUG(!sk); AA_BUG(in_interrupt()); /* TODO: switch to begin_current_label ???? */ label = begin_current_label_crit_section(); error = aa_label_sk_perm(label, op, request, sk); end_current_label_crit_section(label); return error; }
static int aa_label_sk_perm(struct aa_label *label, int op, u32 request, struct sock *sk) { struct aa_profile *profile; AA_BUG(!label); AA_BUG(!sk); if (unconfined(label)) return 0; return fn_for_each_confined(label, profile, aa_profile_af_perm(profile, op, sk->sk_family, sk->sk_type, sk->sk_protocol, sk)); }
static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, struct sock *sk) { int error = 0; AA_BUG(!label); AA_BUG(!sk); if (!unconfined(label)) { struct aa_profile *profile; DEFINE_AUDIT_SK(sa, op, sk); error = fn_for_each_confined(label, profile, aa_profile_af_sk_perm(profile, &sa, request, sk)); } return error; }
/** * 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); }
int aa_remount(struct aa_label *label, const struct path *path, unsigned long flags, void *data) { struct aa_profile *profile; char *buffer = NULL; bool binary; int error; AA_BUG(!label); AA_BUG(!path); binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; get_buffers(buffer); error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, NULL, NULL, NULL, flags, data, binary)); put_buffers(buffer); return error; }
int aa_mount_change_type(struct aa_label *label, const struct path *path, unsigned long flags) { struct aa_profile *profile; char *buffer = NULL; int error; AA_BUG(!label); AA_BUG(!path); /* These are the flags allowed by do_change_type() */ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE); get_buffers(buffer); error = fn_for_each_confined(label, profile, match_mnt(profile, path, buffer, NULL, NULL, NULL, flags, NULL, false)); put_buffers(buffer); return error; }
/** * aa_audit_file - handle the auditing of file operations * @profile: the profile being enforced (NOT NULL) * @perms: the permissions computed for the request (NOT NULL) * @op: operation being mediated * @request: permissions requested * @name: name of object being mediated (MAYBE NULL) * @target: name of target (MAYBE NULL) * @tlabel: target label (MAY BE NULL) * @ouid: object uid * @info: extra information message (MAYBE NULL) * @error: 0 if operation allowed else failure error code * * Returns: %0 or error on failure */ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, const char *op, u32 request, const char *name, const char *target, struct aa_label *tlabel, kuid_t ouid, const char *info, int error) { int type = AUDIT_APPARMOR_AUTO; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op); sa.u.tsk = NULL; aad(&sa)->request = request; aad(&sa)->name = name; aad(&sa)->fs.target = target; aad(&sa)->peer = tlabel; aad(&sa)->fs.ouid = ouid; aad(&sa)->info = info; aad(&sa)->error = error; sa.u.tsk = NULL; if (likely(!aad(&sa)->error)) { u32 mask = perms->audit; if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) mask = 0xffff; /* mask off perms that are not being force audited */ aad(&sa)->request &= mask; if (likely(!aad(&sa)->request)) return 0; type = AUDIT_APPARMOR_AUDIT; } else { /* only report permissions that were denied */ aad(&sa)->request = aad(&sa)->request & ~perms->allow; AA_BUG(!aad(&sa)->request); if (aad(&sa)->request & perms->kill) type = AUDIT_APPARMOR_KILL; /* quiet known rejects, assumes quiet and kill do not overlap */ if ((aad(&sa)->request & perms->quiet) && AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_ALL) aad(&sa)->request &= ~perms->quiet; if (!aad(&sa)->request) return aad(&sa)->error; } aad(&sa)->denied = aad(&sa)->request & ~perms->allow; return aa_audit(type, profile, &sa, file_audit_cb); }
/** * aa_create_ns - create an ns, fail if it already exists * @parent: the parent of the namespace being created * @name: the name of the namespace * @dir: if not null the dir to put the ns entries in * * Returns: the a refcounted ns that has been add or an ERR_PTR */ struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, struct dentry *dir) { struct aa_ns *ns; AA_BUG(!mutex_is_locked(&parent->lock)); /* try and find the specified ns */ /* released by caller */ ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); if (!ns) ns = __aa_create_ns(parent, name, dir); else ns = ERR_PTR(-EEXIST); /* return ref */ return ns; }
static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start, const char *str, struct match_workbuf *wb, unsigned int *count) { u16 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); u16 *next = NEXT_TABLE(dfa); u16 *check = CHECK_TABLE(dfa); unsigned int state = start, pos; AA_BUG(!dfa); AA_BUG(!str); AA_BUG(!wb); AA_BUG(!count); *count = 0; if (state == 0) return 0; /* current state is <state>, matching character *str */ if (dfa->tables[YYTD_ID_EC]) { /* Equivalence class table defined */ u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ while (*str) { unsigned int adjust; wb->history[wb->pos] = state; pos = base_idx(base[state]) + equiv[(u8) *str++]; if (check[pos] == state) state = next[pos]; else state = def[state]; if (is_loop(wb, state, &adjust)) { state = aa_dfa_match(dfa, state, str); *count -= adjust; goto out; } inc_wb_pos(wb); (*count)++; } } else { /* default is direct to next state */ while (*str) { unsigned int adjust; wb->history[wb->pos] = state; pos = base_idx(base[state]) + (u8) *str++; if (check[pos] == state) state = next[pos]; else state = def[state]; if (is_loop(wb, state, &adjust)) { state = aa_dfa_match(dfa, state, str); *count -= adjust; goto out; } inc_wb_pos(wb); (*count)++; } } out: if (!state) *count = 0; return state; }