linux/security/apparmor/lib.c
<<
>>
Prefs
   1/*
   2 * AppArmor security module
   3 *
   4 * This file contains basic common functions used in AppArmor
   5 *
   6 * Copyright (C) 1998-2008 Novell/SUSE
   7 * Copyright 2009-2010 Canonical Ltd.
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation, version 2 of the
  12 * License.
  13 */
  14
  15#include <linux/ctype.h>
  16#include <linux/mm.h>
  17#include <linux/slab.h>
  18#include <linux/string.h>
  19#include <linux/vmalloc.h>
  20
  21#include "include/audit.h"
  22#include "include/apparmor.h"
  23#include "include/lib.h"
  24#include "include/perms.h"
  25#include "include/policy.h"
  26
  27struct aa_perms nullperms;
  28struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
  29                             .quiet = ALL_PERMS_MASK,
  30                             .hide = ALL_PERMS_MASK };
  31
  32/**
  33 * aa_split_fqname - split a fqname into a profile and namespace name
  34 * @fqname: a full qualified name in namespace profile format (NOT NULL)
  35 * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
  36 *
  37 * Returns: profile name or NULL if one is not specified
  38 *
  39 * Split a namespace name from a profile name (see policy.c for naming
  40 * description).  If a portion of the name is missing it returns NULL for
  41 * that portion.
  42 *
  43 * NOTE: may modify the @fqname string.  The pointers returned point
  44 *       into the @fqname string.
  45 */
  46char *aa_split_fqname(char *fqname, char **ns_name)
  47{
  48        char *name = strim(fqname);
  49
  50        *ns_name = NULL;
  51        if (name[0] == ':') {
  52                char *split = strchr(&name[1], ':');
  53                *ns_name = skip_spaces(&name[1]);
  54                if (split) {
  55                        /* overwrite ':' with \0 */
  56                        *split++ = 0;
  57                        if (strncmp(split, "//", 2) == 0)
  58                                split += 2;
  59                        name = skip_spaces(split);
  60                } else
  61                        /* a ns name without a following profile is allowed */
  62                        name = NULL;
  63        }
  64        if (name && *name == 0)
  65                name = NULL;
  66
  67        return name;
  68}
  69
  70/**
  71 * skipn_spaces - Removes leading whitespace from @str.
  72 * @str: The string to be stripped.
  73 *
  74 * Returns a pointer to the first non-whitespace character in @str.
  75 * if all whitespace will return NULL
  76 */
  77
  78const char *skipn_spaces(const char *str, size_t n)
  79{
  80        for (; n && isspace(*str); --n)
  81                ++str;
  82        if (n)
  83                return (char *)str;
  84        return NULL;
  85}
  86
  87const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
  88                             size_t *ns_len)
  89{
  90        const char *end = fqname + n;
  91        const char *name = skipn_spaces(fqname, n);
  92
  93        if (!name)
  94                return NULL;
  95        *ns_name = NULL;
  96        *ns_len = 0;
  97        if (name[0] == ':') {
  98                char *split = strnchr(&name[1], end - &name[1], ':');
  99                *ns_name = skipn_spaces(&name[1], end - &name[1]);
 100                if (!*ns_name)
 101                        return NULL;
 102                if (split) {
 103                        *ns_len = split - *ns_name;
 104                        if (*ns_len == 0)
 105                                *ns_name = NULL;
 106                        split++;
 107                        if (end - split > 1 && strncmp(split, "//", 2) == 0)
 108                                split += 2;
 109                        name = skipn_spaces(split, end - split);
 110                } else {
 111                        /* a ns name without a following profile is allowed */
 112                        name = NULL;
 113                        *ns_len = end - *ns_name;
 114                }
 115        }
 116        if (name && *name == 0)
 117                name = NULL;
 118
 119        return name;
 120}
 121
 122/**
 123 * aa_info_message - log a none profile related status message
 124 * @str: message to log
 125 */
 126void aa_info_message(const char *str)
 127{
 128        if (audit_enabled) {
 129                DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
 130
 131                aad(&sa)->info = str;
 132                aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
 133        }
 134        printk(KERN_INFO "AppArmor: %s\n", str);
 135}
 136
 137__counted char *aa_str_alloc(int size, gfp_t gfp)
 138{
 139        struct counted_str *str;
 140
 141        str = kmalloc(sizeof(struct counted_str) + size, gfp);
 142        if (!str)
 143                return NULL;
 144
 145        kref_init(&str->count);
 146        return str->name;
 147}
 148
 149void aa_str_kref(struct kref *kref)
 150{
 151        kfree(container_of(kref, struct counted_str, count));
 152}
 153
 154
 155const char aa_file_perm_chrs[] = "xwracd         km l     ";
 156const char *aa_file_perm_names[] = {
 157        "exec",
 158        "write",
 159        "read",
 160        "append",
 161
 162        "create",
 163        "delete",
 164        "open",
 165        "rename",
 166
 167        "setattr",
 168        "getattr",
 169        "setcred",
 170        "getcred",
 171
 172        "chmod",
 173        "chown",
 174        "chgrp",
 175        "lock",
 176
 177        "mmap",
 178        "mprot",
 179        "link",
 180        "snapshot",
 181
 182        "unknown",
 183        "unknown",
 184        "unknown",
 185        "unknown",
 186
 187        "unknown",
 188        "unknown",
 189        "unknown",
 190        "unknown",
 191
 192        "stack",
 193        "change_onexec",
 194        "change_profile",
 195        "change_hat",
 196};
 197
 198/**
 199 * aa_perm_mask_to_str - convert a perm mask to its short string
 200 * @str: character buffer to store string in (at least 10 characters)
 201 * @str_size: size of the @str buffer
 202 * @chrs: NUL-terminated character buffer of permission characters
 203 * @mask: permission mask to convert
 204 */
 205void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
 206{
 207        unsigned int i, perm = 1;
 208        size_t num_chrs = strlen(chrs);
 209
 210        for (i = 0; i < num_chrs; perm <<= 1, i++) {
 211                if (mask & perm) {
 212                        /* Ensure that one byte is left for NUL-termination */
 213                        if (WARN_ON_ONCE(str_size <= 1))
 214                                break;
 215
 216                        *str++ = chrs[i];
 217                        str_size--;
 218                }
 219        }
 220        *str = '\0';
 221}
 222
 223void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
 224                         u32 mask)
 225{
 226        const char *fmt = "%s";
 227        unsigned int i, perm = 1;
 228        bool prev = false;
 229
 230        for (i = 0; i < 32; perm <<= 1, i++) {
 231                if (mask & perm) {
 232                        audit_log_format(ab, fmt, names[i]);
 233                        if (!prev) {
 234                                prev = true;
 235                                fmt = " %s";
 236                        }
 237                }
 238        }
 239}
 240
 241void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
 242                        u32 chrsmask, const char * const *names, u32 namesmask)
 243{
 244        char str[33];
 245
 246        audit_log_format(ab, "\"");
 247        if ((mask & chrsmask) && chrs) {
 248                aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
 249                mask &= ~chrsmask;
 250                audit_log_format(ab, "%s", str);
 251                if (mask & namesmask)
 252                        audit_log_format(ab, " ");
 253        }
 254        if ((mask & namesmask) && names)
 255                aa_audit_perm_names(ab, names, mask & namesmask);
 256        audit_log_format(ab, "\"");
 257}
 258
 259/**
 260 * aa_audit_perms_cb - generic callback fn for auditing perms
 261 * @ab: audit buffer (NOT NULL)
 262 * @va: audit struct to audit values of (NOT NULL)
 263 */
 264static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
 265{
 266        struct common_audit_data *sa = va;
 267
 268        if (aad(sa)->request) {
 269                audit_log_format(ab, " requested_mask=");
 270                aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
 271                                   PERMS_CHRS_MASK, aa_file_perm_names,
 272                                   PERMS_NAMES_MASK);
 273        }
 274        if (aad(sa)->denied) {
 275                audit_log_format(ab, "denied_mask=");
 276                aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
 277                                   PERMS_CHRS_MASK, aa_file_perm_names,
 278                                   PERMS_NAMES_MASK);
 279        }
 280        audit_log_format(ab, " peer=");
 281        aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
 282                                      FLAGS_NONE, GFP_ATOMIC);
 283}
 284
 285/**
 286 * aa_apply_modes_to_perms - apply namespace and profile flags to perms
 287 * @profile: that perms where computed from
 288 * @perms: perms to apply mode modifiers to
 289 *
 290 * TODO: split into profile and ns based flags for when accumulating perms
 291 */
 292void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
 293{
 294        switch (AUDIT_MODE(profile)) {
 295        case AUDIT_ALL:
 296                perms->audit = ALL_PERMS_MASK;
 297                /* fall through */
 298        case AUDIT_NOQUIET:
 299                perms->quiet = 0;
 300                break;
 301        case AUDIT_QUIET:
 302                perms->audit = 0;
 303                /* fall through */
 304        case AUDIT_QUIET_DENIED:
 305                perms->quiet = ALL_PERMS_MASK;
 306                break;
 307        }
 308
 309        if (KILL_MODE(profile))
 310                perms->kill = ALL_PERMS_MASK;
 311        else if (COMPLAIN_MODE(profile))
 312                perms->complain = ALL_PERMS_MASK;
 313/*
 314 *  TODO:
 315 *      else if (PROMPT_MODE(profile))
 316 *              perms->prompt = ALL_PERMS_MASK;
 317 */
 318}
 319
 320static u32 map_other(u32 x)
 321{
 322        return ((x & 0x3) << 8) |       /* SETATTR/GETATTR */
 323                ((x & 0x1c) << 18) |    /* ACCEPT/BIND/LISTEN */
 324                ((x & 0x60) << 19);     /* SETOPT/GETOPT */
 325}
 326
 327void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
 328                      struct aa_perms *perms)
 329{
 330        *perms = (struct aa_perms) {
 331                .allow = dfa_user_allow(dfa, state),
 332                .audit = dfa_user_audit(dfa, state),
 333                .quiet = dfa_user_quiet(dfa, state),
 334        };
 335
 336        /* for v5 perm mapping in the policydb, the other set is used
 337         * to extend the general perm set
 338         */
 339        perms->allow |= map_other(dfa_other_allow(dfa, state));
 340        perms->audit |= map_other(dfa_other_audit(dfa, state));
 341        perms->quiet |= map_other(dfa_other_quiet(dfa, state));
 342//      perms->xindex = dfa_user_xindex(dfa, state);
 343}
 344
 345/**
 346 * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
 347 * @accum - perms struct to accumulate into
 348 * @addend - perms struct to add to @accum
 349 */
 350void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
 351{
 352        accum->deny |= addend->deny;
 353        accum->allow &= addend->allow & ~addend->deny;
 354        accum->audit |= addend->audit & addend->allow;
 355        accum->quiet &= addend->quiet & ~addend->allow;
 356        accum->kill |= addend->kill & ~addend->allow;
 357        accum->stop |= addend->stop & ~addend->allow;
 358        accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
 359        accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
 360        accum->hide &= addend->hide & ~addend->allow;
 361        accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
 362}
 363
 364/**
 365 * aa_perms_accum - accumulate perms, masking off overlapping perms
 366 * @accum - perms struct to accumulate into
 367 * @addend - perms struct to add to @accum
 368 */
 369void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
 370{
 371        accum->deny |= addend->deny;
 372        accum->allow &= addend->allow & ~accum->deny;
 373        accum->audit |= addend->audit & accum->allow;
 374        accum->quiet &= addend->quiet & ~accum->allow;
 375        accum->kill |= addend->kill & ~accum->allow;
 376        accum->stop |= addend->stop & ~accum->allow;
 377        accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
 378        accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
 379        accum->hide &= addend->hide & ~accum->allow;
 380        accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
 381}
 382
 383void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
 384                            int type, u32 request, struct aa_perms *perms)
 385{
 386        /* TODO: doesn't yet handle extended types */
 387        unsigned int state;
 388
 389        state = aa_dfa_next(profile->policy.dfa,
 390                            profile->policy.start[AA_CLASS_LABEL],
 391                            type);
 392        aa_label_match(profile, label, state, false, request, perms);
 393}
 394
 395
 396/* currently unused */
 397int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
 398                          u32 request, int type, u32 *deny,
 399                          struct common_audit_data *sa)
 400{
 401        struct aa_perms perms;
 402
 403        aad(sa)->label = &profile->label;
 404        aad(sa)->peer = &target->label;
 405        aad(sa)->request = request;
 406
 407        aa_profile_match_label(profile, &target->label, type, request, &perms);
 408        aa_apply_modes_to_perms(profile, &perms);
 409        *deny |= request & perms.deny;
 410        return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
 411}
 412
 413/**
 414 * aa_check_perms - do audit mode selection based on perms set
 415 * @profile: profile being checked
 416 * @perms: perms computed for the request
 417 * @request: requested perms
 418 * @deny: Returns: explicit deny set
 419 * @sa: initialized audit structure (MAY BE NULL if not auditing)
 420 * @cb: callback fn for type specific fields (MAY BE NULL)
 421 *
 422 * Returns: 0 if permission else error code
 423 *
 424 * Note: profile audit modes need to be set before calling by setting the
 425 *       perm masks appropriately.
 426 *
 427 *       If not auditing then complain mode is not enabled and the
 428 *       error code will indicate whether there was an explicit deny
 429 *       with a positive value.
 430 */
 431int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
 432                   u32 request, struct common_audit_data *sa,
 433                   void (*cb)(struct audit_buffer *, void *))
 434{
 435        int type, error;
 436        u32 denied = request & (~perms->allow | perms->deny);
 437
 438        if (likely(!denied)) {
 439                /* mask off perms that are not being force audited */
 440                request &= perms->audit;
 441                if (!request || !sa)
 442                        return 0;
 443
 444                type = AUDIT_APPARMOR_AUDIT;
 445                error = 0;
 446        } else {
 447                error = -EACCES;
 448
 449                if (denied & perms->kill)
 450                        type = AUDIT_APPARMOR_KILL;
 451                else if (denied == (denied & perms->complain))
 452                        type = AUDIT_APPARMOR_ALLOWED;
 453                else
 454                        type = AUDIT_APPARMOR_DENIED;
 455
 456                if (denied == (denied & perms->hide))
 457                        error = -ENOENT;
 458
 459                denied &= ~perms->quiet;
 460                if (!sa || !denied)
 461                        return error;
 462        }
 463
 464        if (sa) {
 465                aad(sa)->label = &profile->label;
 466                aad(sa)->request = request;
 467                aad(sa)->denied = denied;
 468                aad(sa)->error = error;
 469                aa_audit_msg(type, sa, cb);
 470        }
 471
 472        if (type == AUDIT_APPARMOR_ALLOWED)
 473                error = 0;
 474
 475        return error;
 476}
 477
 478
 479/**
 480 * aa_policy_init - initialize a policy structure
 481 * @policy: policy to initialize  (NOT NULL)
 482 * @prefix: prefix name if any is required.  (MAYBE NULL)
 483 * @name: name of the policy, init will make a copy of it  (NOT NULL)
 484 * @gfp: allocation mode
 485 *
 486 * Note: this fn creates a copy of strings passed in
 487 *
 488 * Returns: true if policy init successful
 489 */
 490bool aa_policy_init(struct aa_policy *policy, const char *prefix,
 491                    const char *name, gfp_t gfp)
 492{
 493        char *hname;
 494
 495        /* freed by policy_free */
 496        if (prefix) {
 497                hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
 498                if (hname)
 499                        sprintf(hname, "%s//%s", prefix, name);
 500        } else {
 501                hname = aa_str_alloc(strlen(name) + 1, gfp);
 502                if (hname)
 503                        strcpy(hname, name);
 504        }
 505        if (!hname)
 506                return false;
 507        policy->hname = hname;
 508        /* base.name is a substring of fqname */
 509        policy->name = basename(policy->hname);
 510        INIT_LIST_HEAD(&policy->list);
 511        INIT_LIST_HEAD(&policy->profiles);
 512
 513        return true;
 514}
 515
 516/**
 517 * aa_policy_destroy - free the elements referenced by @policy
 518 * @policy: policy that is to have its elements freed  (NOT NULL)
 519 */
 520void aa_policy_destroy(struct aa_policy *policy)
 521{
 522        AA_BUG(on_list_rcu(&policy->profiles));
 523        AA_BUG(on_list_rcu(&policy->list));
 524
 525        /* don't free name as its a subset of hname */
 526        aa_put_str(policy->hname);
 527}
 528