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