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:
John Johansen
2023-05-04 01:33:56 -07:00
committed by Paolo Pisati
parent 4e5b6652ed
commit 9e91d7c53d
5 changed files with 229 additions and 58 deletions
+24 -5
View File
@@ -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 */
+24 -6
View File
@@ -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)
{
+1 -1
View File
@@ -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);
+4
View File
@@ -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
View File
@@ -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);