[TOMOYO 08/15](repost) Argv[0] access control functions.

Kentaro Takeda
2007-10-02T07:35:37Z

argv[0] check functions for TOMOYO Linux.
If the executed program name and argv[0] is different,
TOMOYO Linux checks permission.

Each permission can be automatically accumulated into
the policy of each domain using 'learning mode'.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/exec.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 222 insertions(+)

--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/security/tomoyo/exec.c 2007-10-02 11:26:22.000000000 +0900
@@ -0,0 +1,222 @@
+/*
+ * security/tomoyo/exec.c
+ *
+ * Argv0 access control functions for TOMOYO Linux.
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+
+/************************* AUDIT FUNCTIONS *************************/
+
+static int tmy_audit_argv0_log(const struct path_info *filename,
+ const char *argv0,
+ const u8 is_granted,
+ const u8 is_enforce)
+{
+ char *buf;
+ int len;
+
+ if (is_granted) {
+ if (!tmy_audit_grant())
+ return 0;
+ } else {
+ if (!tmy_audit_reject())
+ return 0;
+ }
+
+ len = filename->total_len + strlen(argv0) + 8;
+ buf = tmy_init_audit_log(&len);
+
+ if (!buf)
+ return -ENOMEM;
+
+ snprintf(buf + strlen(buf),
+ len - strlen(buf) - 1,
+ TMY_ALLOW_ARGV0 "%s %s",
+ filename->name,
+ argv0);
+
+ return tmy_write_audit_log(buf, is_granted, is_enforce);
+}
+
+/************************* ARGV0 MISMATCH HANDLER *************************/
+
+static int tmy_add_argv0_entry(const char *filename,
+ const char *argv0,
+ struct domain_info *domain,
+ const struct condition_list *cond,
+ const u8 is_delete)
+{
+ struct acl_info *ptr;
+ const struct path_info *saved_filename;
+ const struct path_info *saved_argv0;
+ int error = -ENOMEM;
+
+ if (!tmy_correct_path(filename, 1, 0, -1, __FUNCTION__) ||
+ !tmy_correct_path(argv0, -1, 0, -1, __FUNCTION__) ||
+ strchr(argv0, '/'))
+ return -EINVAL;
+
+ saved_filename = tmy_save_name(filename);
+ saved_argv0 = tmy_save_name(argv0);
+ if (!saved_filename || !saved_argv0)
+ return -ENOMEM;
+
+ down(&domain_acl_lock);
+
+ if (is_delete)
+ goto remove;
+
+ ptr = domain->first_acl_ptr;
+ if (!ptr)
+ goto first_entry;
+ while (1) {
+ struct argv0_acl *acl = (struct argv0_acl *) ptr;
+
+ if (ptr->type == TMY_TYPE_ARGV0_ACL && ptr->cond == cond &&
+ acl->filename == saved_filename &&
+ acl->argv0 == saved_argv0) {
+ ptr->is_deleted = 0;
+ /* Found. Nothing to do. */
+ error = 0;
+ break;
+ }
+
+ if (ptr->next) {
+ ptr = ptr->next;
+ continue;
+ }
+
+first_entry: ;
+ /* Not found. Append it to the tail. */
+ acl = tmy_alloc_element(sizeof(*acl));
+ if (!acl)
+ break;
+
+ acl->head.type = TMY_TYPE_ARGV0_ACL;
+ acl->head.cond = cond;
+ acl->filename = saved_filename;
+ acl->argv0 = saved_argv0;
+ error = tmy_add_acl(ptr, domain,
+ (struct acl_info *) acl);
+
+ break;
+ }
+ goto ok;
+remove: ;
+ error = -ENOENT;
+ for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+ struct argv0_acl *acl = (struct argv0_acl *) ptr;
+
+ if (ptr->type != TMY_TYPE_ARGV0_ACL ||
+ ptr->cond != cond || ptr->is_deleted ||
+ acl->filename != saved_filename ||
+ acl->argv0 != saved_argv0)
+ continue;
+
+ error = tmy_del_acl(ptr);
+ break;
+ }
+ok: ;
+ up(&domain_acl_lock);
+
+ return error;
+}
+
+static int tmy_argv0_acl(const struct path_info *filename,
+ const char *argv0_)
+{
+ const struct domain_info *domain = TMY_SECURITY->domain;
+ int error = -EPERM;
+ struct acl_info *ptr;
+ struct path_info argv0;
+
+ if (!tmy_flags(TMY_MAC_FOR_ARGV0))
+ return 0;
+
+ argv0.name = argv0_;
+ tmy_fill_path_info(&argv0);
+
+ for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) {
+ struct argv0_acl *acl = (struct argv0_acl *) ptr;
+
+ if (ptr->type == TMY_TYPE_ARGV0_ACL &&
+ ptr->is_deleted == 0 &&
+ tmy_check_condition(ptr->cond, NULL) == 0 &&
+ tmy_path_match(filename, acl->filename) &&
+ tmy_path_match(&argv0, acl->argv0)) {
+ error = 0;
+ break;
+ }
+ }
+
+ return error;
+}
+
+/**
+ * tmy_argv0_perm - check for argv[0] permission.
+ * @filename: pointer to filename.
+ * @argv0: pointer to basename of argv[0].
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_argv0_perm(const struct path_info *filename, const char *argv0)
+{
+ int error = 0;
+ const u8 is_enforce = tmy_enforce(TMY_MAC_FOR_ARGV0);
+
+ if (!tmy_flags(TMY_MAC_FOR_ARGV0))
+ return 0;
+ if (!filename || !argv0 || !*argv0)
+ return 0;
+
+ error = tmy_argv0_acl(filename, argv0);
+
+ tmy_audit_argv0_log(filename, argv0, !error, is_enforce);
+
+ if (error) {
+ struct domain_info * const domain = TMY_SECURITY->domain;
+
+ if (is_enforce)
+ error = tmy_supervisor("%s\n" TMY_ALLOW_ARGV0 "%s %s\n",
+ domain->domainname->name,
+ filename->name, argv0);
+
+ else if (tmy_accept(TMY_MAC_FOR_ARGV0, domain))
+ tmy_add_argv0_entry(filename->name, argv0, domain,
+ NULL, 0);
+
+ if (!is_enforce)
+ error = 0;
+ }
+
+ return error;
+}
+
+/**
+ * tmy_add_argv0_policy - add or delete argv[0] policy.
+ * @data: a line to parse.
+ * @domain: pointer to "struct domain_info".
+ * @cond: pointer to "struct condition_list". May be NULL.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_argv0_policy(char *data,
+ struct domain_info *domain,
+ const struct condition_list *cond,
+ const u8 is_delete)
+{
+ char *argv0 = strchr(data, ' ');
+
+ if (!argv0)
+ return -EINVAL;
+
+ *argv0++ = '\0';
+
+ return tmy_add_argv0_entry(data, argv0, domain, cond,
+ is_delete);
+}