linux/security/smack/smack_access.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
   3 *
   4 *      This program is free software; you can redistribute it and/or modify
   5 *      it under the terms of the GNU General Public License as published by
   6 *      the Free Software Foundation, version 2.
   7 *
   8 * Author:
   9 *      Casey Schaufler <casey@schaufler-ca.com>
  10 *
  11 */
  12
  13#include <linux/types.h>
  14#include <linux/slab.h>
  15#include <linux/fs.h>
  16#include <linux/sched.h>
  17#include "smack.h"
  18
  19struct smack_known smack_known_huh = {
  20        .smk_known      = "?",
  21        .smk_secid      = 2,
  22};
  23
  24struct smack_known smack_known_hat = {
  25        .smk_known      = "^",
  26        .smk_secid      = 3,
  27};
  28
  29struct smack_known smack_known_star = {
  30        .smk_known      = "*",
  31        .smk_secid      = 4,
  32};
  33
  34struct smack_known smack_known_floor = {
  35        .smk_known      = "_",
  36        .smk_secid      = 5,
  37};
  38
  39struct smack_known smack_known_invalid = {
  40        .smk_known      = "",
  41        .smk_secid      = 6,
  42};
  43
  44struct smack_known smack_known_web = {
  45        .smk_known      = "@",
  46        .smk_secid      = 7,
  47};
  48
  49LIST_HEAD(smack_known_list);
  50
  51/*
  52 * The initial value needs to be bigger than any of the
  53 * known values above.
  54 */
  55static u32 smack_next_secid = 10;
  56
  57/*
  58 * what events do we log
  59 * can be overwritten at run-time by /smack/logging
  60 */
  61int log_policy = SMACK_AUDIT_DENIED;
  62
  63/**
  64 * smk_access_entry - look up matching access rule
  65 * @subject_label: a pointer to the subject's Smack label
  66 * @object_label: a pointer to the object's Smack label
  67 * @rule_list: the list of rules to search
  68 *
  69 * This function looks up the subject/object pair in the
  70 * access rule list and returns the access mode. If no
  71 * entry is found returns -ENOENT.
  72 *
  73 * NOTE:
  74 *
  75 * Earlier versions of this function allowed for labels that
  76 * were not on the label list. This was done to allow for
  77 * labels to come over the network that had never been seen
  78 * before on this host. Unless the receiving socket has the
  79 * star label this will always result in a failure check. The
  80 * star labeled socket case is now handled in the networking
  81 * hooks so there is no case where the label is not on the
  82 * label list. Checking to see if the address of two labels
  83 * is the same is now a reliable test.
  84 *
  85 * Do the object check first because that is more
  86 * likely to differ.
  87 */
  88int smk_access_entry(char *subject_label, char *object_label,
  89                        struct list_head *rule_list)
  90{
  91        int may = -ENOENT;
  92        struct smack_rule *srp;
  93
  94        list_for_each_entry_rcu(srp, rule_list, list) {
  95                if (srp->smk_object == object_label &&
  96                    srp->smk_subject == subject_label) {
  97                        may = srp->smk_access;
  98                        break;
  99                }
 100        }
 101
 102        return may;
 103}
 104
 105/**
 106 * smk_access - determine if a subject has a specific access to an object
 107 * @subject_label: a pointer to the subject's Smack label
 108 * @object_label: a pointer to the object's Smack label
 109 * @request: the access requested, in "MAY" format
 110 * @a : a pointer to the audit data
 111 *
 112 * This function looks up the subject/object pair in the
 113 * access rule list and returns 0 if the access is permitted,
 114 * non zero otherwise.
 115 *
 116 * Smack labels are shared on smack_list
 117 */
 118int smk_access(char *subject_label, char *object_label, int request,
 119               struct smk_audit_info *a)
 120{
 121        struct smack_known *skp;
 122        int may = MAY_NOT;
 123        int rc = 0;
 124
 125        /*
 126         * Hardcoded comparisons.
 127         *
 128         * A star subject can't access any object.
 129         */
 130        if (subject_label == smack_known_star.smk_known) {
 131                rc = -EACCES;
 132                goto out_audit;
 133        }
 134        /*
 135         * An internet object can be accessed by any subject.
 136         * Tasks cannot be assigned the internet label.
 137         * An internet subject can access any object.
 138         */
 139        if (object_label == smack_known_web.smk_known ||
 140            subject_label == smack_known_web.smk_known)
 141                goto out_audit;
 142        /*
 143         * A star object can be accessed by any subject.
 144         */
 145        if (object_label == smack_known_star.smk_known)
 146                goto out_audit;
 147        /*
 148         * An object can be accessed in any way by a subject
 149         * with the same label.
 150         */
 151        if (subject_label == object_label)
 152                goto out_audit;
 153        /*
 154         * A hat subject can read any object.
 155         * A floor object can be read by any subject.
 156         */
 157        if ((request & MAY_ANYREAD) == request) {
 158                if (object_label == smack_known_floor.smk_known)
 159                        goto out_audit;
 160                if (subject_label == smack_known_hat.smk_known)
 161                        goto out_audit;
 162        }
 163        /*
 164         * Beyond here an explicit relationship is required.
 165         * If the requested access is contained in the available
 166         * access (e.g. read is included in readwrite) it's
 167         * good. A negative response from smk_access_entry()
 168         * indicates there is no entry for this pair.
 169         */
 170        skp = smk_find_entry(subject_label);
 171        rcu_read_lock();
 172        may = smk_access_entry(subject_label, object_label, &skp->smk_rules);
 173        rcu_read_unlock();
 174
 175        if (may > 0 && (request & may) == request)
 176                goto out_audit;
 177
 178        rc = -EACCES;
 179out_audit:
 180#ifdef CONFIG_AUDIT
 181        if (a)
 182                smack_log(subject_label, object_label, request, rc, a);
 183#endif
 184        return rc;
 185}
 186
 187/**
 188 * smk_curacc - determine if current has a specific access to an object
 189 * @obj_label: a pointer to the object's Smack label
 190 * @mode: the access requested, in "MAY" format
 191 * @a : common audit data
 192 *
 193 * This function checks the current subject label/object label pair
 194 * in the access rule list and returns 0 if the access is permitted,
 195 * non zero otherwise. It allows that current may have the capability
 196 * to override the rules.
 197 */
 198int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
 199{
 200        struct task_smack *tsp = current_security();
 201        char *sp = smk_of_task(tsp);
 202        int may;
 203        int rc;
 204
 205        /*
 206         * Check the global rule list
 207         */
 208        rc = smk_access(sp, obj_label, mode, NULL);
 209        if (rc == 0) {
 210                /*
 211                 * If there is an entry in the task's rule list
 212                 * it can further restrict access.
 213                 */
 214                may = smk_access_entry(sp, obj_label, &tsp->smk_rules);
 215                if (may < 0)
 216                        goto out_audit;
 217                if ((mode & may) == mode)
 218                        goto out_audit;
 219                rc = -EACCES;
 220        }
 221
 222        /*
 223         * Allow for priviliged to override policy.
 224         */
 225        if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE))
 226                rc = 0;
 227
 228out_audit:
 229#ifdef CONFIG_AUDIT
 230        if (a)
 231                smack_log(sp, obj_label, mode, rc, a);
 232#endif
 233        return rc;
 234}
 235
 236#ifdef CONFIG_AUDIT
 237/**
 238 * smack_str_from_perm : helper to transalate an int to a
 239 * readable string
 240 * @string : the string to fill
 241 * @access : the int
 242 *
 243 */
 244static inline void smack_str_from_perm(char *string, int access)
 245{
 246        int i = 0;
 247        if (access & MAY_READ)
 248                string[i++] = 'r';
 249        if (access & MAY_WRITE)
 250                string[i++] = 'w';
 251        if (access & MAY_EXEC)
 252                string[i++] = 'x';
 253        if (access & MAY_APPEND)
 254                string[i++] = 'a';
 255        if (access & MAY_TRANSMUTE)
 256                string[i++] = 't';
 257        string[i] = '\0';
 258}
 259/**
 260 * smack_log_callback - SMACK specific information
 261 * will be called by generic audit code
 262 * @ab : the audit_buffer
 263 * @a  : audit_data
 264 *
 265 */
 266static void smack_log_callback(struct audit_buffer *ab, void *a)
 267{
 268        struct common_audit_data *ad = a;
 269        struct smack_audit_data *sad = ad->smack_audit_data;
 270        audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
 271                         ad->smack_audit_data->function,
 272                         sad->result ? "denied" : "granted");
 273        audit_log_format(ab, " subject=");
 274        audit_log_untrustedstring(ab, sad->subject);
 275        audit_log_format(ab, " object=");
 276        audit_log_untrustedstring(ab, sad->object);
 277        audit_log_format(ab, " requested=%s", sad->request);
 278}
 279
 280/**
 281 *  smack_log - Audit the granting or denial of permissions.
 282 *  @subject_label : smack label of the requester
 283 *  @object_label  : smack label of the object being accessed
 284 *  @request: requested permissions
 285 *  @result: result from smk_access
 286 *  @a:  auxiliary audit data
 287 *
 288 * Audit the granting or denial of permissions in accordance
 289 * with the policy.
 290 */
 291void smack_log(char *subject_label, char *object_label, int request,
 292               int result, struct smk_audit_info *ad)
 293{
 294        char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
 295        struct smack_audit_data *sad;
 296        struct common_audit_data *a = &ad->a;
 297
 298        /* check if we have to log the current event */
 299        if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
 300                return;
 301        if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
 302                return;
 303
 304        sad = a->smack_audit_data;
 305
 306        if (sad->function == NULL)
 307                sad->function = "unknown";
 308
 309        /* end preparing the audit data */
 310        smack_str_from_perm(request_buffer, request);
 311        sad->subject = subject_label;
 312        sad->object  = object_label;
 313        sad->request = request_buffer;
 314        sad->result  = result;
 315
 316        common_lsm_audit(a, smack_log_callback, NULL);
 317}
 318#else /* #ifdef CONFIG_AUDIT */
 319void smack_log(char *subject_label, char *object_label, int request,
 320               int result, struct smk_audit_info *ad)
 321{
 322}
 323#endif
 324
 325DEFINE_MUTEX(smack_known_lock);
 326
 327/**
 328 * smk_find_entry - find a label on the list, return the list entry
 329 * @string: a text string that might be a Smack label
 330 *
 331 * Returns a pointer to the entry in the label list that
 332 * matches the passed string.
 333 */
 334struct smack_known *smk_find_entry(const char *string)
 335{
 336        struct smack_known *skp;
 337
 338        list_for_each_entry_rcu(skp, &smack_known_list, list) {
 339                if (strcmp(skp->smk_known, string) == 0)
 340                        return skp;
 341        }
 342
 343        return NULL;
 344}
 345
 346/**
 347 * smk_parse_smack - parse smack label from a text string
 348 * @string: a text string that might contain a Smack label
 349 * @len: the maximum size, or zero if it is NULL terminated.
 350 *
 351 * Returns a pointer to the clean label, or NULL
 352 */
 353char *smk_parse_smack(const char *string, int len)
 354{
 355        char *smack;
 356        int i;
 357
 358        if (len <= 0)
 359                len = strlen(string) + 1;
 360
 361        /*
 362         * Reserve a leading '-' as an indicator that
 363         * this isn't a label, but an option to interfaces
 364         * including /smack/cipso and /smack/cipso2
 365         */
 366        if (string[0] == '-')
 367                return NULL;
 368
 369        for (i = 0; i < len; i++)
 370                if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
 371                    string[i] == '"' || string[i] == '\\' || string[i] == '\'')
 372                        break;
 373
 374        if (i == 0 || i >= SMK_LONGLABEL)
 375                return NULL;
 376
 377        smack = kzalloc(i + 1, GFP_KERNEL);
 378        if (smack != NULL) {
 379                strncpy(smack, string, i + 1);
 380                smack[i] = '\0';
 381        }
 382        return smack;
 383}
 384
 385/**
 386 * smk_netlbl_mls - convert a catset to netlabel mls categories
 387 * @catset: the Smack categories
 388 * @sap: where to put the netlabel categories
 389 *
 390 * Allocates and fills attr.mls
 391 * Returns 0 on success, error code on failure.
 392 */
 393int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
 394                        int len)
 395{
 396        unsigned char *cp;
 397        unsigned char m;
 398        int cat;
 399        int rc;
 400        int byte;
 401
 402        sap->flags |= NETLBL_SECATTR_MLS_CAT;
 403        sap->attr.mls.lvl = level;
 404        sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
 405        sap->attr.mls.cat->startbit = 0;
 406
 407        for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++)
 408                for (m = 0x80; m != 0; m >>= 1, cat++) {
 409                        if ((m & *cp) == 0)
 410                                continue;
 411                        rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat,
 412                                                          cat, GFP_ATOMIC);
 413                        if (rc < 0) {
 414                                netlbl_secattr_catmap_free(sap->attr.mls.cat);
 415                                return rc;
 416                        }
 417                }
 418
 419        return 0;
 420}
 421
 422/**
 423 * smk_import_entry - import a label, return the list entry
 424 * @string: a text string that might be a Smack label
 425 * @len: the maximum size, or zero if it is NULL terminated.
 426 *
 427 * Returns a pointer to the entry in the label list that
 428 * matches the passed string, adding it if necessary.
 429 */
 430struct smack_known *smk_import_entry(const char *string, int len)
 431{
 432        struct smack_known *skp;
 433        char *smack;
 434        int slen;
 435        int rc;
 436
 437        smack = smk_parse_smack(string, len);
 438        if (smack == NULL)
 439                return NULL;
 440
 441        mutex_lock(&smack_known_lock);
 442
 443        skp = smk_find_entry(smack);
 444        if (skp != NULL)
 445                goto freeout;
 446
 447        skp = kzalloc(sizeof(*skp), GFP_KERNEL);
 448        if (skp == NULL)
 449                goto freeout;
 450
 451        skp->smk_known = smack;
 452        skp->smk_secid = smack_next_secid++;
 453        skp->smk_netlabel.domain = skp->smk_known;
 454        skp->smk_netlabel.flags =
 455                NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
 456        /*
 457         * If direct labeling works use it.
 458         * Otherwise use mapped labeling.
 459         */
 460        slen = strlen(smack);
 461        if (slen < SMK_CIPSOLEN)
 462                rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
 463                               &skp->smk_netlabel, slen);
 464        else
 465                rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
 466                               &skp->smk_netlabel, sizeof(skp->smk_secid));
 467
 468        if (rc >= 0) {
 469                INIT_LIST_HEAD(&skp->smk_rules);
 470                mutex_init(&skp->smk_rules_lock);
 471                /*
 472                 * Make sure that the entry is actually
 473                 * filled before putting it on the list.
 474                 */
 475                list_add_rcu(&skp->list, &smack_known_list);
 476                goto unlockout;
 477        }
 478        /*
 479         * smk_netlbl_mls failed.
 480         */
 481        kfree(skp);
 482        skp = NULL;
 483freeout:
 484        kfree(smack);
 485unlockout:
 486        mutex_unlock(&smack_known_lock);
 487
 488        return skp;
 489}
 490
 491/**
 492 * smk_import - import a smack label
 493 * @string: a text string that might be a Smack label
 494 * @len: the maximum size, or zero if it is NULL terminated.
 495 *
 496 * Returns a pointer to the label in the label list that
 497 * matches the passed string, adding it if necessary.
 498 */
 499char *smk_import(const char *string, int len)
 500{
 501        struct smack_known *skp;
 502
 503        /* labels cannot begin with a '-' */
 504        if (string[0] == '-')
 505                return NULL;
 506        skp = smk_import_entry(string, len);
 507        if (skp == NULL)
 508                return NULL;
 509        return skp->smk_known;
 510}
 511
 512/**
 513 * smack_from_secid - find the Smack label associated with a secid
 514 * @secid: an integer that might be associated with a Smack label
 515 *
 516 * Returns a pointer to the appropriate Smack label if there is one,
 517 * otherwise a pointer to the invalid Smack label.
 518 */
 519char *smack_from_secid(const u32 secid)
 520{
 521        struct smack_known *skp;
 522
 523        rcu_read_lock();
 524        list_for_each_entry_rcu(skp, &smack_known_list, list) {
 525                if (skp->smk_secid == secid) {
 526                        rcu_read_unlock();
 527                        return skp->smk_known;
 528                }
 529        }
 530
 531        /*
 532         * If we got this far someone asked for the translation
 533         * of a secid that is not on the list.
 534         */
 535        rcu_read_unlock();
 536        return smack_known_invalid.smk_known;
 537}
 538
 539/**
 540 * smack_to_secid - find the secid associated with a Smack label
 541 * @smack: the Smack label
 542 *
 543 * Returns the appropriate secid if there is one,
 544 * otherwise 0
 545 */
 546u32 smack_to_secid(const char *smack)
 547{
 548        struct smack_known *skp = smk_find_entry(smack);
 549
 550        if (skp == NULL)
 551                return 0;
 552        return skp->smk_secid;
 553}
 554