linux/security/tomoyo/domain.c
<<
>>
Prefs
   1/*
   2 * security/tomoyo/domain.c
   3 *
   4 * Domain transition functions for TOMOYO.
   5 *
   6 * Copyright (C) 2005-2010  NTT DATA CORPORATION
   7 */
   8
   9#include "common.h"
  10#include <linux/binfmts.h>
  11#include <linux/slab.h>
  12
  13/* Variables definitions.*/
  14
  15/* The initial domain. */
  16struct tomoyo_domain_info tomoyo_kernel_domain;
  17
  18/**
  19 * tomoyo_update_policy - Update an entry for exception policy.
  20 *
  21 * @new_entry:       Pointer to "struct tomoyo_acl_info".
  22 * @size:            Size of @new_entry in bytes.
  23 * @is_delete:       True if it is a delete request.
  24 * @list:            Pointer to "struct list_head".
  25 * @check_duplicate: Callback function to find duplicated entry.
  26 *
  27 * Returns 0 on success, negative value otherwise.
  28 *
  29 * Caller holds tomoyo_read_lock().
  30 */
  31int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
  32                         bool is_delete, struct list_head *list,
  33                         bool (*check_duplicate) (const struct tomoyo_acl_head
  34                                                  *,
  35                                                  const struct tomoyo_acl_head
  36                                                  *))
  37{
  38        int error = is_delete ? -ENOENT : -ENOMEM;
  39        struct tomoyo_acl_head *entry;
  40
  41        if (mutex_lock_interruptible(&tomoyo_policy_lock))
  42                return -ENOMEM;
  43        list_for_each_entry_rcu(entry, list, list) {
  44                if (!check_duplicate(entry, new_entry))
  45                        continue;
  46                entry->is_deleted = is_delete;
  47                error = 0;
  48                break;
  49        }
  50        if (error && !is_delete) {
  51                entry = tomoyo_commit_ok(new_entry, size);
  52                if (entry) {
  53                        list_add_tail_rcu(&entry->list, list);
  54                        error = 0;
  55                }
  56        }
  57        mutex_unlock(&tomoyo_policy_lock);
  58        return error;
  59}
  60
  61/**
  62 * tomoyo_update_domain - Update an entry for domain policy.
  63 *
  64 * @new_entry:       Pointer to "struct tomoyo_acl_info".
  65 * @size:            Size of @new_entry in bytes.
  66 * @is_delete:       True if it is a delete request.
  67 * @domain:          Pointer to "struct tomoyo_domain_info".
  68 * @check_duplicate: Callback function to find duplicated entry.
  69 * @merge_duplicate: Callback function to merge duplicated entry.
  70 *
  71 * Returns 0 on success, negative value otherwise.
  72 *
  73 * Caller holds tomoyo_read_lock().
  74 */
  75int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
  76                         bool is_delete, struct tomoyo_domain_info *domain,
  77                         bool (*check_duplicate) (const struct tomoyo_acl_info
  78                                                  *,
  79                                                  const struct tomoyo_acl_info
  80                                                  *),
  81                         bool (*merge_duplicate) (struct tomoyo_acl_info *,
  82                                                  struct tomoyo_acl_info *,
  83                                                  const bool))
  84{
  85        int error = is_delete ? -ENOENT : -ENOMEM;
  86        struct tomoyo_acl_info *entry;
  87
  88        if (mutex_lock_interruptible(&tomoyo_policy_lock))
  89                return error;
  90        list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
  91                if (!check_duplicate(entry, new_entry))
  92                        continue;
  93                if (merge_duplicate)
  94                        entry->is_deleted = merge_duplicate(entry, new_entry,
  95                                                            is_delete);
  96                else
  97                        entry->is_deleted = is_delete;
  98                error = 0;
  99                break;
 100        }
 101        if (error && !is_delete) {
 102                entry = tomoyo_commit_ok(new_entry, size);
 103                if (entry) {
 104                        list_add_tail_rcu(&entry->list, &domain->acl_info_list);
 105                        error = 0;
 106                }
 107        }
 108        mutex_unlock(&tomoyo_policy_lock);
 109        return error;
 110}
 111
 112void tomoyo_check_acl(struct tomoyo_request_info *r,
 113                      bool (*check_entry) (struct tomoyo_request_info *,
 114                                           const struct tomoyo_acl_info *))
 115{
 116        const struct tomoyo_domain_info *domain = r->domain;
 117        struct tomoyo_acl_info *ptr;
 118
 119        list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
 120                if (ptr->is_deleted || ptr->type != r->param_type)
 121                        continue;
 122                if (check_entry(r, ptr)) {
 123                        r->granted = true;
 124                        return;
 125                }
 126        }
 127        r->granted = false;
 128}
 129
 130/* The list for "struct tomoyo_domain_info". */
 131LIST_HEAD(tomoyo_domain_list);
 132
 133struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
 134struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
 135
 136/**
 137 * tomoyo_last_word - Get last component of a domainname.
 138 *
 139 * @domainname: Domainname to check.
 140 *
 141 * Returns the last word of @domainname.
 142 */
 143static const char *tomoyo_last_word(const char *name)
 144{
 145        const char *cp = strrchr(name, ' ');
 146        if (cp)
 147                return cp + 1;
 148        return name;
 149}
 150
 151static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
 152                                           const struct tomoyo_acl_head *b)
 153{
 154        const struct tomoyo_transition_control *p1 = container_of(a,
 155                                                                  typeof(*p1),
 156                                                                  head);
 157        const struct tomoyo_transition_control *p2 = container_of(b,
 158                                                                  typeof(*p2),
 159                                                                  head);
 160        return p1->type == p2->type && p1->is_last_name == p2->is_last_name
 161                && p1->domainname == p2->domainname
 162                && p1->program == p2->program;
 163}
 164
 165/**
 166 * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.
 167 *
 168 * @domainname: The name of domain. Maybe NULL.
 169 * @program:    The name of program. Maybe NULL.
 170 * @type:       Type of transition.
 171 * @is_delete:  True if it is a delete request.
 172 *
 173 * Returns 0 on success, negative value otherwise.
 174 */
 175static int tomoyo_update_transition_control_entry(const char *domainname,
 176                                                  const char *program,
 177                                                  const u8 type,
 178                                                  const bool is_delete)
 179{
 180        struct tomoyo_transition_control e = { .type = type };
 181        int error = is_delete ? -ENOENT : -ENOMEM;
 182        if (program) {
 183                if (!tomoyo_correct_path(program))
 184                        return -EINVAL;
 185                e.program = tomoyo_get_name(program);
 186                if (!e.program)
 187                        goto out;
 188        }
 189        if (domainname) {
 190                if (!tomoyo_correct_domain(domainname)) {
 191                        if (!tomoyo_correct_path(domainname))
 192                                goto out;
 193                        e.is_last_name = true;
 194                }
 195                e.domainname = tomoyo_get_name(domainname);
 196                if (!e.domainname)
 197                        goto out;
 198        }
 199        error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
 200                                     &tomoyo_policy_list
 201                                     [TOMOYO_ID_TRANSITION_CONTROL],
 202                                     tomoyo_same_transition_control);
 203 out:
 204        tomoyo_put_name(e.domainname);
 205        tomoyo_put_name(e.program);
 206        return error;
 207}
 208
 209/**
 210 * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
 211 *
 212 * @data:      String to parse.
 213 * @is_delete: True if it is a delete request.
 214 * @type:      Type of this entry.
 215 *
 216 * Returns 0 on success, negative value otherwise.
 217 */
 218int tomoyo_write_transition_control(char *data, const bool is_delete,
 219                                    const u8 type)
 220{
 221        char *domainname = strstr(data, " from ");
 222        if (domainname) {
 223                *domainname = '\0';
 224                domainname += 6;
 225        } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
 226                   type == TOMOYO_TRANSITION_CONTROL_KEEP) {
 227                domainname = data;
 228                data = NULL;
 229        }
 230        return tomoyo_update_transition_control_entry(domainname, data, type,
 231                                                      is_delete);
 232}
 233
 234/**
 235 * tomoyo_transition_type - Get domain transition type.
 236 *
 237 * @domainname: The name of domain.
 238 * @program:    The name of program.
 239 *
 240 * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
 241 * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
 242 * @program suppresses domain transition, others otherwise.
 243 *
 244 * Caller holds tomoyo_read_lock().
 245 */
 246static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
 247                                 const struct tomoyo_path_info *program)
 248{
 249        const struct tomoyo_transition_control *ptr;
 250        const char *last_name = tomoyo_last_word(domainname->name);
 251        u8 type;
 252        for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) {
 253 next:
 254                list_for_each_entry_rcu(ptr, &tomoyo_policy_list
 255                                        [TOMOYO_ID_TRANSITION_CONTROL],
 256                                        head.list) {
 257                        if (ptr->head.is_deleted || ptr->type != type)
 258                                continue;
 259                        if (ptr->domainname) {
 260                                if (!ptr->is_last_name) {
 261                                        if (ptr->domainname != domainname)
 262                                                continue;
 263                                } else {
 264                                        /*
 265                                         * Use direct strcmp() since this is
 266                                         * unlikely used.
 267                                         */
 268                                        if (strcmp(ptr->domainname->name,
 269                                                   last_name))
 270                                                continue;
 271                                }
 272                        }
 273                        if (ptr->program &&
 274                            tomoyo_pathcmp(ptr->program, program))
 275                                continue;
 276                        if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) {
 277                                /*
 278                                 * Do not check for initialize_domain if
 279                                 * no_initialize_domain matched.
 280                                 */
 281                                type = TOMOYO_TRANSITION_CONTROL_NO_KEEP;
 282                                goto next;
 283                        }
 284                        goto done;
 285                }
 286        }
 287 done:
 288        return type;
 289}
 290
 291static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
 292                                   const struct tomoyo_acl_head *b)
 293{
 294        const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head);
 295        const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head);
 296        return p1->original_name == p2->original_name &&
 297                p1->aggregated_name == p2->aggregated_name;
 298}
 299
 300/**
 301 * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.
 302 *
 303 * @original_name:   The original program's name.
 304 * @aggregated_name: The program name to use.
 305 * @is_delete:       True if it is a delete request.
 306 *
 307 * Returns 0 on success, negative value otherwise.
 308 *
 309 * Caller holds tomoyo_read_lock().
 310 */
 311static int tomoyo_update_aggregator_entry(const char *original_name,
 312                                          const char *aggregated_name,
 313                                          const bool is_delete)
 314{
 315        struct tomoyo_aggregator e = { };
 316        int error = is_delete ? -ENOENT : -ENOMEM;
 317
 318        if (!tomoyo_correct_path(original_name) ||
 319            !tomoyo_correct_path(aggregated_name))
 320                return -EINVAL;
 321        e.original_name = tomoyo_get_name(original_name);
 322        e.aggregated_name = tomoyo_get_name(aggregated_name);
 323        if (!e.original_name || !e.aggregated_name ||
 324            e.aggregated_name->is_patterned) /* No patterns allowed. */
 325                goto out;
 326        error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
 327                                     &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR],
 328                                     tomoyo_same_aggregator);
 329 out:
 330        tomoyo_put_name(e.original_name);
 331        tomoyo_put_name(e.aggregated_name);
 332        return error;
 333}
 334
 335/**
 336 * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
 337 *
 338 * @data:      String to parse.
 339 * @is_delete: True if it is a delete request.
 340 *
 341 * Returns 0 on success, negative value otherwise.
 342 *
 343 * Caller holds tomoyo_read_lock().
 344 */
 345int tomoyo_write_aggregator(char *data, const bool is_delete)
 346{
 347        char *cp = strchr(data, ' ');
 348
 349        if (!cp)
 350                return -EINVAL;
 351        *cp++ = '\0';
 352        return tomoyo_update_aggregator_entry(data, cp, is_delete);
 353}
 354
 355/**
 356 * tomoyo_assign_domain - Create a domain.
 357 *
 358 * @domainname: The name of domain.
 359 * @profile:    Profile number to assign if the domain was newly created.
 360 *
 361 * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
 362 *
 363 * Caller holds tomoyo_read_lock().
 364 */
 365struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
 366                                                const u8 profile)
 367{
 368        struct tomoyo_domain_info *entry;
 369        struct tomoyo_domain_info *domain = NULL;
 370        const struct tomoyo_path_info *saved_domainname;
 371        bool found = false;
 372
 373        if (!tomoyo_correct_domain(domainname))
 374                return NULL;
 375        saved_domainname = tomoyo_get_name(domainname);
 376        if (!saved_domainname)
 377                return NULL;
 378        entry = kzalloc(sizeof(*entry), GFP_NOFS);
 379        if (mutex_lock_interruptible(&tomoyo_policy_lock))
 380                goto out;
 381        list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
 382                if (domain->is_deleted ||
 383                    tomoyo_pathcmp(saved_domainname, domain->domainname))
 384                        continue;
 385                found = true;
 386                break;
 387        }
 388        if (!found && tomoyo_memory_ok(entry)) {
 389                INIT_LIST_HEAD(&entry->acl_info_list);
 390                entry->domainname = saved_domainname;
 391                saved_domainname = NULL;
 392                entry->profile = profile;
 393                list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
 394                domain = entry;
 395                entry = NULL;
 396                found = true;
 397        }
 398        mutex_unlock(&tomoyo_policy_lock);
 399 out:
 400        tomoyo_put_name(saved_domainname);
 401        kfree(entry);
 402        return found ? domain : NULL;
 403}
 404
 405/**
 406 * tomoyo_find_next_domain - Find a domain.
 407 *
 408 * @bprm: Pointer to "struct linux_binprm".
 409 *
 410 * Returns 0 on success, negative value otherwise.
 411 *
 412 * Caller holds tomoyo_read_lock().
 413 */
 414int tomoyo_find_next_domain(struct linux_binprm *bprm)
 415{
 416        struct tomoyo_request_info r;
 417        char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
 418        struct tomoyo_domain_info *old_domain = tomoyo_domain();
 419        struct tomoyo_domain_info *domain = NULL;
 420        const char *original_name = bprm->filename;
 421        u8 mode;
 422        bool is_enforce;
 423        int retval = -ENOMEM;
 424        bool need_kfree = false;
 425        struct tomoyo_path_info rn = { }; /* real name */
 426
 427        mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
 428        is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
 429        if (!tmp)
 430                goto out;
 431
 432 retry:
 433        if (need_kfree) {
 434                kfree(rn.name);
 435                need_kfree = false;
 436        }
 437        /* Get symlink's pathname of program. */
 438        retval = -ENOENT;
 439        rn.name = tomoyo_realpath_nofollow(original_name);
 440        if (!rn.name)
 441                goto out;
 442        tomoyo_fill_path_info(&rn);
 443        need_kfree = true;
 444
 445        /* Check 'aggregator' directive. */
 446        {
 447                struct tomoyo_aggregator *ptr;
 448                list_for_each_entry_rcu(ptr, &tomoyo_policy_list
 449                                        [TOMOYO_ID_AGGREGATOR], head.list) {
 450                        if (ptr->head.is_deleted ||
 451                            !tomoyo_path_matches_pattern(&rn,
 452                                                         ptr->original_name))
 453                                continue;
 454                        kfree(rn.name);
 455                        need_kfree = false;
 456                        /* This is OK because it is read only. */
 457                        rn = *ptr->aggregated_name;
 458                        break;
 459                }
 460        }
 461
 462        /* Check execute permission. */
 463        retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
 464        if (retval == TOMOYO_RETRY_REQUEST)
 465                goto retry;
 466        if (retval < 0)
 467                goto out;
 468        /*
 469         * To be able to specify domainnames with wildcards, use the
 470         * pathname specified in the policy (which may contain
 471         * wildcard) rather than the pathname passed to execve()
 472         * (which never contains wildcard).
 473         */
 474        if (r.param.path.matched_path) {
 475                if (need_kfree)
 476                        kfree(rn.name);
 477                need_kfree = false;
 478                /* This is OK because it is read only. */
 479                rn = *r.param.path.matched_path;
 480        }
 481
 482        /* Calculate domain to transit to. */
 483        switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
 484        case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
 485                /* Transit to the child of tomoyo_kernel_domain domain. */
 486                snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
 487                         "%s", rn.name);
 488                break;
 489        case TOMOYO_TRANSITION_CONTROL_KEEP:
 490                /* Keep current domain. */
 491                domain = old_domain;
 492                break;
 493        default:
 494                if (old_domain == &tomoyo_kernel_domain &&
 495                    !tomoyo_policy_loaded) {
 496                        /*
 497                         * Needn't to transit from kernel domain before
 498                         * starting /sbin/init. But transit from kernel domain
 499                         * if executing initializers because they might start
 500                         * before /sbin/init.
 501                         */
 502                        domain = old_domain;
 503                } else {
 504                        /* Normal domain transition. */
 505                        snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
 506                                 old_domain->domainname->name, rn.name);
 507                }
 508                break;
 509        }
 510        if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
 511                goto done;
 512        domain = tomoyo_find_domain(tmp);
 513        if (domain)
 514                goto done;
 515        if (is_enforce) {
 516                int error = tomoyo_supervisor(&r, "# wants to create domain\n"
 517                                              "%s\n", tmp);
 518                if (error == TOMOYO_RETRY_REQUEST)
 519                        goto retry;
 520                if (error < 0)
 521                        goto done;
 522        }
 523        domain = tomoyo_assign_domain(tmp, old_domain->profile);
 524 done:
 525        if (domain)
 526                goto out;
 527        printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
 528        if (is_enforce)
 529                retval = -EPERM;
 530        else
 531                old_domain->transition_failed = true;
 532 out:
 533        if (!domain)
 534                domain = old_domain;
 535        /* Update reference count on "struct tomoyo_domain_info". */
 536        atomic_inc(&domain->users);
 537        bprm->cred->security = domain;
 538        if (need_kfree)
 539                kfree(rn.name);
 540        kfree(tmp);
 541        return retval;
 542}
 543