linux/security/apparmor/file.c
<<
>>
Prefs
   1/*
   2 * AppArmor security module
   3 *
   4 * This file contains AppArmor mediation of files
   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 "include/apparmor.h"
  16#include "include/audit.h"
  17#include "include/file.h"
  18#include "include/match.h"
  19#include "include/path.h"
  20#include "include/policy.h"
  21
  22struct file_perms nullperms;
  23
  24
  25/**
  26 * audit_file_mask - convert mask to permission string
  27 * @buffer: buffer to write string to (NOT NULL)
  28 * @mask: permission mask to convert
  29 */
  30static void audit_file_mask(struct audit_buffer *ab, u32 mask)
  31{
  32        char str[10];
  33
  34        char *m = str;
  35
  36        if (mask & AA_EXEC_MMAP)
  37                *m++ = 'm';
  38        if (mask & (MAY_READ | AA_MAY_META_READ))
  39                *m++ = 'r';
  40        if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD |
  41                    AA_MAY_CHOWN))
  42                *m++ = 'w';
  43        else if (mask & MAY_APPEND)
  44                *m++ = 'a';
  45        if (mask & AA_MAY_CREATE)
  46                *m++ = 'c';
  47        if (mask & AA_MAY_DELETE)
  48                *m++ = 'd';
  49        if (mask & AA_MAY_LINK)
  50                *m++ = 'l';
  51        if (mask & AA_MAY_LOCK)
  52                *m++ = 'k';
  53        if (mask & MAY_EXEC)
  54                *m++ = 'x';
  55        *m = '\0';
  56
  57        audit_log_string(ab, str);
  58}
  59
  60/**
  61 * file_audit_cb - call back for file specific audit fields
  62 * @ab: audit_buffer  (NOT NULL)
  63 * @va: audit struct to audit values of  (NOT NULL)
  64 */
  65static void file_audit_cb(struct audit_buffer *ab, void *va)
  66{
  67        struct common_audit_data *sa = va;
  68        uid_t fsuid = current_fsuid();
  69
  70        if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
  71                audit_log_format(ab, " requested_mask=");
  72                audit_file_mask(ab, sa->aad->fs.request);
  73        }
  74        if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) {
  75                audit_log_format(ab, " denied_mask=");
  76                audit_file_mask(ab, sa->aad->fs.denied);
  77        }
  78        if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
  79                audit_log_format(ab, " fsuid=%d", fsuid);
  80                audit_log_format(ab, " ouid=%d", sa->aad->fs.ouid);
  81        }
  82
  83        if (sa->aad->fs.target) {
  84                audit_log_format(ab, " target=");
  85                audit_log_untrustedstring(ab, sa->aad->fs.target);
  86        }
  87}
  88
  89/**
  90 * aa_audit_file - handle the auditing of file operations
  91 * @profile: the profile being enforced  (NOT NULL)
  92 * @perms: the permissions computed for the request (NOT NULL)
  93 * @gfp: allocation flags
  94 * @op: operation being mediated
  95 * @request: permissions requested
  96 * @name: name of object being mediated (MAYBE NULL)
  97 * @target: name of target (MAYBE NULL)
  98 * @ouid: object uid
  99 * @info: extra information message (MAYBE NULL)
 100 * @error: 0 if operation allowed else failure error code
 101 *
 102 * Returns: %0 or error on failure
 103 */
 104int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
 105                  gfp_t gfp, int op, u32 request, const char *name,
 106                  const char *target, uid_t ouid, const char *info, int error)
 107{
 108        int type = AUDIT_APPARMOR_AUTO;
 109        struct common_audit_data sa;
 110        struct apparmor_audit_data aad = {0,};
 111        sa.type = LSM_AUDIT_DATA_NONE;
 112        sa.aad = &aad;
 113        aad.op = op,
 114        aad.fs.request = request;
 115        aad.name = name;
 116        aad.fs.target = target;
 117        aad.fs.ouid = ouid;
 118        aad.info = info;
 119        aad.error = error;
 120
 121        if (likely(!sa.aad->error)) {
 122                u32 mask = perms->audit;
 123
 124                if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
 125                        mask = 0xffff;
 126
 127                /* mask off perms that are not being force audited */
 128                sa.aad->fs.request &= mask;
 129
 130                if (likely(!sa.aad->fs.request))
 131                        return 0;
 132                type = AUDIT_APPARMOR_AUDIT;
 133        } else {
 134                /* only report permissions that were denied */
 135                sa.aad->fs.request = sa.aad->fs.request & ~perms->allow;
 136
 137                if (sa.aad->fs.request & perms->kill)
 138                        type = AUDIT_APPARMOR_KILL;
 139
 140                /* quiet known rejects, assumes quiet and kill do not overlap */
 141                if ((sa.aad->fs.request & perms->quiet) &&
 142                    AUDIT_MODE(profile) != AUDIT_NOQUIET &&
 143                    AUDIT_MODE(profile) != AUDIT_ALL)
 144                        sa.aad->fs.request &= ~perms->quiet;
 145
 146                if (!sa.aad->fs.request)
 147                        return COMPLAIN_MODE(profile) ? 0 : sa.aad->error;
 148        }
 149
 150        sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow;
 151        return aa_audit(type, profile, gfp, &sa, file_audit_cb);
 152}
 153
 154/**
 155 * map_old_perms - map old file perms layout to the new layout
 156 * @old: permission set in old mapping
 157 *
 158 * Returns: new permission mapping
 159 */
 160static u32 map_old_perms(u32 old)
 161{
 162        u32 new = old & 0xf;
 163        if (old & MAY_READ)
 164                new |= AA_MAY_META_READ;
 165        if (old & MAY_WRITE)
 166                new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE |
 167                        AA_MAY_CHMOD | AA_MAY_CHOWN;
 168        if (old & 0x10)
 169                new |= AA_MAY_LINK;
 170        /* the old mapping lock and link_subset flags where overlaid
 171         * and use was determined by part of a pair that they were in
 172         */
 173        if (old & 0x20)
 174                new |= AA_MAY_LOCK | AA_LINK_SUBSET;
 175        if (old & 0x40) /* AA_EXEC_MMAP */
 176                new |= AA_EXEC_MMAP;
 177
 178        return new;
 179}
 180
 181/**
 182 * compute_perms - convert dfa compressed perms to internal perms
 183 * @dfa: dfa to compute perms for   (NOT NULL)
 184 * @state: state in dfa
 185 * @cond:  conditions to consider  (NOT NULL)
 186 *
 187 * TODO: convert from dfa + state to permission entry, do computation conversion
 188 *       at load time.
 189 *
 190 * Returns: computed permission set
 191 */
 192static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state,
 193                                       struct path_cond *cond)
 194{
 195        struct file_perms perms;
 196
 197        /* FIXME: change over to new dfa format
 198         * currently file perms are encoded in the dfa, new format
 199         * splits the permissions from the dfa.  This mapping can be
 200         * done at profile load
 201         */
 202        perms.kill = 0;
 203
 204        if (current_fsuid() == cond->uid) {
 205                perms.allow = map_old_perms(dfa_user_allow(dfa, state));
 206                perms.audit = map_old_perms(dfa_user_audit(dfa, state));
 207                perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
 208                perms.xindex = dfa_user_xindex(dfa, state);
 209        } else {
 210                perms.allow = map_old_perms(dfa_other_allow(dfa, state));
 211                perms.audit = map_old_perms(dfa_other_audit(dfa, state));
 212                perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
 213                perms.xindex = dfa_other_xindex(dfa, state);
 214        }
 215        perms.allow |= AA_MAY_META_READ;
 216
 217        /* change_profile wasn't determined by ownership in old mapping */
 218        if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
 219                perms.allow |= AA_MAY_CHANGE_PROFILE;
 220        if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
 221                perms.allow |= AA_MAY_ONEXEC;
 222
 223        return perms;
 224}
 225
 226/**
 227 * aa_str_perms - find permission that match @name
 228 * @dfa: to match against  (MAYBE NULL)
 229 * @state: state to start matching in
 230 * @name: string to match against dfa  (NOT NULL)
 231 * @cond: conditions to consider for permission set computation  (NOT NULL)
 232 * @perms: Returns - the permissions found when matching @name
 233 *
 234 * Returns: the final state in @dfa when beginning @start and walking @name
 235 */
 236unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
 237                          const char *name, struct path_cond *cond,
 238                          struct file_perms *perms)
 239{
 240        unsigned int state;
 241        if (!dfa) {
 242                *perms = nullperms;
 243                return DFA_NOMATCH;
 244        }
 245
 246        state = aa_dfa_match(dfa, start, name);
 247        *perms = compute_perms(dfa, state, cond);
 248
 249        return state;
 250}
 251
 252/**
 253 * is_deleted - test if a file has been completely unlinked
 254 * @dentry: dentry of file to test for deletion  (NOT NULL)
 255 *
 256 * Returns: %1 if deleted else %0
 257 */
 258static inline bool is_deleted(struct dentry *dentry)
 259{
 260        if (d_unlinked(dentry) && dentry->d_inode->i_nlink == 0)
 261                return 1;
 262        return 0;
 263}
 264
 265/**
 266 * aa_path_perm - do permissions check & audit for @path
 267 * @op: operation being checked
 268 * @profile: profile being enforced  (NOT NULL)
 269 * @path: path to check permissions of  (NOT NULL)
 270 * @flags: any additional path flags beyond what the profile specifies
 271 * @request: requested permissions
 272 * @cond: conditional info for this request  (NOT NULL)
 273 *
 274 * Returns: %0 else error if access denied or other error
 275 */
 276int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
 277                 int flags, u32 request, struct path_cond *cond)
 278{
 279        char *buffer = NULL;
 280        struct file_perms perms = {};
 281        const char *name, *info = NULL;
 282        int error;
 283
 284        flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
 285        error = aa_path_name(path, flags, &buffer, &name, &info);
 286        if (error) {
 287                if (error == -ENOENT && is_deleted(path->dentry)) {
 288                        /* Access to open files that are deleted are
 289                         * give a pass (implicit delegation)
 290                         */
 291                        error = 0;
 292                        info = NULL;
 293                        perms.allow = request;
 294                }
 295        } else {
 296                aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
 297                             &perms);
 298                if (request & ~perms.allow)
 299                        error = -EACCES;
 300        }
 301        error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name,
 302                              NULL, cond->uid, info, error);
 303        kfree(buffer);
 304
 305        return error;
 306}
 307
 308/**
 309 * xindex_is_subset - helper for aa_path_link
 310 * @link: link permission set
 311 * @target: target permission set
 312 *
 313 * test target x permissions are equal OR a subset of link x permissions
 314 * this is done as part of the subset test, where a hardlink must have
 315 * a subset of permissions that the target has.
 316 *
 317 * Returns: %1 if subset else %0
 318 */
 319static inline bool xindex_is_subset(u32 link, u32 target)
 320{
 321        if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) ||
 322            ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE)))
 323                return 0;
 324
 325        return 1;
 326}
 327
 328/**
 329 * aa_path_link - Handle hard link permission check
 330 * @profile: the profile being enforced  (NOT NULL)
 331 * @old_dentry: the target dentry  (NOT NULL)
 332 * @new_dir: directory the new link will be created in  (NOT NULL)
 333 * @new_dentry: the link being created  (NOT NULL)
 334 *
 335 * Handle the permission test for a link & target pair.  Permission
 336 * is encoded as a pair where the link permission is determined
 337 * first, and if allowed, the target is tested.  The target test
 338 * is done from the point of the link match (not start of DFA)
 339 * making the target permission dependent on the link permission match.
 340 *
 341 * The subset test if required forces that permissions granted
 342 * on link are a subset of the permission granted to target.
 343 *
 344 * Returns: %0 if allowed else error
 345 */
 346int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 347                 struct path *new_dir, struct dentry *new_dentry)
 348{
 349        struct path link = { new_dir->mnt, new_dentry };
 350        struct path target = { new_dir->mnt, old_dentry };
 351        struct path_cond cond = {
 352                old_dentry->d_inode->i_uid,
 353                old_dentry->d_inode->i_mode
 354        };
 355        char *buffer = NULL, *buffer2 = NULL;
 356        const char *lname, *tname = NULL, *info = NULL;
 357        struct file_perms lperms, perms;
 358        u32 request = AA_MAY_LINK;
 359        unsigned int state;
 360        int error;
 361
 362        lperms = nullperms;
 363
 364        /* buffer freed below, lname is pointer in buffer */
 365        error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
 366                             &info);
 367        if (error)
 368                goto audit;
 369
 370        /* buffer2 freed below, tname is pointer in buffer2 */
 371        error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
 372                             &info);
 373        if (error)
 374                goto audit;
 375
 376        error = -EACCES;
 377        /* aa_str_perms - handles the case of the dfa being NULL */
 378        state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
 379                             &cond, &lperms);
 380
 381        if (!(lperms.allow & AA_MAY_LINK))
 382                goto audit;
 383
 384        /* test to see if target can be paired with link */
 385        state = aa_dfa_null_transition(profile->file.dfa, state);
 386        aa_str_perms(profile->file.dfa, state, tname, &cond, &perms);
 387
 388        /* force audit/quiet masks for link are stored in the second entry
 389         * in the link pair.
 390         */
 391        lperms.audit = perms.audit;
 392        lperms.quiet = perms.quiet;
 393        lperms.kill = perms.kill;
 394
 395        if (!(perms.allow & AA_MAY_LINK)) {
 396                info = "target restricted";
 397                goto audit;
 398        }
 399
 400        /* done if link subset test is not required */
 401        if (!(perms.allow & AA_LINK_SUBSET))
 402                goto done_tests;
 403
 404        /* Do link perm subset test requiring allowed permission on link are a
 405         * subset of the allowed permissions on target.
 406         */
 407        aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond,
 408                     &perms);
 409
 410        /* AA_MAY_LINK is not considered in the subset test */
 411        request = lperms.allow & ~AA_MAY_LINK;
 412        lperms.allow &= perms.allow | AA_MAY_LINK;
 413
 414        request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow);
 415        if (request & ~lperms.allow) {
 416                goto audit;
 417        } else if ((lperms.allow & MAY_EXEC) &&
 418                   !xindex_is_subset(lperms.xindex, perms.xindex)) {
 419                lperms.allow &= ~MAY_EXEC;
 420                request |= MAY_EXEC;
 421                info = "link not subset of target";
 422                goto audit;
 423        }
 424
 425done_tests:
 426        error = 0;
 427
 428audit:
 429        error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request,
 430                              lname, tname, cond.uid, info, error);
 431        kfree(buffer);
 432        kfree(buffer2);
 433
 434        return error;
 435}
 436
 437/**
 438 * aa_file_perm - do permission revalidation check & audit for @file
 439 * @op: operation being checked
 440 * @profile: profile being enforced   (NOT NULL)
 441 * @file: file to revalidate access permissions on  (NOT NULL)
 442 * @request: requested permissions
 443 *
 444 * Returns: %0 if access allowed else error
 445 */
 446int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
 447                 u32 request)
 448{
 449        struct path_cond cond = {
 450                .uid = file->f_path.dentry->d_inode->i_uid,
 451                .mode = file->f_path.dentry->d_inode->i_mode
 452        };
 453
 454        return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED,
 455                            request, &cond);
 456}
 457
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.