LCOV - code coverage report
Current view: top level - security/apparmor - domain.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 32 229 14.0 %
Date: 2014-02-18 Functions: 6 15 40.0 %
Branches: 14 189 7.4 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * AppArmor security module
       3                 :            :  *
       4                 :            :  * This file contains AppArmor policy attachment and domain transitions
       5                 :            :  *
       6                 :            :  * Copyright (C) 2002-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/errno.h>
      16                 :            : #include <linux/fdtable.h>
      17                 :            : #include <linux/file.h>
      18                 :            : #include <linux/mount.h>
      19                 :            : #include <linux/syscalls.h>
      20                 :            : #include <linux/tracehook.h>
      21                 :            : #include <linux/personality.h>
      22                 :            : 
      23                 :            : #include "include/audit.h"
      24                 :            : #include "include/apparmorfs.h"
      25                 :            : #include "include/context.h"
      26                 :            : #include "include/domain.h"
      27                 :            : #include "include/file.h"
      28                 :            : #include "include/ipc.h"
      29                 :            : #include "include/match.h"
      30                 :            : #include "include/path.h"
      31                 :            : #include "include/policy.h"
      32                 :            : 
      33                 :            : /**
      34                 :            :  * aa_free_domain_entries - free entries in a domain table
      35                 :            :  * @domain: the domain table to free  (MAYBE NULL)
      36                 :            :  */
      37                 :          0 : void aa_free_domain_entries(struct aa_domain *domain)
      38                 :            : {
      39                 :            :         int i;
      40         [ #  # ]:          0 :         if (domain) {
      41         [ #  # ]:          0 :                 if (!domain->table)
      42                 :          0 :                         return;
      43                 :            : 
      44         [ #  # ]:          0 :                 for (i = 0; i < domain->size; i++)
      45                 :          0 :                         kzfree(domain->table[i]);
      46                 :          0 :                 kzfree(domain->table);
      47                 :          0 :                 domain->table = NULL;
      48                 :            :         }
      49                 :            : }
      50                 :            : 
      51                 :            : /**
      52                 :            :  * may_change_ptraced_domain - check if can change profile on ptraced task
      53                 :            :  * @to_profile: profile to change to  (NOT NULL)
      54                 :            :  *
      55                 :            :  * Check if current is ptraced and if so if the tracing task is allowed
      56                 :            :  * to trace the new domain
      57                 :            :  *
      58                 :            :  * Returns: %0 or error if change not allowed
      59                 :            :  */
      60                 :          0 : static int may_change_ptraced_domain(struct aa_profile *to_profile)
      61                 :            : {
      62                 :            :         struct task_struct *tracer;
      63                 :            :         struct aa_profile *tracerp = NULL;
      64                 :            :         int error = 0;
      65                 :            : 
      66                 :            :         rcu_read_lock();
      67                 :          0 :         tracer = ptrace_parent(current);
      68         [ #  # ]:          0 :         if (tracer)
      69                 :            :                 /* released below */
      70                 :          0 :                 tracerp = aa_get_task_profile(tracer);
      71                 :            : 
      72                 :            :         /* not ptraced */
      73 [ #  # ][ #  # ]:          0 :         if (!tracer || unconfined(tracerp))
      74                 :            :                 goto out;
      75                 :            : 
      76                 :          0 :         error = aa_may_ptrace(tracerp, to_profile, PTRACE_MODE_ATTACH);
      77                 :            : 
      78                 :            : out:
      79                 :            :         rcu_read_unlock();
      80                 :            :         aa_put_profile(tracerp);
      81                 :            : 
      82                 :          0 :         return error;
      83                 :            : }
      84                 :            : 
      85                 :            : /**
      86                 :            :  * change_profile_perms - find permissions for change_profile
      87                 :            :  * @profile: the current profile  (NOT NULL)
      88                 :            :  * @ns: the namespace being switched to  (NOT NULL)
      89                 :            :  * @name: the name of the profile to change to  (NOT NULL)
      90                 :            :  * @request: requested perms
      91                 :            :  * @start: state to start matching in
      92                 :            :  *
      93                 :            :  * Returns: permission set
      94                 :            :  */
      95                 :          0 : static struct file_perms change_profile_perms(struct aa_profile *profile,
      96                 :            :                                               struct aa_namespace *ns,
      97                 :            :                                               const char *name, u32 request,
      98                 :            :                                               unsigned int start)
      99                 :            : {
     100                 :            :         struct file_perms perms;
     101                 :          0 :         struct path_cond cond = { };
     102                 :            :         unsigned int state;
     103                 :            : 
     104         [ #  # ]:          0 :         if (unconfined(profile)) {
     105                 :          0 :                 perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
     106                 :          0 :                 perms.audit = perms.quiet = perms.kill = 0;
     107                 :          0 :                 return perms;
     108         [ #  # ]:          0 :         } else if (!profile->file.dfa) {
     109                 :          0 :                 return nullperms;
     110         [ #  # ]:          0 :         } else if ((ns == profile->ns)) {
     111                 :            :                 /* try matching against rules with out namespace prepended */
     112                 :          0 :                 aa_str_perms(profile->file.dfa, start, name, &cond, &perms);
     113         [ #  # ]:          0 :                 if (COMBINED_PERM_MASK(perms) & request)
     114                 :          0 :                         return perms;
     115                 :            :         }
     116                 :            : 
     117                 :            :         /* try matching with namespace name and then profile */
     118                 :          0 :         state = aa_dfa_match(profile->file.dfa, start, ns->base.name);
     119                 :          0 :         state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
     120                 :          0 :         aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
     121                 :            : 
     122                 :          0 :         return perms;
     123                 :            : }
     124                 :            : 
     125                 :            : /**
     126                 :            :  * __attach_match_ - find an attachment match
     127                 :            :  * @name - to match against  (NOT NULL)
     128                 :            :  * @head - profile list to walk  (NOT NULL)
     129                 :            :  *
     130                 :            :  * Do a linear search on the profiles in the list.  There is a matching
     131                 :            :  * preference where an exact match is preferred over a name which uses
     132                 :            :  * expressions to match, and matching expressions with the greatest
     133                 :            :  * xmatch_len are preferred.
     134                 :            :  *
     135                 :            :  * Requires: @head not be shared or have appropriate locks held
     136                 :            :  *
     137                 :            :  * Returns: profile or NULL if no match found
     138                 :            :  */
     139                 :          0 : static struct aa_profile *__attach_match(const char *name,
     140                 :            :                                          struct list_head *head)
     141                 :            : {
     142                 :            :         int len = 0;
     143                 :            :         struct aa_profile *profile, *candidate = NULL;
     144                 :            : 
     145         [ -  + ]:      58932 :         list_for_each_entry_rcu(profile, head, base.list) {
     146         [ #  # ]:          0 :                 if (profile->flags & PFLAG_NULL)
     147                 :          0 :                         continue;
     148 [ #  # ][ #  # ]:          0 :                 if (profile->xmatch && profile->xmatch_len > len) {
     149                 :          0 :                         unsigned int state = aa_dfa_match(profile->xmatch,
     150                 :            :                                                           DFA_START, name);
     151                 :          0 :                         u32 perm = dfa_user_allow(profile->xmatch, state);
     152                 :            :                         /* any accepting state means a valid match. */
     153         [ #  # ]:          0 :                         if (perm & MAY_EXEC) {
     154                 :            :                                 candidate = profile;
     155                 :          0 :                                 len = profile->xmatch_len;
     156                 :            :                         }
     157            [ - ]:          0 :                 } else if (!strcmp(profile->base.name, name))
     158                 :            :                         /* exact non-re match, no more searching required */
     159                 :            :                         return profile;
     160                 :            :         }
     161                 :            : 
     162                 :            :         return candidate;
     163                 :            : }
     164                 :            : 
     165                 :            : /**
     166                 :            :  * find_attach - do attachment search for unconfined processes
     167                 :            :  * @ns: the current namespace  (NOT NULL)
     168                 :            :  * @list: list to search  (NOT NULL)
     169                 :            :  * @name: the executable name to match against  (NOT NULL)
     170                 :            :  *
     171                 :            :  * Returns: profile or NULL if no match found
     172                 :            :  */
     173                 :          0 : static struct aa_profile *find_attach(struct aa_namespace *ns,
     174                 :            :                                       struct list_head *list, const char *name)
     175                 :            : {
     176                 :            :         struct aa_profile *profile;
     177                 :            : 
     178                 :            :         rcu_read_lock();
     179                 :      58847 :         profile = aa_get_profile(__attach_match(name, list));
     180                 :            :         rcu_read_unlock();
     181                 :            : 
     182                 :      58947 :         return profile;
     183                 :            : }
     184                 :            : 
     185                 :            : /**
     186                 :            :  * separate_fqname - separate the namespace and profile names
     187                 :            :  * @fqname: the fqname name to split  (NOT NULL)
     188                 :            :  * @ns_name: the namespace name if it exists  (NOT NULL)
     189                 :            :  *
     190                 :            :  * This is the xtable equivalent routine of aa_split_fqname.  It finds the
     191                 :            :  * split in an xtable fqname which contains an embedded \0 instead of a :
     192                 :            :  * if a namespace is specified.  This is done so the xtable is constant and
     193                 :            :  * isn't re-split on every lookup.
     194                 :            :  *
     195                 :            :  * Either the profile or namespace name may be optional but if the namespace
     196                 :            :  * is specified the profile name termination must be present.  This results
     197                 :            :  * in the following possible encodings:
     198                 :            :  * profile_name\0
     199                 :            :  * :ns_name\0profile_name\0
     200                 :            :  * :ns_name\0\0
     201                 :            :  *
     202                 :            :  * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table
     203                 :            :  *
     204                 :            :  * Returns: profile name if it is specified else NULL
     205                 :            :  */
     206                 :          0 : static const char *separate_fqname(const char *fqname, const char **ns_name)
     207                 :            : {
     208                 :            :         const char *name;
     209                 :            : 
     210         [ #  # ]:          0 :         if (fqname[0] == ':') {
     211                 :            :                 /* In this case there is guaranteed to be two \0 terminators
     212                 :            :                  * in the string.  They are verified at load time by
     213                 :            :                  * by unpack_trans_table
     214                 :            :                  */
     215                 :          0 :                 *ns_name = fqname + 1;          /* skip : */
     216                 :          0 :                 name = *ns_name + strlen(*ns_name) + 1;
     217         [ #  # ]:          0 :                 if (!*name)
     218                 :            :                         name = NULL;
     219                 :            :         } else {
     220                 :          0 :                 *ns_name = NULL;
     221                 :            :                 name = fqname;
     222                 :            :         }
     223                 :            : 
     224                 :          0 :         return name;
     225                 :            : }
     226                 :            : 
     227                 :            : static const char *next_name(int xtype, const char *name)
     228                 :            : {
     229                 :            :         return NULL;
     230                 :            : }
     231                 :            : 
     232                 :            : /**
     233                 :            :  * x_table_lookup - lookup an x transition name via transition table
     234                 :            :  * @profile: current profile (NOT NULL)
     235                 :            :  * @xindex: index into x transition table
     236                 :            :  *
     237                 :            :  * Returns: refcounted profile, or NULL on failure (MAYBE NULL)
     238                 :            :  */
     239                 :          0 : static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
     240                 :            : {
     241                 :            :         struct aa_profile *new_profile = NULL;
     242                 :          0 :         struct aa_namespace *ns = profile->ns;
     243                 :            :         u32 xtype = xindex & AA_X_TYPE_MASK;
     244                 :          0 :         int index = xindex & AA_X_INDEX_MASK;
     245                 :            :         const char *name;
     246                 :            : 
     247                 :            :         /* index is guaranteed to be in range, validated at load time */
     248         [ #  # ]:          0 :         for (name = profile->file.trans.table[index]; !new_profile && name;
     249                 :            :              name = next_name(xtype, name)) {
     250                 :            :                 struct aa_namespace *new_ns;
     251                 :            :                 const char *xname = NULL;
     252                 :            : 
     253                 :            :                 new_ns = NULL;
     254         [ #  # ]:          0 :                 if (xindex & AA_X_CHILD) {
     255                 :            :                         /* release by caller */
     256                 :          0 :                         new_profile = aa_find_child(profile, name);
     257                 :          0 :                         continue;
     258         [ #  # ]:          0 :                 } else if (*name == ':') {
     259                 :            :                         /* switching namespace */
     260                 :            :                         const char *ns_name;
     261                 :          0 :                         xname = name = separate_fqname(name, &ns_name);
     262         [ #  # ]:          0 :                         if (!xname)
     263                 :            :                                 /* no name so use profile name */
     264                 :          0 :                                 xname = profile->base.hname;
     265                 :            :                         if (*ns_name == '@') {
     266                 :            :                                 /* TODO: variable support */
     267                 :            :                                 ;
     268                 :            :                         }
     269                 :            :                         /* released below */
     270                 :          0 :                         new_ns = aa_find_namespace(ns, ns_name);
     271         [ #  # ]:          0 :                         if (!new_ns)
     272                 :          0 :                                 continue;
     273         [ #  # ]:          0 :                 } else if (*name == '@') {
     274                 :            :                         /* TODO: variable support */
     275                 :          0 :                         continue;
     276                 :            :                 } else {
     277                 :            :                         /* basic namespace lookup */
     278                 :            :                         xname = name;
     279                 :            :                 }
     280                 :            : 
     281                 :            :                 /* released by caller */
     282         [ #  # ]:          0 :                 new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname);
     283                 :            :                 aa_put_namespace(new_ns);
     284                 :            :         }
     285                 :            : 
     286                 :            :         /* released by caller */
     287                 :          0 :         return new_profile;
     288                 :            : }
     289                 :            : 
     290                 :            : /**
     291                 :            :  * x_to_profile - get target profile for a given xindex
     292                 :            :  * @profile: current profile  (NOT NULL)
     293                 :            :  * @name: name to lookup (NOT NULL)
     294                 :            :  * @xindex: index into x transition table
     295                 :            :  *
     296                 :            :  * find profile for a transition index
     297                 :            :  *
     298                 :            :  * Returns: refcounted profile or NULL if not found available
     299                 :            :  */
     300                 :          0 : static struct aa_profile *x_to_profile(struct aa_profile *profile,
     301                 :            :                                        const char *name, u32 xindex)
     302                 :            : {
     303                 :            :         struct aa_profile *new_profile = NULL;
     304                 :          0 :         struct aa_namespace *ns = profile->ns;
     305                 :          0 :         u32 xtype = xindex & AA_X_TYPE_MASK;
     306                 :            : 
     307   [ #  #  #  # ]:          0 :         switch (xtype) {
     308                 :            :         case AA_X_NONE:
     309                 :            :                 /* fail exec unless ix || ux fallback - handled by caller */
     310                 :            :                 return NULL;
     311                 :            :         case AA_X_NAME:
     312         [ #  # ]:          0 :                 if (xindex & AA_X_CHILD)
     313                 :            :                         /* released by caller */
     314                 :          0 :                         new_profile = find_attach(ns, &profile->base.profiles,
     315                 :            :                                                   name);
     316                 :            :                 else
     317                 :            :                         /* released by caller */
     318                 :          0 :                         new_profile = find_attach(ns, &ns->base.profiles,
     319                 :            :                                                   name);
     320                 :            :                 break;
     321                 :            :         case AA_X_TABLE:
     322                 :            :                 /* released by caller */
     323                 :          0 :                 new_profile = x_table_lookup(profile, xindex);
     324                 :          0 :                 break;
     325                 :            :         }
     326                 :            : 
     327                 :            :         /* released by caller */
     328                 :          0 :         return new_profile;
     329                 :            : }
     330                 :            : 
     331                 :            : /**
     332                 :            :  * apparmor_bprm_set_creds - set the new creds on the bprm struct
     333                 :            :  * @bprm: binprm for the exec  (NOT NULL)
     334                 :            :  *
     335                 :            :  * Returns: %0 or error on failure
     336                 :            :  */
     337                 :          0 : int apparmor_bprm_set_creds(struct linux_binprm *bprm)
     338                 :            : {
     339                 :            :         struct aa_task_cxt *cxt;
     340                 :            :         struct aa_profile *profile, *new_profile = NULL;
     341                 :            :         struct aa_namespace *ns;
     342                 :      60171 :         char *buffer = NULL;
     343                 :            :         unsigned int state;
     344                 :      60171 :         struct file_perms perms = {};
     345                 :     120342 :         struct path_cond cond = {
     346                 :      60171 :                 file_inode(bprm->file)->i_uid,
     347                 :      60171 :                 file_inode(bprm->file)->i_mode
     348                 :            :         };
     349                 :      60171 :         const char *name = NULL, *target = NULL, *info = NULL;
     350                 :      60171 :         int error = cap_bprm_set_creds(bprm);
     351         [ +  + ]:      60080 :         if (error)
     352                 :            :                 return error;
     353                 :            : 
     354         [ +  + ]:      60078 :         if (bprm->cred_prepared)
     355                 :            :                 return 0;
     356                 :            : 
     357                 :      58896 :         cxt = cred_cxt(bprm->cred);
     358         [ -  + ]:      58896 :         BUG_ON(!cxt);
     359                 :            : 
     360                 :      58896 :         profile = aa_get_newest_profile(cxt->profile);
     361                 :            :         /*
     362                 :            :          * get the namespace from the replacement profile as replacement
     363                 :            :          * can change the namespace
     364                 :            :          */
     365                 :      58901 :         ns = profile->ns;
     366                 :      58901 :         state = profile->file.start;
     367                 :            : 
     368                 :            :         /* buffer freed below, name is pointer into buffer */
     369                 :      58901 :         error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
     370                 :            :                              &name, &info);
     371         [ -  + ]:      58971 :         if (error) {
     372 [ #  # ][ #  # ]:          0 :                 if (unconfined(profile) ||
     373                 :          0 :                     (profile->flags & PFLAG_IX_ON_NAME_ERROR))
     374                 :            :                         error = 0;
     375                 :          0 :                 name = bprm->filename;
     376                 :          0 :                 goto audit;
     377                 :            :         }
     378                 :            : 
     379                 :            :         /* Test for onexec first as onexec directives override other
     380                 :            :          * x transitions.
     381                 :            :          */
     382         [ +  - ]:      58971 :         if (unconfined(profile)) {
     383                 :            :                 /* unconfined task */
     384         [ -  + ]:      58971 :                 if (cxt->onexec)
     385                 :            :                         /* change_profile on exec already been granted */
     386                 :            :                         new_profile = aa_get_profile(cxt->onexec);
     387                 :            :                 else
     388                 :      58971 :                         new_profile = find_attach(ns, &ns->base.profiles, name);
     389         [ -  + ]:      58983 :                 if (!new_profile)
     390                 :            :                         goto cleanup;
     391                 :            :                 /*
     392                 :            :                  * NOTE: Domain transitions from unconfined are allowed
     393                 :            :                  * even when no_new_privs is set because this aways results
     394                 :            :                  * in a further reduction of permissions.
     395                 :            :                  */
     396                 :            :                 goto apply;
     397                 :            :         }
     398                 :            : 
     399                 :            :         /* find exec permissions for name */
     400                 :          0 :         state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
     401         [ #  # ]:          0 :         if (cxt->onexec) {
     402                 :            :                 struct file_perms cp;
     403                 :          0 :                 info = "change_profile onexec";
     404         [ #  # ]:          0 :                 if (!(perms.allow & AA_MAY_ONEXEC))
     405                 :            :                         goto audit;
     406                 :            : 
     407                 :            :                 /* test if this exec can be paired with change_profile onexec.
     408                 :            :                  * onexec permission is linked to exec with a standard pairing
     409                 :            :                  * exec\0change_profile
     410                 :            :                  */
     411                 :          0 :                 state = aa_dfa_null_transition(profile->file.dfa, state);
     412                 :          0 :                 cp = change_profile_perms(profile, cxt->onexec->ns,
     413                 :          0 :                                           cxt->onexec->base.name,
     414                 :            :                                           AA_MAY_ONEXEC, state);
     415                 :            : 
     416         [ #  # ]:          0 :                 if (!(cp.allow & AA_MAY_ONEXEC))
     417                 :            :                         goto audit;
     418                 :          0 :                 new_profile = aa_get_newest_profile(cxt->onexec);
     419                 :          0 :                 goto apply;
     420                 :            :         }
     421                 :            : 
     422         [ #  # ]:          0 :         if (perms.allow & MAY_EXEC) {
     423                 :            :                 /* exec permission determine how to transition */
     424                 :          0 :                 new_profile = x_to_profile(profile, name, perms.xindex);
     425         [ #  # ]:          0 :                 if (!new_profile) {
     426         [ #  # ]:          0 :                         if (perms.xindex & AA_X_INHERIT) {
     427                 :            :                                 /* (p|c|n)ix - don't change profile but do
     428                 :            :                                  * use the newest version, which was picked
     429                 :            :                                  * up above when getting profile
     430                 :            :                                  */
     431                 :          0 :                                 info = "ix fallback";
     432                 :            :                                 new_profile = aa_get_profile(profile);
     433                 :          0 :                                 goto x_clear;
     434         [ #  # ]:          0 :                         } else if (perms.xindex & AA_X_UNCONFINED) {
     435                 :          0 :                                 new_profile = aa_get_newest_profile(ns->unconfined);
     436                 :          0 :                                 info = "ux fallback";
     437                 :            :                         } else {
     438                 :            :                                 error = -ENOENT;
     439                 :          0 :                                 info = "profile not found";
     440                 :            :                                 /* remove MAY_EXEC to audit as failure */
     441                 :          0 :                                 perms.allow &= ~MAY_EXEC;
     442                 :            :                         }
     443                 :            :                 }
     444 [ #  # ][ #  # ]:          0 :         } else if (COMPLAIN_MODE(profile)) {
     445                 :            :                 /* no exec permission - are we in learning mode */
     446                 :          0 :                 new_profile = aa_new_null_profile(profile, 0);
     447         [ #  # ]:          0 :                 if (!new_profile) {
     448                 :            :                         error = -ENOMEM;
     449                 :          0 :                         info = "could not create null profile";
     450                 :            :                 } else {
     451                 :            :                         error = -EACCES;
     452                 :          0 :                         target = new_profile->base.hname;
     453                 :            :                 }
     454                 :          0 :                 perms.xindex |= AA_X_UNSAFE;
     455                 :            :         } else
     456                 :            :                 /* fail exec */
     457                 :            :                 error = -EACCES;
     458                 :            : 
     459                 :            :         /*
     460                 :            :          * Policy has specified a domain transition, if no_new_privs then
     461                 :            :          * fail the exec.
     462                 :            :          */
     463         [ #  # ]:          0 :         if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
     464                 :            :                 aa_put_profile(new_profile);
     465                 :            :                 error = -EPERM;
     466                 :            :                 goto cleanup;
     467                 :            :         }
     468                 :            : 
     469         [ #  # ]:          0 :         if (!new_profile)
     470                 :            :                 goto audit;
     471                 :            : 
     472                 :            :         if (bprm->unsafe & LSM_UNSAFE_SHARE) {
     473                 :            :                 /* FIXME: currently don't mediate shared state */
     474                 :            :                 ;
     475                 :            :         }
     476                 :            : 
     477         [ #  # ]:          0 :         if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
     478                 :          0 :                 error = may_change_ptraced_domain(new_profile);
     479         [ #  # ]:          0 :                 if (error) {
     480                 :            :                         aa_put_profile(new_profile);
     481                 :            :                         goto audit;
     482                 :            :                 }
     483                 :            :         }
     484                 :            : 
     485                 :            :         /* Determine if secure exec is needed.
     486                 :            :          * Can be at this point for the following reasons:
     487                 :            :          * 1. unconfined switching to confined
     488                 :            :          * 2. confined switching to different confinement
     489                 :            :          * 3. confined switching to unconfined
     490                 :            :          *
     491                 :            :          * Cases 2 and 3 are marked as requiring secure exec
     492                 :            :          * (unless policy specified "unsafe exec")
     493                 :            :          *
     494                 :            :          * bprm->unsafe is used to cache the AA_X_UNSAFE permission
     495                 :            :          * to avoid having to recompute in secureexec
     496                 :            :          */
     497         [ #  # ]:          0 :         if (!(perms.xindex & AA_X_UNSAFE)) {
     498 [ #  # ][ #  # ]:          0 :                 AA_DEBUG("scrubbing environment variables for %s profile=%s\n",
     499                 :            :                          name, new_profile->base.hname);
     500                 :          0 :                 bprm->unsafe |= AA_SECURE_X_NEEDED;
     501                 :            :         }
     502                 :            : apply:
     503                 :          0 :         target = new_profile->base.hname;
     504                 :            :         /* when transitioning profiles clear unsafe personality bits */
     505                 :          0 :         bprm->per_clear |= PER_CLEAR_ON_SETID;
     506                 :            : 
     507                 :            : x_clear:
     508                 :          0 :         aa_put_profile(cxt->profile);
     509                 :            :         /* transfer new profile reference will be released when cxt is freed */
     510                 :          0 :         cxt->profile = new_profile;
     511                 :            : 
     512                 :            :         /* clear out all temporary/transitional state from the context */
     513                 :            :         aa_clear_task_cxt_trans(cxt);
     514                 :            : 
     515                 :            : audit:
     516                 :          0 :         error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
     517                 :            :                               name, target, cond.uid, info, error);
     518                 :            : 
     519                 :            : cleanup:
     520                 :            :         aa_put_profile(profile);
     521                 :      58968 :         kfree(buffer);
     522                 :            : 
     523                 :      58976 :         return error;
     524                 :            : }
     525                 :            : 
     526                 :            : /**
     527                 :            :  * apparmor_bprm_secureexec - determine if secureexec is needed
     528                 :            :  * @bprm: binprm for exec  (NOT NULL)
     529                 :            :  *
     530                 :            :  * Returns: %1 if secureexec is needed else %0
     531                 :            :  */
     532                 :          0 : int apparmor_bprm_secureexec(struct linux_binprm *bprm)
     533                 :            : {
     534                 :      58974 :         int ret = cap_bprm_secureexec(bprm);
     535                 :            : 
     536                 :            :         /* the decision to use secure exec is computed in set_creds
     537                 :            :          * and stored in bprm->unsafe.
     538                 :            :          */
     539 [ +  + ][ -  + ]:      58976 :         if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED))
     540                 :            :                 ret = 1;
     541                 :            : 
     542                 :          2 :         return ret;
     543                 :            : }
     544                 :            : 
     545                 :            : /**
     546                 :            :  * apparmor_bprm_committing_creds - do task cleanup on committing new creds
     547                 :            :  * @bprm: binprm for the exec  (NOT NULL)
     548                 :            :  */
     549                 :          0 : void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
     550                 :            : {
     551                 :            :         struct aa_profile *profile = __aa_current_profile();
     552                 :      58976 :         struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred);
     553                 :            : 
     554                 :            :         /* bail out if unconfined or not changing profile */
     555 [ -  + ][ #  # ]:      58976 :         if ((new_cxt->profile == profile) ||
     556                 :          0 :             (unconfined(new_cxt->profile)))
     557                 :      58976 :                 return;
     558                 :            : 
     559                 :          0 :         current->pdeath_signal = 0;
     560                 :            : 
     561                 :            :         /* reset soft limits and set hard limits for the new profile */
     562                 :          0 :         __aa_transition_rlimits(profile, new_cxt->profile);
     563                 :            : }
     564                 :            : 
     565                 :            : /**
     566                 :            :  * apparmor_bprm_commited_cred - do cleanup after new creds committed
     567                 :            :  * @bprm: binprm for the exec  (NOT NULL)
     568                 :            :  */
     569                 :          0 : void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
     570                 :            : {
     571                 :            :         /* TODO: cleanup signals - ipc mediation */
     572                 :      58976 :         return;
     573                 :            : }
     574                 :            : 
     575                 :            : /*
     576                 :            :  * Functions for self directed profile change
     577                 :            :  */
     578                 :            : 
     579                 :            : /**
     580                 :            :  * new_compound_name - create an hname with @n2 appended to @n1
     581                 :            :  * @n1: base of hname  (NOT NULL)
     582                 :            :  * @n2: name to append (NOT NULL)
     583                 :            :  *
     584                 :            :  * Returns: new name or NULL on error
     585                 :            :  */
     586                 :          0 : static char *new_compound_name(const char *n1, const char *n2)
     587                 :            : {
     588                 :          0 :         char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL);
     589         [ #  # ]:          0 :         if (name)
     590                 :          0 :                 sprintf(name, "%s//%s", n1, n2);
     591                 :          0 :         return name;
     592                 :            : }
     593                 :            : 
     594                 :            : /**
     595                 :            :  * aa_change_hat - change hat to/from subprofile
     596                 :            :  * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0)
     597                 :            :  * @count: number of hat names in @hats
     598                 :            :  * @token: magic value to validate the hat change
     599                 :            :  * @permtest: true if this is just a permission test
     600                 :            :  *
     601                 :            :  * Change to the first profile specified in @hats that exists, and store
     602                 :            :  * the @hat_magic in the current task context.  If the count == 0 and the
     603                 :            :  * @token matches that stored in the current task context, return to the
     604                 :            :  * top level profile.
     605                 :            :  *
     606                 :            :  * Returns %0 on success, error otherwise.
     607                 :            :  */
     608                 :          0 : int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
     609                 :            : {
     610                 :            :         const struct cred *cred;
     611                 :            :         struct aa_task_cxt *cxt;
     612                 :            :         struct aa_profile *profile, *previous_profile, *hat = NULL;
     613                 :            :         char *name = NULL;
     614                 :            :         int i;
     615                 :          0 :         struct file_perms perms = {};
     616                 :            :         const char *target = NULL, *info = NULL;
     617                 :            :         int error = 0;
     618                 :            : 
     619                 :            :         /*
     620                 :            :          * Fail explicitly requested domain transitions if no_new_privs.
     621                 :            :          * There is no exception for unconfined as change_hat is not
     622                 :            :          * available.
     623                 :            :          */
     624         [ #  # ]:          0 :         if (current->no_new_privs)
     625                 :            :                 return -EPERM;
     626                 :            : 
     627                 :            :         /* released below */
     628                 :          0 :         cred = get_current_cred();
     629                 :          0 :         cxt = cred_cxt(cred);
     630                 :            :         profile = aa_cred_profile(cred);
     631                 :          0 :         previous_profile = cxt->previous;
     632                 :            : 
     633         [ #  # ]:          0 :         if (unconfined(profile)) {
     634                 :            :                 info = "unconfined";
     635                 :            :                 error = -EPERM;
     636                 :            :                 goto audit;
     637                 :            :         }
     638                 :            : 
     639         [ #  # ]:          0 :         if (count) {
     640                 :            :                 /* attempting to change into a new hat or switch to a sibling */
     641                 :            :                 struct aa_profile *root;
     642         [ #  # ]:          0 :                 if (PROFILE_IS_HAT(profile))
     643                 :            :                         root = aa_get_profile_rcu(&profile->parent);
     644                 :            :                 else
     645                 :            :                         root = aa_get_profile(profile);
     646                 :            : 
     647                 :            :                 /* find first matching hat */
     648         [ #  # ]:          0 :                 for (i = 0; i < count && !hat; i++)
     649                 :            :                         /* released below */
     650                 :          0 :                         hat = aa_find_child(root, hats[i]);
     651         [ #  # ]:          0 :                 if (!hat) {
     652 [ #  # ][ #  # ]:          0 :                         if (!COMPLAIN_MODE(root) || permtest) {
                 [ #  # ]
     653         [ #  # ]:          0 :                                 if (list_empty(&root->base.profiles))
     654                 :            :                                         error = -ECHILD;
     655                 :            :                                 else
     656                 :            :                                         error = -ENOENT;
     657                 :            :                                 aa_put_profile(root);
     658                 :            :                                 goto out;
     659                 :            :                         }
     660                 :            : 
     661                 :            :                         /*
     662                 :            :                          * In complain mode and failed to match any hats.
     663                 :            :                          * Audit the failure is based off of the first hat
     664                 :            :                          * supplied.  This is done due how userspace
     665                 :            :                          * interacts with change_hat.
     666                 :            :                          *
     667                 :            :                          * TODO: Add logging of all failed hats
     668                 :            :                          */
     669                 :            : 
     670                 :            :                         /* freed below */
     671                 :          0 :                         name = new_compound_name(root->base.hname, hats[0]);
     672                 :            :                         aa_put_profile(root);
     673                 :            :                         target = name;
     674                 :            :                         /* released below */
     675                 :          0 :                         hat = aa_new_null_profile(profile, 1);
     676         [ #  # ]:          0 :                         if (!hat) {
     677                 :            :                                 info = "failed null profile create";
     678                 :            :                                 error = -ENOMEM;
     679                 :            :                                 goto audit;
     680                 :            :                         }
     681                 :            :                 } else {
     682                 :            :                         aa_put_profile(root);
     683                 :          0 :                         target = hat->base.hname;
     684         [ #  # ]:          0 :                         if (!PROFILE_IS_HAT(hat)) {
     685                 :            :                                 info = "target not hat";
     686                 :            :                                 error = -EPERM;
     687                 :            :                                 goto audit;
     688                 :            :                         }
     689                 :            :                 }
     690                 :            : 
     691                 :          0 :                 error = may_change_ptraced_domain(hat);
     692         [ #  # ]:          0 :                 if (error) {
     693                 :            :                         info = "ptraced";
     694                 :            :                         error = -EPERM;
     695                 :            :                         goto audit;
     696                 :            :                 }
     697                 :            : 
     698         [ #  # ]:          0 :                 if (!permtest) {
     699                 :          0 :                         error = aa_set_current_hat(hat, token);
     700         [ #  # ]:          0 :                         if (error == -EACCES)
     701                 :            :                                 /* kill task in case of brute force attacks */
     702                 :          0 :                                 perms.kill = AA_MAY_CHANGEHAT;
     703         [ #  # ]:          0 :                         else if (name && !error)
     704                 :            :                                 /* reset error for learning of new hats */
     705                 :            :                                 error = -ENOENT;
     706                 :            :                 }
     707         [ #  # ]:          0 :         } else if (previous_profile) {
     708                 :            :                 /* Return to saved profile.  Kill task if restore fails
     709                 :            :                  * to avoid brute force attacks
     710                 :            :                  */
     711                 :          0 :                 target = previous_profile->base.hname;
     712                 :          0 :                 error = aa_restore_previous_profile(token);
     713                 :          0 :                 perms.kill = AA_MAY_CHANGEHAT;
     714                 :            :         } else
     715                 :            :                 /* ignore restores when there is no saved profile */
     716                 :            :                 goto out;
     717                 :            : 
     718                 :            : audit:
     719         [ #  # ]:          0 :         if (!permtest)
     720                 :          0 :                 error = aa_audit_file(profile, &perms, GFP_KERNEL,
     721                 :            :                                       OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL,
     722                 :            :                                       target, GLOBAL_ROOT_UID, info, error);
     723                 :            : 
     724                 :            : out:
     725                 :            :         aa_put_profile(hat);
     726                 :          0 :         kfree(name);
     727                 :            :         put_cred(cred);
     728                 :            : 
     729                 :          0 :         return error;
     730                 :            : }
     731                 :            : 
     732                 :            : /**
     733                 :            :  * aa_change_profile - perform a one-way profile transition
     734                 :            :  * @ns_name: name of the profile namespace to change to (MAYBE NULL)
     735                 :            :  * @hname: name of profile to change to (MAYBE NULL)
     736                 :            :  * @onexec: whether this transition is to take place immediately or at exec
     737                 :            :  * @permtest: true if this is just a permission test
     738                 :            :  *
     739                 :            :  * Change to new profile @name.  Unlike with hats, there is no way
     740                 :            :  * to change back.  If @name isn't specified the current profile name is
     741                 :            :  * used.
     742                 :            :  * If @onexec then the transition is delayed until
     743                 :            :  * the next exec.
     744                 :            :  *
     745                 :            :  * Returns %0 on success, error otherwise.
     746                 :            :  */
     747                 :          0 : int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
     748                 :            :                       bool permtest)
     749                 :            : {
     750                 :          0 :         const struct cred *cred;
     751                 :            :         struct aa_profile *profile, *target = NULL;
     752                 :            :         struct aa_namespace *ns = NULL;
     753                 :          0 :         struct file_perms perms = {};
     754                 :            :         const char *name = NULL, *info = NULL;
     755                 :            :         int op, error = 0;
     756                 :            :         u32 request;
     757                 :            : 
     758         [ #  # ]:          0 :         if (!hname && !ns_name)
     759                 :            :                 return -EINVAL;
     760                 :            : 
     761         [ #  # ]:          0 :         if (onexec) {
     762                 :            :                 request = AA_MAY_ONEXEC;
     763                 :            :                 op = OP_CHANGE_ONEXEC;
     764                 :            :         } else {
     765                 :            :                 request = AA_MAY_CHANGE_PROFILE;
     766                 :            :                 op = OP_CHANGE_PROFILE;
     767                 :            :         }
     768                 :            : 
     769                 :          0 :         cred = get_current_cred();
     770                 :            :         profile = aa_cred_profile(cred);
     771                 :            : 
     772                 :            :         /*
     773                 :            :          * Fail explicitly requested domain transitions if no_new_privs
     774                 :            :          * and not unconfined.
     775                 :            :          * Domain transitions from unconfined are allowed even when
     776                 :            :          * no_new_privs is set because this aways results in a reduction
     777                 :            :          * of permissions.
     778                 :            :          */
     779 [ #  # ][ #  # ]:          0 :         if (current->no_new_privs && !unconfined(profile)) {
     780                 :            :                 put_cred(cred);
     781                 :            :                 return -EPERM;
     782                 :            :         }
     783                 :            : 
     784         [ #  # ]:          0 :         if (ns_name) {
     785                 :            :                 /* released below */
     786                 :          0 :                 ns = aa_find_namespace(profile->ns, ns_name);
     787         [ #  # ]:          0 :                 if (!ns) {
     788                 :            :                         /* we don't create new namespace in complain mode */
     789                 :            :                         name = ns_name;
     790                 :            :                         info = "namespace not found";
     791                 :            :                         error = -ENOENT;
     792                 :            :                         goto audit;
     793                 :            :                 }
     794                 :            :         } else
     795                 :            :                 /* released below */
     796                 :          0 :                 ns = aa_get_namespace(profile->ns);
     797                 :            : 
     798                 :            :         /* if the name was not specified, use the name of the current profile */
     799         [ #  # ]:          0 :         if (!hname) {
     800         [ #  # ]:          0 :                 if (unconfined(profile))
     801                 :          0 :                         hname = ns->unconfined->base.hname;
     802                 :            :                 else
     803                 :          0 :                         hname = profile->base.hname;
     804                 :            :         }
     805                 :            : 
     806                 :          0 :         perms = change_profile_perms(profile, ns, hname, request,
     807                 :            :                                      profile->file.start);
     808         [ #  # ]:          0 :         if (!(perms.allow & request)) {
     809                 :            :                 error = -EACCES;
     810                 :            :                 goto audit;
     811                 :            :         }
     812                 :            : 
     813                 :            :         /* released below */
     814                 :          0 :         target = aa_lookup_profile(ns, hname);
     815         [ #  # ]:          0 :         if (!target) {
     816                 :            :                 info = "profile not found";
     817                 :            :                 error = -ENOENT;
     818 [ #  # ][ #  # ]:          0 :                 if (permtest || !COMPLAIN_MODE(profile))
                 [ #  # ]
     819                 :            :                         goto audit;
     820                 :            :                 /* released below */
     821                 :          0 :                 target = aa_new_null_profile(profile, 0);
     822         [ #  # ]:          0 :                 if (!target) {
     823                 :            :                         info = "failed null profile create";
     824                 :            :                         error = -ENOMEM;
     825                 :            :                         goto audit;
     826                 :            :                 }
     827                 :            :         }
     828                 :            : 
     829                 :            :         /* check if tracing task is allowed to trace target domain */
     830                 :          0 :         error = may_change_ptraced_domain(target);
     831         [ #  # ]:          0 :         if (error) {
     832                 :            :                 info = "ptrace prevents transition";
     833                 :            :                 goto audit;
     834                 :            :         }
     835                 :            : 
     836         [ #  # ]:          0 :         if (permtest)
     837                 :            :                 goto audit;
     838                 :            : 
     839         [ #  # ]:          0 :         if (onexec)
     840                 :          0 :                 error = aa_set_current_onexec(target);
     841                 :            :         else
     842                 :          0 :                 error = aa_replace_current_profile(target);
     843                 :            : 
     844                 :            : audit:
     845         [ #  # ]:          0 :         if (!permtest)
     846                 :          0 :                 error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
     847                 :            :                                       name, hname, GLOBAL_ROOT_UID, info, error);
     848                 :            : 
     849                 :            :         aa_put_namespace(ns);
     850                 :            :         aa_put_profile(target);
     851                 :            :         put_cred(cred);
     852                 :            : 
     853                 :          0 :         return error;
     854                 :            : }

Generated by: LCOV version 1.9