+ * security_dump_masked_av - dumps masked permissions during
+ * security_compute_av due to RBAC, MLS/Constraint and Type bounds.
+ */
+static int dump_masked_av_helper(void *k, void *d, void *args)
+{
+ struct perm_datum *pdatum = d;
+ char **permission_names = args;
+
+ BUG_ON(pdatum->value < 1 || pdatum->value > 32);
+
+ permission_names[pdatum->value - 1] = (char *)k;
+
+ return 0;
+}
+
+static void security_dump_masked_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 permissions,
+ const char *reason)
+{
+ struct common_datum *common_dat;
+ struct class_datum *tclass_dat;
+ struct audit_buffer *ab;
+ char *tclass_name;
+ char *scontext_name = NULL;
+ char *tcontext_name = NULL;
+ char *permission_names[32];
+ int index, length;
+ bool need_comma = false;
+
+ if (!permissions)
+ return;
+
+ tclass_name = policydb.p_class_val_to_name[tclass - 1];
+ tclass_dat = policydb.class_val_to_struct[tclass - 1];
+ common_dat = tclass_dat->comdatum;
+
+ /* init permission_names */
+ if (common_dat &&
+ hashtab_map(common_dat->permissions.table,
+ dump_masked_av_helper, permission_names) < 0)
+ goto out;
+
+ if (hashtab_map(tclass_dat->permissions.table,
+ dump_masked_av_helper, permission_names) < 0)
+ goto out;
+
+ /* get scontext/tcontext in text form */
+ if (context_struct_to_string(scontext,
+ &scontext_name, &length) < 0)
+ goto out;
+
+ if (context_struct_to_string(tcontext,
+ &tcontext_name, &length) < 0)
+ goto out;
+
+ /* audit a message */
+ ab = audit_log_start(current->audit_context,
+ GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ if (!ab)
+ goto out;
+
+ audit_log_format(ab, "op=security_compute_av reason=%s "
+ "scontext=%s tcontext=%s tclass=%s perms=",
+ reason, scontext_name, tcontext_name, tclass_name);
+
+ for (index = 0; index < 32; index++) {
+ u32 mask = (1 << index);
+
+ if ((mask & permissions) == 0)
+ continue;
+
+ audit_log_format(ab, "%s%s",
+ need_comma ? "," : "",
+ permission_names[index]
+ ? permission_names[index] : "????");
+ need_comma = true;
+ }
+ audit_log_end(ab);
+out:
+ /* release scontext/tcontext */
+ kfree(tcontext_name);
+ kfree(scontext_name);
+
+ return;
+}
+
+/*
+ * security_boundary_permission - drops violated permissions
+ * on boundary constraint.
+ */
+static void type_attribute_bounds_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct context lo_scontext;
+ struct context lo_tcontext;
+ struct av_decision lo_avd;
+ struct type_datum *source
+ = policydb.type_val_to_struct[scontext->type - 1];
+ struct type_datum *target
+ = policydb.type_val_to_struct[tcontext->type - 1];
+ u32 masked = 0;
+
+ if (source->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+
+ memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+ lo_scontext.type = source->bounds;
+
+ context_struct_compute_av(&lo_scontext,
+ tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (target->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+
+ memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
+ lo_tcontext.type = target->bounds;
+
+ context_struct_compute_av(scontext,
+ &lo_tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (source->bounds && target->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+ /*
+ * lo_scontext and lo_tcontext are already
+ * set up.
+ */
+
+ context_struct_compute_av(&lo_scontext,
+ &lo_tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (masked) {
+ /* mask violated permissions */
+ avd->allowed &= ~masked;
+
+ /* audit masked permissions */
+ security_dump_masked_av(scontext, tcontext,
+ tclass, masked, "bounds");
+ }
+}
+
+/*