UBUNTU: SAUCE: apparmor4.0.0 [61/90]: prompt - add the ability to reply with a profile name
BugLink: http://bugs.launchpad.net/bugs/2028253 BugLink: https://bugs.launchpad.net/bugs/2028253 Add the ability to extend a profile with another profiles base rule set as a reply to a prompt. Signed-off-by: John Johansen <john.johansen@canonical.com> (cherry picked from https://gitlab.com/jjohansen/apparmor-kernel) Signed-off-by: Andrea Righi <andrea.righi@canonical.com> (cherry picked from commit 56bf6533b2c415700a366ca6b675775e57ec4816 https://git.launchpad.net/~apparmor-dev/ubuntu-kernel-next) Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
This commit is contained in:
committed by
Paolo Pisati
parent
4e5b6652ed
commit
9e91d7c53d
@@ -16,11 +16,12 @@
|
||||
#define APPARMOR_FLAG_NOCACHE 1
|
||||
|
||||
enum apparmor_notif_type {
|
||||
APPARMOR_NOTIF_RESP,
|
||||
APPARMOR_NOTIF_RESP_PERM,
|
||||
APPARMOR_NOTIF_CANCEL,
|
||||
APPARMOR_NOTIF_INTERUPT,
|
||||
APPARMOR_NOTIF_ALIVE,
|
||||
APPARMOR_NOTIF_OP
|
||||
APPARMOR_NOTIF_OP,
|
||||
APPARMOR_NOTIF_RESP_NAME,
|
||||
};
|
||||
|
||||
#define APPARMOR_NOTIFY_VERSION 3
|
||||
@@ -40,6 +41,11 @@ struct apparmor_notif_filter {
|
||||
__u8 data[];
|
||||
} __attribute__((packed));
|
||||
|
||||
// flags
|
||||
#define URESPONSE_NO_CACHE 1
|
||||
#define URESPONSE_LOOKUP 2
|
||||
#define URESPONSE_PROFILE 4
|
||||
|
||||
struct apparmor_notif {
|
||||
struct apparmor_notif_common base;
|
||||
__u16 ntype; /* notify type */
|
||||
@@ -56,13 +62,25 @@ struct apparmor_notif_update {
|
||||
} __attribute__((packed));
|
||||
|
||||
/* userspace response to notification that expects a response */
|
||||
struct apparmor_notif_resp {
|
||||
struct apparmor_notif_resp_perm {
|
||||
struct apparmor_notif base;
|
||||
__s32 error; /* error if unchanged */
|
||||
__u32 allow;
|
||||
__u32 deny;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct apparmor_notif_resp_name {
|
||||
struct apparmor_notif_resp_perm perm;
|
||||
__u32 name;
|
||||
__u8 data[];
|
||||
} __attribute__((packed));
|
||||
|
||||
union apparmor_notif_resp {
|
||||
struct apparmor_notif base;
|
||||
struct apparmor_notif_resp_perm perm;
|
||||
struct apparmor_notif_resp_name name;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct apparmor_notif_op {
|
||||
struct apparmor_notif base;
|
||||
__u32 allow;
|
||||
@@ -75,7 +93,7 @@ struct apparmor_notif_op {
|
||||
|
||||
struct apparmor_notif_file {
|
||||
struct apparmor_notif_op base;
|
||||
uid_t suid, ouid;
|
||||
uid_t subj_uid, obj_uid;
|
||||
__u32 name; /* offset into data */
|
||||
|
||||
__u8 data[];
|
||||
@@ -87,6 +105,7 @@ union apparmor_notif_all {
|
||||
struct apparmor_notif base;
|
||||
struct apparmor_notif_op op;
|
||||
struct apparmor_notif_file file;
|
||||
union apparmor_notif_resp respnse;
|
||||
};
|
||||
|
||||
#define APPARMOR_IOC_MAGIC 0xF8
|
||||
@@ -103,6 +122,6 @@ union apparmor_notif_all {
|
||||
#define APPARMOR_NOTIF_RECV _IOWR(APPARMOR_IOC_MAGIC, 4, \
|
||||
struct apparmor_notif *)
|
||||
#define APPARMOR_NOTIF_SEND _IOWR(APPARMOR_IOC_MAGIC, 5, \
|
||||
struct apparmor_notif_resp *)
|
||||
union apparmor_notif_resp *)
|
||||
|
||||
#endif /* _UAPI_LINUX_APPARMOR_H */
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_ns.h"
|
||||
#include "include/resource.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy_unpack.h"
|
||||
#include "include/task.h"
|
||||
|
||||
@@ -693,20 +694,37 @@ static long notify_user_recv(struct aa_listener *listener,
|
||||
static long notify_user_response(struct aa_listener *listener,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct apparmor_notif_resp uresp = {};
|
||||
union apparmor_notif_resp uresp = {};
|
||||
union apparmor_notif_resp *big_resp = NULL;
|
||||
long error;
|
||||
u16 size;
|
||||
void __user *buf = (void __user *)arg;
|
||||
|
||||
if (copy_from_user(&size, buf, sizeof(size)))
|
||||
return -EFAULT;
|
||||
size = min_t(size_t, size, sizeof(uresp));
|
||||
if (copy_from_user(&uresp, buf, size))
|
||||
return -EFAULT;
|
||||
if (size > aa_g_path_max)
|
||||
return -EMSGSIZE;
|
||||
if (size > sizeof(uresp)) {
|
||||
/* TODO: put max size on message */
|
||||
big_resp = (union apparmor_notif_resp *) aa_get_buffer(false);
|
||||
if (big_resp)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(big_resp, buf, size)) {
|
||||
kfree(big_resp);
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
size = min_t(size_t, size, sizeof(uresp));
|
||||
if (copy_from_user(&uresp, buf, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return aa_listener_unotif_response(listener, &uresp, size);
|
||||
error = aa_listener_unotif_response(listener, &uresp, size);
|
||||
aa_put_buffer((char *) big_resp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static long notify_is_id_valid(struct aa_listener *listener,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@ int aa_do_notification(u16 ntype, struct aa_audit_node *node);
|
||||
long aa_listener_unotif_recv(struct aa_listener *listener, void __user *buf,
|
||||
u16 max_size);
|
||||
long aa_listener_unotif_response(struct aa_listener *listener,
|
||||
struct apparmor_notif_resp *uresp,
|
||||
union apparmor_notif_resp *uresp,
|
||||
u16 size);
|
||||
|
||||
void aa_listener_kref(struct kref *kref);
|
||||
|
||||
@@ -178,6 +178,10 @@ struct aa_ruleset {
|
||||
struct aa_secmark *secmark;
|
||||
};
|
||||
|
||||
void aa_free_ruleset(struct aa_ruleset *rules);
|
||||
struct aa_ruleset *aa_new_ruleset(gfp_t gfp);
|
||||
struct aa_ruleset *aa_clone_ruleset(struct aa_ruleset *rules);
|
||||
|
||||
/* struct aa_attachment - data and rules for a profiles attachment
|
||||
* @list:
|
||||
* @xmatch_str: human readable attachment string
|
||||
|
||||
+176
-46
@@ -523,12 +523,9 @@ static void listener_complete_held_user_pending(struct aa_listener *listener,
|
||||
spin_unlock(&listener->lock);
|
||||
}
|
||||
|
||||
/* base checks userspace respnse to a notification is valid */
|
||||
static bool response_is_valid(struct apparmor_notif_resp *reply,
|
||||
struct aa_knotif *knotif)
|
||||
static bool response_is_valid_perm(struct apparmor_notif_resp_perm *reply,
|
||||
struct aa_knotif *knotif, u16 size)
|
||||
{
|
||||
if (reply->base.ntype != APPARMOR_NOTIF_RESP)
|
||||
return false;
|
||||
if ((knotif->ad->request | knotif->ad->denied) &
|
||||
~(reply->allow | reply->deny)) {
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
@@ -537,6 +534,7 @@ static bool response_is_valid(struct apparmor_notif_resp *reply,
|
||||
reply->deny);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
/* TODO: this was disabled per snapd request, setup flag to do check
|
||||
* // allow bits that were never requested
|
||||
* if (reply->allow & ~knotif->ad->request) {
|
||||
@@ -550,14 +548,71 @@ static bool response_is_valid(struct apparmor_notif_resp *reply,
|
||||
* return false;
|
||||
* }
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/* copy uresponse into knotif */
|
||||
static void knotif_update_from_uresp(struct aa_knotif *knotif,
|
||||
struct apparmor_notif_resp *uresp,
|
||||
u16 *flags)
|
||||
static bool response_is_valid_name(struct apparmor_notif_resp_name *reply,
|
||||
struct aa_knotif *knotif, u16 size)
|
||||
{
|
||||
long i;
|
||||
|
||||
if (size <= sizeof(*reply)) {
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"id %lld: reply bad size %u < %ld",
|
||||
knotif->id, size, sizeof(*reply));
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
if (reply->name < sizeof(*reply)) {
|
||||
/* inside of data declared fields */
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"id %lld: reply bad name offset in fields %u < %ld",
|
||||
knotif->id, reply->name, sizeof(*reply));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (reply->name > size) {
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"id %lld: reply name pasted end of data size %u > %ld",
|
||||
knotif->id, reply->name, sizeof(*reply));
|
||||
return -EINVAL;
|
||||
}
|
||||
/* currently supported flags */
|
||||
if (reply->perm.base.flags != (URESPONSE_LOOKUP | URESPONSE_PROFILE)) {
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"id %lld: reply bad flags 0x%x expected 0x%x",
|
||||
knotif->id, reply->perm.base.flags,
|
||||
URESPONSE_LOOKUP | URESPONSE_PROFILE);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* check name for terminating null */
|
||||
for (i = reply->name - sizeof(*reply); i < size - sizeof(*reply); i++) {
|
||||
if (reply->data[i] == 0)
|
||||
return true;
|
||||
}
|
||||
/* reached end of data without finding null */
|
||||
return -EINVAL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* base checks userspace respnse to a notification is valid */
|
||||
static bool response_is_valid(union apparmor_notif_resp *reply,
|
||||
struct aa_knotif *knotif, u16 size)
|
||||
{
|
||||
if (reply->base.ntype == APPARMOR_NOTIF_RESP_PERM)
|
||||
return response_is_valid_perm(&reply->perm, knotif, size);
|
||||
else if (reply->base.ntype == APPARMOR_NOTIF_RESP_NAME)
|
||||
return response_is_valid_name(&reply->name, knotif, size);
|
||||
else
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* copy uresponse into knotif */
|
||||
static void knotif_update_from_uresp_perm(struct aa_knotif *knotif,
|
||||
struct apparmor_notif_resp_perm *uresp)
|
||||
{
|
||||
u16 flags;
|
||||
|
||||
if (uresp) {
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"notif %lld: response allow/reply 0x%x/0x%x, denied/reply 0x%x/0x%x, error %d/%d",
|
||||
@@ -567,7 +622,7 @@ static void knotif_update_from_uresp(struct aa_knotif *knotif,
|
||||
|
||||
knotif->ad->denied = uresp->deny;
|
||||
knotif->ad->request = uresp->allow | uresp->deny;
|
||||
*flags = uresp->base.flags;
|
||||
flags = uresp->base.flags;
|
||||
if (!knotif->ad->denied) {
|
||||
/* no more denial, clear the error*/
|
||||
knotif->ad->error = 0;
|
||||
@@ -581,43 +636,12 @@ static void knotif_update_from_uresp(struct aa_knotif *knotif,
|
||||
}
|
||||
} else {
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"notif %lld: respons bad going with: allow 0x%x, denied 0x%x, error %d",
|
||||
"id %lld: respons bad going with: allow 0x%x, denied 0x%x, error %d",
|
||||
knotif->id, knotif->ad->request, knotif->ad->denied,
|
||||
knotif->ad->error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// move to apparmor.h
|
||||
#define UNOTIF_NO_CACHE 1
|
||||
|
||||
/* handle userspace responding to a synchronous notification */
|
||||
long aa_listener_unotif_response(struct aa_listener *listener,
|
||||
struct apparmor_notif_resp *uresp,
|
||||
u16 size)
|
||||
{
|
||||
struct aa_knotif *knotif = NULL;
|
||||
u16 flags;
|
||||
long ret;
|
||||
|
||||
spin_lock(&listener->lock);
|
||||
knotif = __del_and_hold_user_pending(listener, uresp->base.id);
|
||||
if (!knotif) {
|
||||
ret = -ENOENT;
|
||||
AA_DEBUG(DEBUG_UPCALL, "could not find id %lld",
|
||||
uresp->base.id);
|
||||
goto out;
|
||||
}
|
||||
if (!response_is_valid(uresp, knotif)) {
|
||||
ret = -EINVAL;
|
||||
AA_DEBUG(DEBUG_UPCALL, "id %lld: response not valid", knotif->id);
|
||||
__listener_complete_held_user_pending(listener, knotif);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* handle updating actual data */
|
||||
knotif_update_from_uresp(knotif, uresp, &flags);
|
||||
if (!(flags & UNOTIF_NO_CACHE)) {
|
||||
if (!(flags & URESPONSE_NO_CACHE)) {
|
||||
/* cache of response requested */
|
||||
struct aa_audit_node *node = container_of(knotif,
|
||||
struct aa_audit_node,
|
||||
@@ -644,8 +668,114 @@ long aa_listener_unotif_response(struct aa_listener *listener,
|
||||
}
|
||||
/* now to audit */
|
||||
} /* cache_response */
|
||||
}
|
||||
|
||||
|
||||
void aa_free_ruleset(struct aa_ruleset *rules)
|
||||
{
|
||||
if (!rules)
|
||||
return;
|
||||
aa_put_pdb(rules->policy);
|
||||
aa_put_pdb(rules->file);
|
||||
kfree_sensitive(rules);
|
||||
}
|
||||
|
||||
struct aa_ruleset *aa_new_ruleset(gfp_t gfp)
|
||||
{
|
||||
struct aa_ruleset *rules = kzalloc(sizeof(*rules), gfp);
|
||||
|
||||
INIT_LIST_HEAD(&rules->list);
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
struct aa_ruleset *aa_clone_ruleset(struct aa_ruleset *rules)
|
||||
{
|
||||
struct aa_ruleset *clone;
|
||||
|
||||
clone = aa_new_ruleset(GFP_KERNEL);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
clone->size = rules->size;
|
||||
clone->policy = aa_get_pdb(rules->policy);
|
||||
clone->file = aa_get_pdb(rules->file);
|
||||
clone->caps = rules->caps;
|
||||
clone->rlimits = rules->rlimits;
|
||||
|
||||
/* TODO: secmark */
|
||||
return clone;
|
||||
}
|
||||
|
||||
static long knotif_update_from_uresp_name(struct aa_knotif *knotif,
|
||||
struct apparmor_notif_resp_name *uresp,
|
||||
u16 size)
|
||||
{
|
||||
struct aa_ruleset *rules;
|
||||
struct aa_profile *profile;
|
||||
struct aa_ns *ns;
|
||||
char *name;
|
||||
struct aa_audit_node *node = container_of(knotif,
|
||||
struct aa_audit_node,
|
||||
knotif);
|
||||
|
||||
ns = aa_get_current_ns();
|
||||
name = (char *) &uresp->data[uresp->name - sizeof(*uresp)];
|
||||
profile = aa_lookup_profile(ns, name);
|
||||
if (!profile) {
|
||||
aa_put_ns(ns);
|
||||
return -ENOENT;
|
||||
}
|
||||
aa_put_ns(ns);
|
||||
|
||||
rules = aa_clone_ruleset(list_first_entry(&profile->rules,
|
||||
typeof(*rules), list));
|
||||
if (!rules) {
|
||||
aa_put_profile(profile);
|
||||
return -ENOMEM;
|
||||
}
|
||||
AA_DEBUG(DEBUG_UPCALL,
|
||||
"id %lld: cloned profile '%s' rule set", knotif->id,
|
||||
profile->base.hname);
|
||||
aa_put_profile(profile);
|
||||
|
||||
/* add list to profile rules TODO: improve locking*/
|
||||
profile = labels_profile(node->data.subj_label);
|
||||
list_add_tail_entry(rules, &profile->rules, list);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* handle userspace responding to a synchronous notification */
|
||||
long aa_listener_unotif_response(struct aa_listener *listener,
|
||||
union apparmor_notif_resp *uresp,
|
||||
u16 size)
|
||||
{
|
||||
struct aa_knotif *knotif = NULL;
|
||||
long ret;
|
||||
|
||||
spin_lock(&listener->lock);
|
||||
knotif = __del_and_hold_user_pending(listener, uresp->base.id);
|
||||
if (!knotif) {
|
||||
ret = -ENOENT;
|
||||
AA_DEBUG(DEBUG_UPCALL, "could not find id %lld",
|
||||
uresp->base.id);
|
||||
goto out;
|
||||
}
|
||||
if (!response_is_valid(uresp, knotif, size)) {
|
||||
ret = -EINVAL;
|
||||
AA_DEBUG(DEBUG_UPCALL, "id %lld: response not valid", knotif->id);
|
||||
__listener_complete_held_user_pending(listener, knotif);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (uresp->perm.base.ntype == APPARMOR_NOTIF_RESP_PERM) {
|
||||
knotif_update_from_uresp_perm(knotif, &uresp->perm);
|
||||
} else if (uresp->perm.base.ntype == APPARMOR_NOTIF_RESP_NAME) {
|
||||
size = knotif_update_from_uresp_name(knotif, &uresp->name, size);
|
||||
} else {
|
||||
AA_DEBUG(DEBUG_UPCALL, "id %lld: unknown response type", knotif->id);
|
||||
size = -EINVAL;
|
||||
}
|
||||
ret = size;
|
||||
|
||||
AA_DEBUG(DEBUG_UPCALL, "id %lld: completing notif", knotif->id);
|
||||
@@ -720,10 +850,10 @@ static long build_v3_unotif(struct aa_knotif *knotif, void __user *buf,
|
||||
|
||||
if (knotif->ad->subjtsk != NULL) {
|
||||
unotif.op.pid = task_pid_vnr(knotif->ad->subjtsk);
|
||||
unotif.file.suid = from_kuid(user_ns, task_uid(knotif->ad->subjtsk));
|
||||
unotif.file.subj_uid = from_kuid(user_ns, task_uid(knotif->ad->subjtsk));
|
||||
}
|
||||
unotif.op.class = knotif->ad->class;
|
||||
unotif.file.ouid = from_kuid(user_ns, knotif->ad->fs.ouid);
|
||||
unotif.file.obj_uid = from_kuid(user_ns, knotif->ad->fs.ouid);
|
||||
|
||||
put_user_ns(user_ns);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user