UBUNTU: SAUCE: apparmor4.0.0 [63/90]: prompt - add tailglob on name for cache support

BugLink: http://bugs.launchpad.net/bugs/2028253

Allow the cache to cover more than a single rejection by allowing
an entry to match everything that would match a tailglob expression.
This allows a reduction of cache entries and a reduction of prompts
to userspace when access to a directory hierarchy should be allowed
or denied.

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 66c531b9ce8499689677fd4e9e08817dea4fae47
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 02:28:09 -07:00
committed by Paolo Pisati
parent 47e0e296bc
commit c723113f3a
4 changed files with 117 additions and 26 deletions
+5 -1
View File
@@ -45,6 +45,7 @@ struct apparmor_notif_filter {
#define URESPONSE_NO_CACHE 1
#define URESPONSE_LOOKUP 2
#define URESPONSE_PROFILE 4
#define URESPONSE_TAILGLOB 8
struct apparmor_notif {
struct apparmor_notif_common base;
@@ -70,7 +71,10 @@ struct apparmor_notif_resp_perm {
} __attribute__((packed));
struct apparmor_notif_resp_name {
struct apparmor_notif_resp_perm perm;
union {
struct apparmor_notif base;
struct apparmor_notif_resp_perm perm;
};
__u32 name;
__u8 data[];
} __attribute__((packed));
+6 -1
View File
@@ -323,7 +323,11 @@ long aa_audit_data_cmp(struct apparmor_audit_data *lhs,
if (res)
return res;
/* don't compare op */
res = strcmp(lhs->name, rhs->name);
if (lhs->flags & AUDIT_TAILGLOB_NAME)
/* lhs glob matches strings longer than it */
res = strncmp(lhs->name, rhs->name, strlen(lhs->name));
else
res = strcmp(lhs->name, rhs->name);
if (res)
return res;
res = aa_label_cmp(lhs->subj_label, rhs->subj_label);
@@ -395,6 +399,7 @@ struct aa_audit_node *aa_dup_audit_data(struct apparmor_audit_data *orig,
INIT_LIST_HEAD(&copy->list);
/* copy class early so aa_free_audit_node can use switch on failure */
copy->data.class = orig->class;
copy->data.flags = orig->flags;
/* handle anything with possible failure first */
if (orig->name) {
+3 -1
View File
@@ -110,7 +110,9 @@ enum audit_type {
#define OP_URING_OVERRIDE "uring_override"
#define OP_URING_SQPOLL "uring_sqpoll"
#define AUDIT_TAILGLOB_NAME 1
struct apparmor_audit_data {
u32 flags; /* control flags not part of actual data */
int error;
int type;
u16 class;
@@ -121,7 +123,7 @@ struct apparmor_audit_data {
const char *info;
u32 request;
u32 denied;
//u8 flags; /* temporary - move to audit_node or knotif */
struct task_struct *subjtsk;
union {
+103 -23
View File
@@ -574,20 +574,32 @@ static bool response_is_valid_name(struct apparmor_notif_resp_name *reply,
return -EINVAL;
}
/* currently supported flags */
if (reply->perm.base.flags != (URESPONSE_LOOKUP | URESPONSE_PROFILE)) {
if ((reply->perm.base.flags != (URESPONSE_LOOKUP | URESPONSE_PROFILE)) ||
(reply->perm.base.flags != (URESPONSE_TAILGLOB))) {
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;
}
if ((reply->perm.base.flags == URESPONSE_TAILGLOB) &&
!response_is_valid_perm(&reply->perm, knotif, size)) {
AA_DEBUG(DEBUG_UPCALL,
"id %lld: reply bad tail glob perms",
knotif->id);
return false;
}
/* 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;
AA_DEBUG(DEBUG_UPCALL,
"id %lld: reply bad no terminating null on name",
knotif->id);
return false;
}
@@ -709,41 +721,109 @@ struct aa_ruleset *aa_clone_ruleset(struct aa_ruleset *rules)
}
static long knotif_update_from_uresp_name(struct aa_knotif *knotif,
struct apparmor_notif_resp_name *uresp,
struct apparmor_notif_resp_name *reply,
u16 size)
{
struct aa_ruleset *rules;
struct aa_profile *profile;
struct aa_ns *ns;
char *name;
char *name, *glob;
struct aa_audit_node *clone;
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) {
name = (char *) &reply->data[reply->name - sizeof(*reply)];
if (reply->perm.base.flags == (URESPONSE_LOOKUP | URESPONSE_PROFILE)) {
profile = aa_lookup_profile(ns, name);
if (!profile) {
aa_put_ns(ns);
return -ENOENT;
}
aa_put_ns(ns);
return -ENOENT;
}
aa_put_ns(ns);
rules = aa_clone_ruleset(list_first_entry(&profile->rules,
typeof(*rules), list));
if (!rules) {
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);
return -ENOMEM;
/* add list to profile rules TODO: improve locking*/
profile = labels_profile(node->data.subj_label);
list_add_tail_entry(rules, &profile->rules, list);
} else if (reply->perm.base.flags == URESPONSE_TAILGLOB) {
// TODO: dedup with cache update in perm
struct aa_audit_node *node = container_of(knotif,
struct aa_audit_node,
knotif);
struct aa_audit_node *hit;
struct aa_profile *profile = labels_profile(node->data.subj_label);
clone = aa_dup_audit_data(&node->data, GFP_KERNEL);
glob = kstrdup(name, GFP_KERNEL);
if (!name)
return -ENOMEM;
if (!clone) {
kfree(name);
return -ENOMEM;
}
kfree(clone->data.name);
clone->data.name = glob;
clone->data.flags = AUDIT_TAILGLOB_NAME;
clone->knotif.id = knotif->id;
clone->knotif.ntype = knotif->ntype;
node = clone;
knotif = &clone->knotif;
// now add it to the cache
AA_DEBUG(DEBUG_UPCALL,
"notif %lld: response allow/reply 0x%x/0x%x, denied/reply 0x%x/0x%x, error %d/%d",
knotif->id, knotif->ad->request, reply->perm.allow,
knotif->ad->denied, reply->perm.deny, knotif->ad->error,
reply->base.error);
knotif->ad->denied = reply->perm.deny;
knotif->ad->request = reply->perm.allow | reply->perm.deny;
if (!knotif->ad->denied) {
/* no more denial, clear the error*/
knotif->ad->error = 0;
AA_DEBUG(DEBUG_UPCALL,
"notif %lld: response allowed, clearing error\n",
knotif->id);
} else {
AA_DEBUG(DEBUG_UPCALL,
"notif %lld: response denied returning error %d\n",
knotif->id, knotif->ad->error);
}
AA_DEBUG(DEBUG_UPCALL, "id %lld: inserting cache entry requ 0x%x denied 0x%x",
knotif->id, node->data.request, node->data.denied);
hit = aa_audit_cache_insert(&profile->learning_cache,
node);
AA_DEBUG(DEBUG_UPCALL, "id %lld: cache insert %s: name %s node %s\n",
knotif->id, hit != node ? "lost race" : "",
hit->data.name, node->data.name);
if (hit != node) {
AA_DEBUG(DEBUG_UPCALL,
"id %lld: updating existing cache entry",
knotif->id);
aa_audit_cache_update_ent(&profile->learning_cache,
hit, &node->data);
aa_put_audit_node(hit);
} else {
AA_DEBUG(DEBUG_UPCALL, "inserted into cache");
}
aa_put_audit_node(clone);
}
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;
}