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 * @mask: permission mask to convert
 202 */
 203void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
 204{
 205        unsigned int i, perm = 1;
 206
 207        for (i = 0; i < 32; perm <<= 1, i++) {
 208                if (mask & perm)
 209                        *str++ = chrs[i];
 210        }
 211        *str = '\0';
 212}
 213
 214void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
 215                         u32 mask)
 216{
 217        const char *fmt = "%s";
 218        unsigned int i, perm = 1;
 219        bool prev = false;
 220
 221        for (i = 0; i < 32; perm <<= 1, i++) {
 222                if (mask & perm) {
 223                        audit_log_format(ab, fmt, names[i]);
 224                        if (!prev) {
 225                                prev = true;
 226                                fmt = " %s";
 227                        }
 228                }
 229        }
 230}
 231
 232void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
 233                        u32 chrsmask, const char * const *names, u32 namesmask)
 234{
 235        char str[33];
 236
 237        audit_log_format(ab, "\"");
 238        if ((mask & chrsmask) && chrs) {
 239                aa_perm_mask_to_str(str, chrs, mask & chrsmask);
 240                mask &= ~chrsmask;
 241                audit_log_format(ab, "%s", str);
 242                if (mask & namesmask)
 243                        audit_log_format(ab, " ");
 244        }
 245        if ((mask & namesmask) && names)
 246                aa_audit_perm_names(ab, names, mask & namesmask);
 247        audit_log_format(ab, "\"");
 248}
 249
 250/**
 251 * aa_audit_perms_cb - generic callback fn for auditing perms
 252 * @ab: audit buffer (NOT NULL)
 253 * @va: audit struct to audit values of (NOT NULL)
 254 */
 255static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
 256{
 257        struct common_audit_data *sa = va;
 258
 259        if (aad(sa)->request) {
 260                audit_log_format(ab, " requested_mask=");
 261                aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
 262                                   PERMS_CHRS_MASK, aa_file_perm_names,
 263                                   PERMS_NAMES_MASK);
 264        }
 265        if (aad(sa)->denied) {
 266                audit_log_format(ab, "denied_mask=");
 267                aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
 268                                   PERMS_CHRS_MASK, aa_file_perm_names,
 269                                   PERMS_NAMES_MASK);
 270        }
 271        audit_log_format(ab, " peer=");
 272        aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
 273                                      FLAGS_NONE, GFP_ATOMIC);
 274}
 275
 276/**
 277 * aa_apply_modes_to_perms - apply namespace and profile flags to perms
 278 * @profile: that perms where computed from
 279 * @perms: perms to apply mode modifiers to
 280 *
 281 * TODO: split into profile and ns based flags for when accumulating perms
 282 */
 283void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
 284{
 285        switch (AUDIT_MODE(profile)) {
 286        case AUDIT_ALL:
 287                perms->audit = ALL_PERMS_MASK;
 288                /* fall through */
 289        case AUDIT_NOQUIET:
 290                perms->quiet = 0;
 291                break;
 292        case AUDIT_QUIET:
 293                perms->audit = 0;
 294                /* fall through */
 295        case AUDIT_QUIET_DENIED:
 296                perms->quiet = ALL_PERMS_MASK;
 297                break;
 298        }
 299
 300        if (KILL_MODE(profile))
 301                perms->kill = ALL_PERMS_MASK;
 302        else if (COMPLAIN_MODE(profile))
 303                perms->complain = ALL_PERMS_MASK;
 304/*
 305 *  TODO:
 306 *      else if (PROMPT_MODE(profile))
 307 *              perms->prompt = ALL_PERMS_MASK;
 308 */
 309}
 310
 311static u32 map_other(u32 x)
 312{
 313        return ((x & 0x3) << 8) |       /* SETATTR/GETATTR */
 314                ((x & 0x1c) << 18) |    /* ACCEPT/BIND/LISTEN */
 315                ((x & 0x60) << 19);     /* SETOPT/GETOPT */
 316}
 317
 318void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
 319                      struct aa_perms *perms)
 320{
 321        *perms = (struct aa_perms) {
 322                .allow = dfa_user_allow(dfa, state),
 323                .audit = dfa_user_audit(dfa, state),
 324                .quiet = dfa_user_quiet(dfa, state),
 325        };
 326
 327        /* for v5 perm mapping in the policydb, the other set is used
 328         * to extend the general perm set
 329         */
 330        perms->allow |= map_other(dfa_other_allow(dfa, state));
 331        perms->audit |= map_other(dfa_other_audit(dfa, state));
 332        perms->quiet |= map_other(dfa_other_quiet(dfa, state));
 333//      perms->xindex = dfa_user_xindex(dfa, state);
 334}
 335
 336/**
 337 * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
 338 * @accum - perms struct to accumulate into
 339 * @addend - perms struct to add to @accum
 340 */
 341void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
 342{
 343        accum->deny |= addend->deny;
 344        accum->allow &= addend->allow & ~addend->deny;
 345        accum->audit |= addend->audit & addend->allow;
 346        accum->quiet &= addend->quiet & ~addend->allow;
 347        accum->kill |= addend->kill & ~addend->allow;
 348        accum->stop |= addend->stop & ~addend->allow;
 349        accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
 350        accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
 351        accum->hide &= addend->hide & ~addend->allow;
 352        accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
 353}
 354
 355/**
 356 * aa_perms_accum - accumulate perms, masking off overlapping perms
 357 * @accum - perms struct to accumulate into
 358 * @addend - perms struct to add to @accum
 359 */
 360void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
 361{
 362        accum->deny |= addend->deny;
 363        accum->allow &= addend->allow & ~accum->deny;
 364        accum->audit |= addend->audit & accum->allow;
 365        accum->quiet &= addend->quiet & ~accum->allow;
 366        accum->kill |= addend->kill & ~accum->allow;
 367        accum->stop |= addend->stop & ~accum->allow;
 368        accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
 369        accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
 370        accum->hide &= addend->hide & ~accum->allow;
 371        accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
 372}
 373
 374void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
 375                            int type, u32 request, struct aa_perms *perms)
 376{
 377        /* TODO: doesn't yet handle extended types */
 378        unsigned int state;
 379
 380        state = aa_dfa_next(profile->policy.dfa,
 381                            profile->policy.start[AA_CLASS_LABEL],
 382                            type);
 383        aa_label_match(profile, label, state, false, request, perms);
 384}
 385
 386
 387/* currently unused */
 388int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
 389                          u32 request, int type, u32 *deny,
 390                          struct common_audit_data *sa)
 391{
 392        struct aa_perms perms;
 393
 394        aad(sa)->label = &profile->label;
 395        aad(sa)->peer = &target->label;
 396        aad(sa)->request = request;
 397
 398        aa_profile_match_label(profile, &target->label, type, request, &perms);
 399        aa_apply_modes_to_perms(profile, &perms);
 400        *deny |= request & perms.deny;
 401        return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
 402}
 403
 404/**
 405 * aa_check_perms - do audit mode selection based on perms set
 406 * @profile: profile being checked
 407 * @perms: perms computed for the request
 408 * @request: requested perms
 409 * @deny: Returns: explicit deny set
 410 * @sa: initialized audit structure (MAY BE NULL if not auditing)
 411 * @cb: callback fn for tpye specific fields (MAY BE NULL)
 412 *
 413 * Returns: 0 if permission else error code
 414 *
 415 * Note: profile audit modes need to be set before calling by setting the
 416 *       perm masks appropriately.
 417 *
 418 *       If not auditing then complain mode is not enabled and the
 419 *       error code will indicate whether there was an explicit deny
 420 *       with a positive value.
 421 */
 422int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
 423                   u32 request, struct common_audit_data *sa,
 424                   void (*cb)(struct audit_buffer *, void *))
 425{
 426        int type, error;
 427        u32 denied = request & (~perms->allow | perms->deny);
 428
 429        if (likely(!denied)) {
 430                /* mask off perms that are not being force audited */
 431                request &= perms->audit;
 432                if (!request || !sa)
 433                        return 0;
 434
 435                type = AUDIT_APPARMOR_AUDIT;
 436                error = 0;
 437        } else {
 438                error = -EACCES;
 439
 440                if (denied & perms->kill)
 441                        type = AUDIT_APPARMOR_KILL;
 442                else if (denied == (denied & perms->complain))
 443                        type = AUDIT_APPARMOR_ALLOWED;
 444                else
 445                        type = AUDIT_APPARMOR_DENIED;
 446
 447                if (denied == (denied & perms->hide))
 448                        error = -ENOENT;
 449
 450                denied &= ~perms->quiet;
 451                if (!sa || !denied)
 452                        return error;
 453        }
 454
 455        if (sa) {
 456                aad(sa)->label = &profile->label;
 457                aad(sa)->request = request;
 458                aad(sa)->denied = denied;
 459                aad(sa)->error = error;
 460                aa_audit_msg(type, sa, cb);
 461        }
 462
 463        if (type == AUDIT_APPARMOR_ALLOWED)
 464                error = 0;
 465
 466        return error;
 467}
 468
 469
 470/**
 471 * aa_policy_init - initialize a policy structure
 472 * @policy: policy to initialize  (NOT NULL)
 473 * @prefix: prefix name if any is required.  (MAYBE NULL)
 474 * @name: name of the policy, init will make a copy of it  (NOT NULL)
 475 * @gfp: allocation mode
 476 *
 477 * Note: this fn creates a copy of strings passed in
 478 *
 479 * Returns: true if policy init successful
 480 */
 481bool aa_policy_init(struct aa_policy *policy, const char *prefix,
 482                    const char *name, gfp_t gfp)
 483{
 484        char *hname;
 485
 486        /* freed by policy_free */
 487        if (prefix) {
 488                hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
 489                if (hname)
 490                        sprintf(hname, "%s//%s", prefix, name);
 491        } else {
 492                hname = aa_str_alloc(strlen(name) + 1, gfp);
 493                if (hname)
 494                        strcpy(hname, name);
 495        }
 496        if (!hname)
 497                return false;
 498        policy->hname = hname;
 499        /* base.name is a substring of fqname */
 500        policy->name = basename(policy->hname);
 501        INIT_LIST_HEAD(&policy->list);
 502        INIT_LIST_HEAD(&policy->profiles);
 503
 504        return true;
 505}
 506
 507/**
 508 * aa_policy_destroy - free the elements referenced by @policy
 509 * @policy: policy that is to have its elements freed  (NOT NULL)
 510 */
 511void aa_policy_destroy(struct aa_policy *policy)
 512{
 513        AA_BUG(on_list_rcu(&policy->profiles));
 514        AA_BUG(on_list_rcu(&policy->list));
 515
 516        /* don't free name as its a subset of hname */
 517        aa_put_str(policy->hname);
 518}
 519