LCOV - code coverage report
Current view: top level - security/apparmor - lsm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 107 289 37.0 %
Date: 2014-02-18 Functions: 32 48 66.7 %
Branches: 62 191 32.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * AppArmor security module
       3                 :            :  *
       4                 :            :  * This file contains AppArmor LSM hooks.
       5                 :            :  *
       6                 :            :  * Copyright (C) 1998-2008 Novell/SUSE
       7                 :            :  * Copyright 2009-2010 Canonical Ltd.
       8                 :            :  *
       9                 :            :  * This program is free software; you can redistribute it and/or
      10                 :            :  * modify it under the terms of the GNU General Public License as
      11                 :            :  * published by the Free Software Foundation, version 2 of the
      12                 :            :  * License.
      13                 :            :  */
      14                 :            : 
      15                 :            : #include <linux/security.h>
      16                 :            : #include <linux/moduleparam.h>
      17                 :            : #include <linux/mm.h>
      18                 :            : #include <linux/mman.h>
      19                 :            : #include <linux/mount.h>
      20                 :            : #include <linux/namei.h>
      21                 :            : #include <linux/ptrace.h>
      22                 :            : #include <linux/ctype.h>
      23                 :            : #include <linux/sysctl.h>
      24                 :            : #include <linux/audit.h>
      25                 :            : #include <linux/user_namespace.h>
      26                 :            : #include <net/sock.h>
      27                 :            : 
      28                 :            : #include "include/apparmor.h"
      29                 :            : #include "include/apparmorfs.h"
      30                 :            : #include "include/audit.h"
      31                 :            : #include "include/capability.h"
      32                 :            : #include "include/context.h"
      33                 :            : #include "include/file.h"
      34                 :            : #include "include/ipc.h"
      35                 :            : #include "include/path.h"
      36                 :            : #include "include/policy.h"
      37                 :            : #include "include/procattr.h"
      38                 :            : 
      39                 :            : /* Flag indicating whether initialization completed */
      40                 :            : int apparmor_initialized __initdata;
      41                 :            : 
      42                 :            : /*
      43                 :            :  * LSM hook functions
      44                 :            :  */
      45                 :            : 
      46                 :            : /*
      47                 :            :  * free the associated aa_task_cxt and put its profiles
      48                 :            :  */
      49                 :          0 : static void apparmor_cred_free(struct cred *cred)
      50                 :            : {
      51                 :    1747072 :         aa_free_task_context(cred_cxt(cred));
      52                 :    1748154 :         cred_cxt(cred) = NULL;
      53                 :    1748154 : }
      54                 :            : 
      55                 :            : /*
      56                 :            :  * allocate the apparmor part of blank credentials
      57                 :            :  */
      58                 :          0 : static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
      59                 :            : {
      60                 :            :         /* freed by apparmor_cred_free */
      61                 :          0 :         struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
      62         [ #  # ]:          0 :         if (!cxt)
      63                 :            :                 return -ENOMEM;
      64                 :            : 
      65                 :          0 :         cred_cxt(cred) = cxt;
      66                 :          0 :         return 0;
      67                 :            : }
      68                 :            : 
      69                 :            : /*
      70                 :            :  * prepare new aa_task_cxt for modification by prepare_cred block
      71                 :            :  */
      72                 :          0 : static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
      73                 :            :                                  gfp_t gfp)
      74                 :            : {
      75                 :            :         /* freed by apparmor_cred_free */
      76                 :    1748482 :         struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
      77            [ + ]:    1748407 :         if (!cxt)
      78                 :            :                 return -ENOMEM;
      79                 :            : 
      80                 :    1748411 :         aa_dup_task_context(cxt, cred_cxt(old));
      81                 :    1748418 :         cred_cxt(new) = cxt;
      82                 :    1748418 :         return 0;
      83                 :            : }
      84                 :            : 
      85                 :            : /*
      86                 :            :  * transfer the apparmor data to a blank set of creds
      87                 :            :  */
      88                 :          0 : static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
      89                 :            : {
      90                 :          0 :         const struct aa_task_cxt *old_cxt = cred_cxt(old);
      91                 :          0 :         struct aa_task_cxt *new_cxt = cred_cxt(new);
      92                 :            : 
      93                 :          0 :         aa_dup_task_context(new_cxt, old_cxt);
      94                 :          0 : }
      95                 :            : 
      96                 :          0 : static int apparmor_ptrace_access_check(struct task_struct *child,
      97                 :            :                                         unsigned int mode)
      98                 :            : {
      99                 :      73654 :         int error = cap_ptrace_access_check(child, mode);
     100         [ +  - ]:      73654 :         if (error)
     101                 :            :                 return error;
     102                 :            : 
     103                 :      73654 :         return aa_ptrace(current, child, mode);
     104                 :            : }
     105                 :            : 
     106                 :          0 : static int apparmor_ptrace_traceme(struct task_struct *parent)
     107                 :            : {
     108                 :        102 :         int error = cap_ptrace_traceme(parent);
     109         [ +  - ]:        102 :         if (error)
     110                 :            :                 return error;
     111                 :            : 
     112                 :        102 :         return aa_ptrace(parent, current, PTRACE_MODE_ATTACH);
     113                 :            : }
     114                 :            : 
     115                 :            : /* Derived from security/commoncap.c:cap_capget */
     116                 :          0 : static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
     117                 :            :                            kernel_cap_t *inheritable, kernel_cap_t *permitted)
     118                 :            : {
     119                 :            :         struct aa_profile *profile;
     120                 :          4 :         const struct cred *cred;
     121                 :            : 
     122                 :            :         rcu_read_lock();
     123                 :          4 :         cred = __task_cred(target);
     124                 :            :         profile = aa_cred_profile(cred);
     125                 :            : 
     126                 :          4 :         *effective = cred->cap_effective;
     127                 :          4 :         *inheritable = cred->cap_inheritable;
     128                 :          4 :         *permitted = cred->cap_permitted;
     129                 :            : 
     130 [ -  + ][ #  # ]:          4 :         if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
                 [ #  # ]
     131                 :          0 :                 *effective = cap_intersect(*effective, profile->caps.allow);
     132                 :          0 :                 *permitted = cap_intersect(*permitted, profile->caps.allow);
     133                 :            :         }
     134                 :            :         rcu_read_unlock();
     135                 :            : 
     136                 :          4 :         return 0;
     137                 :            : }
     138                 :            : 
     139                 :          0 : static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
     140                 :            :                             int cap, int audit)
     141                 :            : {
     142                 :            :         struct aa_profile *profile;
     143                 :            :         /* cap_capable returns 0 on success, else -EPERM */
     144                 :     111675 :         int error = cap_capable(cred, ns, cap, audit);
     145         [ +  + ]:     111673 :         if (!error) {
     146                 :            :                 profile = aa_cred_profile(cred);
     147         [ -  + ]:     108856 :                 if (!unconfined(profile))
     148                 :          0 :                         error = aa_capable(profile, cap, audit);
     149                 :            :         }
     150                 :     111675 :         return error;
     151                 :            : }
     152                 :            : 
     153                 :            : /**
     154                 :            :  * common_perm - basic common permission check wrapper fn for paths
     155                 :            :  * @op: operation being checked
     156                 :            :  * @path: path to check permission of  (NOT NULL)
     157                 :            :  * @mask: requested permissions mask
     158                 :            :  * @cond: conditional info for the permission request  (NOT NULL)
     159                 :            :  *
     160                 :            :  * Returns: %0 else error code if error or permission denied
     161                 :            :  */
     162                 :          0 : static int common_perm(int op, struct path *path, u32 mask,
     163                 :            :                        struct path_cond *cond)
     164                 :            : {
     165                 :            :         struct aa_profile *profile;
     166                 :            :         int error = 0;
     167                 :            : 
     168                 :            :         profile = __aa_current_profile();
     169         [ -  + ]:    7127727 :         if (!unconfined(profile))
     170                 :          0 :                 error = aa_path_perm(op, profile, path, 0, mask, cond);
     171                 :            : 
     172                 :    7127727 :         return error;
     173                 :            : }
     174                 :            : 
     175                 :            : /**
     176                 :            :  * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
     177                 :            :  * @op: operation being checked
     178                 :            :  * @dir: directory of the dentry  (NOT NULL)
     179                 :            :  * @dentry: dentry to check  (NOT NULL)
     180                 :            :  * @mask: requested permissions mask
     181                 :            :  * @cond: conditional info for the permission request  (NOT NULL)
     182                 :            :  *
     183                 :            :  * Returns: %0 else error code if error or permission denied
     184                 :            :  */
     185                 :            : static int common_perm_dir_dentry(int op, struct path *dir,
     186                 :            :                                   struct dentry *dentry, u32 mask,
     187                 :            :                                   struct path_cond *cond)
     188                 :            : {
     189                 :     669020 :         struct path path = { dir->mnt, dentry };
     190                 :            : 
     191                 :     669020 :         return common_perm(op, &path, mask, cond);
     192                 :            : }
     193                 :            : 
     194                 :            : /**
     195                 :            :  * common_perm_mnt_dentry - common permission wrapper when mnt, dentry
     196                 :            :  * @op: operation being checked
     197                 :            :  * @mnt: mount point of dentry (NOT NULL)
     198                 :            :  * @dentry: dentry to check  (NOT NULL)
     199                 :            :  * @mask: requested permissions mask
     200                 :            :  *
     201                 :            :  * Returns: %0 else error code if error or permission denied
     202                 :            :  */
     203                 :          0 : static int common_perm_mnt_dentry(int op, struct vfsmount *mnt,
     204                 :            :                                   struct dentry *dentry, u32 mask)
     205                 :            : {
     206                 :    6415652 :         struct path path = { mnt, dentry };
     207                 :   12831304 :         struct path_cond cond = { dentry->d_inode->i_uid,
     208                 :    6415652 :                                   dentry->d_inode->i_mode
     209                 :            :         };
     210                 :            : 
     211                 :    6415652 :         return common_perm(op, &path, mask, &cond);
     212                 :            : }
     213                 :            : 
     214                 :            : /**
     215                 :            :  * common_perm_rm - common permission wrapper for operations doing rm
     216                 :            :  * @op: operation being checked
     217                 :            :  * @dir: directory that the dentry is in  (NOT NULL)
     218                 :            :  * @dentry: dentry being rm'd  (NOT NULL)
     219                 :            :  * @mask: requested permission mask
     220                 :            :  *
     221                 :            :  * Returns: %0 else error code if error or permission denied
     222                 :            :  */
     223                 :     202687 : static int common_perm_rm(int op, struct path *dir,
     224                 :            :                           struct dentry *dentry, u32 mask)
     225                 :            : {
     226                 :     405374 :         struct inode *inode = dentry->d_inode;
     227                 :     202687 :         struct path_cond cond = { };
     228                 :            : 
     229 [ +  - ][ +  - ]:     202687 :         if (!inode || !dir->mnt || !mediated_filesystem(inode))
                 [ +  - ]
     230                 :            :                 return 0;
     231                 :            : 
     232                 :     202687 :         cond.uid = inode->i_uid;
     233                 :     202687 :         cond.mode = inode->i_mode;
     234                 :            : 
     235                 :            :         return common_perm_dir_dentry(op, dir, dentry, mask, &cond);
     236                 :            : }
     237                 :            : 
     238                 :            : /**
     239                 :            :  * common_perm_create - common permission wrapper for operations doing create
     240                 :            :  * @op: operation being checked
     241                 :            :  * @dir: directory that dentry will be created in  (NOT NULL)
     242                 :            :  * @dentry: dentry to create   (NOT NULL)
     243                 :            :  * @mask: request permission mask
     244                 :            :  * @mode: created file mode
     245                 :            :  *
     246                 :            :  * Returns: %0 else error code if error or permission denied
     247                 :            :  */
     248                 :          0 : static int common_perm_create(int op, struct path *dir, struct dentry *dentry,
     249                 :            :                               u32 mask, umode_t mode)
     250                 :            : {
     251                 :     466334 :         struct path_cond cond = { current_fsuid(), mode };
     252                 :            : 
     253 [ +  - ][ +  + ]:     466334 :         if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode))
     254                 :            :                 return 0;
     255                 :            : 
     256                 :     466332 :         return common_perm_dir_dentry(op, dir, dentry, mask, &cond);
     257                 :            : }
     258                 :            : 
     259                 :          0 : static int apparmor_path_unlink(struct path *dir, struct dentry *dentry)
     260                 :            : {
     261                 :     160717 :         return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE);
     262                 :            : }
     263                 :            : 
     264                 :          0 : static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry,
     265                 :            :                                umode_t mode)
     266                 :            : {
     267                 :      41908 :         return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE,
     268                 :            :                                   S_IFDIR);
     269                 :            : }
     270                 :            : 
     271                 :          0 : static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry)
     272                 :            : {
     273                 :      41971 :         return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE);
     274                 :            : }
     275                 :            : 
     276                 :          0 : static int apparmor_path_mknod(struct path *dir, struct dentry *dentry,
     277                 :            :                                umode_t mode, unsigned int dev)
     278                 :            : {
     279                 :     415170 :         return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode);
     280                 :            : }
     281                 :            : 
     282                 :          0 : static int apparmor_path_truncate(struct path *path)
     283                 :            : {
     284                 :     118481 :         struct path_cond cond = { path->dentry->d_inode->i_uid,
     285                 :      39495 :                                   path->dentry->d_inode->i_mode
     286                 :            :         };
     287                 :            : 
     288    [ +  + ][ + ]:      39495 :         if (!path->mnt || !mediated_filesystem(path->dentry->d_inode))
     289                 :            :                 return 0;
     290                 :            : 
     291                 :      39493 :         return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE,
     292                 :            :                            &cond);
     293                 :            : }
     294                 :            : 
     295                 :          0 : static int apparmor_path_symlink(struct path *dir, struct dentry *dentry,
     296                 :            :                                  const char *old_name)
     297                 :            : {
     298                 :       9256 :         return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE,
     299                 :            :                                   S_IFLNK);
     300                 :            : }
     301                 :            : 
     302                 :          0 : static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir,
     303                 :            :                               struct dentry *new_dentry)
     304                 :            : {
     305                 :            :         struct aa_profile *profile;
     306                 :            :         int error = 0;
     307                 :            : 
     308         [ +  - ]:       8999 :         if (!mediated_filesystem(old_dentry->d_inode))
     309                 :            :                 return 0;
     310                 :            : 
     311                 :            :         profile = aa_current_profile();
     312         [ -  + ]:       8999 :         if (!unconfined(profile))
     313                 :          0 :                 error = aa_path_link(profile, old_dentry, new_dir, new_dentry);
     314                 :       8999 :         return error;
     315                 :            : }
     316                 :            : 
     317                 :          0 : static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
     318                 :            :                                 struct path *new_dir, struct dentry *new_dentry)
     319                 :            : {
     320                 :            :         struct aa_profile *profile;
     321                 :            :         int error = 0;
     322                 :            : 
     323         [ +  - ]:     266398 :         if (!mediated_filesystem(old_dentry->d_inode))
     324                 :            :                 return 0;
     325                 :            : 
     326                 :            :         profile = aa_current_profile();
     327         [ -  + ]:     266398 :         if (!unconfined(profile)) {
     328                 :          0 :                 struct path old_path = { old_dir->mnt, old_dentry };
     329                 :          0 :                 struct path new_path = { new_dir->mnt, new_dentry };
     330                 :          0 :                 struct path_cond cond = { old_dentry->d_inode->i_uid,
     331                 :          0 :                                           old_dentry->d_inode->i_mode
     332                 :            :                 };
     333                 :            : 
     334                 :          0 :                 error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0,
     335                 :            :                                      MAY_READ | AA_MAY_META_READ | MAY_WRITE |
     336                 :            :                                      AA_MAY_META_WRITE | AA_MAY_DELETE,
     337                 :            :                                      &cond);
     338         [ #  # ]:          0 :                 if (!error)
     339                 :          0 :                         error = aa_path_perm(OP_RENAME_DEST, profile, &new_path,
     340                 :            :                                              0, MAY_WRITE | AA_MAY_META_WRITE |
     341                 :            :                                              AA_MAY_CREATE, &cond);
     342                 :            : 
     343                 :            :         }
     344                 :     266398 :         return error;
     345                 :            : }
     346                 :            : 
     347                 :          0 : static int apparmor_path_chmod(struct path *path, umode_t mode)
     348                 :            : {
     349         [ +  - ]:      17444 :         if (!mediated_filesystem(path->dentry->d_inode))
     350                 :            :                 return 0;
     351                 :            : 
     352                 :      17444 :         return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD);
     353                 :            : }
     354                 :            : 
     355                 :          0 : static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
     356                 :            : {
     357                 :       7176 :         struct path_cond cond =  { path->dentry->d_inode->i_uid,
     358                 :       3588 :                                    path->dentry->d_inode->i_mode
     359                 :            :         };
     360                 :            : 
     361         [ +  - ]:       3588 :         if (!mediated_filesystem(path->dentry->d_inode))
     362                 :            :                 return 0;
     363                 :            : 
     364                 :       3588 :         return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond);
     365                 :            : }
     366                 :            : 
     367                 :          0 : static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
     368                 :            : {
     369         [ +  + ]:    6408219 :         if (!mediated_filesystem(dentry->d_inode))
     370                 :            :                 return 0;
     371                 :            : 
     372                 :    6398249 :         return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry,
     373                 :            :                                       AA_MAY_META_READ);
     374                 :            : }
     375                 :            : 
     376                 :          0 : static int apparmor_file_open(struct file *file, const struct cred *cred)
     377                 :            : {
     378                 :    3510575 :         struct aa_file_cxt *fcxt = file->f_security;
     379                 :            :         struct aa_profile *profile;
     380                 :            :         int error = 0;
     381                 :            : 
     382         [ +  + ]:    3510575 :         if (!mediated_filesystem(file_inode(file)))
     383                 :            :                 return 0;
     384                 :            : 
     385                 :            :         /* If in exec, permission is handled by bprm hooks.
     386                 :            :          * Cache permissions granted by the previous exec check, with
     387                 :            :          * implicit read and executable mmap which are required to
     388                 :            :          * actually execute the image.
     389                 :            :          */
     390         [ +  + ]:    3510509 :         if (current->in_execve) {
     391                 :     119140 :                 fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP;
     392                 :     119140 :                 return 0;
     393                 :            :         }
     394                 :            : 
     395                 :            :         profile = aa_cred_profile(cred);
     396         [ -  + ]:    3391397 :         if (!unconfined(profile)) {
     397                 :            :                 struct inode *inode = file_inode(file);
     398                 :          0 :                 struct path_cond cond = { inode->i_uid, inode->i_mode };
     399                 :            : 
     400                 :          0 :                 error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0,
     401                 :            :                                      aa_map_file_to_perms(file), &cond);
     402                 :            :                 /* todo cache full allowed permissions set and state */
     403                 :          0 :                 fcxt->allow = aa_map_file_to_perms(file);
     404                 :            :         }
     405                 :            : 
     406                 :    3391397 :         return error;
     407                 :            : }
     408                 :            : 
     409                 :          0 : static int apparmor_file_alloc_security(struct file *file)
     410                 :            : {
     411                 :            :         /* freed by apparmor_file_free_security */
     412                 :    7741206 :         file->f_security = aa_alloc_file_context(GFP_KERNEL);
     413            [ + ]:    7741206 :         if (!file->f_security)
     414                 :            :                 return -ENOMEM;
     415                 :    7741237 :         return 0;
     416                 :            : 
     417                 :            : }
     418                 :            : 
     419                 :          0 : static void apparmor_file_free_security(struct file *file)
     420                 :            : {
     421                 :    7741133 :         struct aa_file_cxt *cxt = file->f_security;
     422                 :            : 
     423                 :            :         aa_free_file_context(cxt);
     424                 :         88 : }
     425                 :            : 
     426                 :          0 : static int common_file_perm(int op, struct file *file, u32 mask)
     427                 :            : {
     428                 :   26605767 :         struct aa_file_cxt *fcxt = file->f_security;
     429                 :   26605767 :         struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred);
     430                 :            :         int error = 0;
     431                 :            : 
     432         [ -  + ]:   26603439 :         BUG_ON(!fprofile);
     433                 :            : 
     434 [ +  + ][ +  + ]:   26603439 :         if (!file->f_path.mnt ||
     435                 :   26603039 :             !mediated_filesystem(file_inode(file)))
     436                 :            :                 return 0;
     437                 :            : 
     438                 :            :         profile = __aa_current_profile();
     439                 :            : 
     440                 :            :         /* revalidate access, if task is unconfined, or the cached cred
     441                 :            :          * doesn't match or if the request is for more permissions than
     442                 :            :          * was granted.
     443                 :            :          *
     444                 :            :          * Note: the test for !unconfined(fprofile) is to handle file
     445                 :            :          *       delegation from unconfined tasks
     446                 :            :          */
     447 [ -  + ][ #  # ]:   23753074 :         if (!unconfined(profile) && !unconfined(fprofile) &&
                 [ #  # ]
     448         [ #  # ]:          0 :             ((fprofile != profile) || (mask & ~fcxt->allow)))
     449                 :          0 :                 error = aa_file_perm(op, profile, file, mask);
     450                 :            : 
     451                 :   23753578 :         return error;
     452                 :            : }
     453                 :            : 
     454                 :          0 : static int apparmor_file_permission(struct file *file, int mask)
     455                 :            : {
     456                 :   22569351 :         return common_file_perm(OP_FPERM, file, mask);
     457                 :            : }
     458                 :            : 
     459                 :          0 : static int apparmor_file_lock(struct file *file, unsigned int cmd)
     460                 :            : {
     461                 :            :         u32 mask = AA_MAY_LOCK;
     462                 :            : 
     463         [ +  + ]:    2057506 :         if (cmd == F_WRLCK)
     464                 :            :                 mask |= MAY_WRITE;
     465                 :            : 
     466                 :    2057506 :         return common_file_perm(OP_FLOCK, file, mask);
     467                 :            : }
     468                 :            : 
     469                 :          0 : static int common_mmap(int op, struct file *file, unsigned long prot,
     470                 :            :                        unsigned long flags)
     471                 :            : {
     472                 :            :         int mask = 0;
     473                 :            : 
     474    [ +  + ][ + ]:    3484192 :         if (!file || !file->f_security)
     475                 :            :                 return 0;
     476                 :            : 
     477         [ +  + ]:    1978096 :         if (prot & PROT_READ)
     478                 :            :                 mask |= MAY_READ;
     479                 :            :         /*
     480                 :            :          * Private mappings don't require write perms since they don't
     481                 :            :          * write back to the files
     482                 :            :          */
     483 [ +  + ][ +  + ]:    1978096 :         if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE))
     484                 :     114661 :                 mask |= MAY_WRITE;
     485            [ + ]:    1978096 :         if (prot & PROT_EXEC)
     486                 :     358220 :                 mask |= AA_EXEC_MMAP;
     487                 :            : 
     488                 :          0 :         return common_file_perm(op, file, mask);
     489                 :            : }
     490                 :            : 
     491                 :          0 : static int apparmor_mmap_file(struct file *file, unsigned long reqprot,
     492                 :            :                               unsigned long prot, unsigned long flags)
     493                 :            : {
     494                 :    2883175 :         return common_mmap(OP_FMMAP, file, prot, flags);
     495                 :            : }
     496                 :            : 
     497                 :          0 : static int apparmor_file_mprotect(struct vm_area_struct *vma,
     498                 :            :                                   unsigned long reqprot, unsigned long prot)
     499                 :            : {
     500         [ +  + ]:     601099 :         return common_mmap(OP_FMPROT, vma->vm_file, prot,
     501                 :     601099 :                            !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
     502                 :            : }
     503                 :            : 
     504                 :          0 : static int apparmor_getprocattr(struct task_struct *task, char *name,
     505                 :            :                                 char **value)
     506                 :            : {
     507                 :            :         int error = -ENOENT;
     508                 :            :         /* released below */
     509                 :         14 :         const struct cred *cred = get_task_cred(task);
     510                 :         14 :         struct aa_task_cxt *cxt = cred_cxt(cred);
     511                 :            :         struct aa_profile *profile = NULL;
     512                 :            : 
     513         [ +  + ]:         14 :         if (strcmp(name, "current") == 0)
     514                 :          4 :                 profile = aa_get_newest_profile(cxt->profile);
     515 [ +  + ][ -  + ]:         10 :         else if (strcmp(name, "prev") == 0  && cxt->previous)
     516                 :          0 :                 profile = aa_get_newest_profile(cxt->previous);
     517 [ +  + ][ -  + ]:         10 :         else if (strcmp(name, "exec") == 0 && cxt->onexec)
     518                 :          0 :                 profile = aa_get_newest_profile(cxt->onexec);
     519                 :            :         else
     520                 :            :                 error = -EINVAL;
     521                 :            : 
     522         [ +  + ]:         14 :         if (profile)
     523                 :          4 :                 error = aa_getprocattr(profile, value);
     524                 :            : 
     525                 :            :         aa_put_profile(profile);
     526                 :            :         put_cred(cred);
     527                 :            : 
     528                 :         14 :         return error;
     529                 :            : }
     530                 :            : 
     531                 :          0 : static int apparmor_setprocattr(struct task_struct *task, char *name,
     532                 :            :                                 void *value, size_t size)
     533                 :            : {
     534                 :            :         struct common_audit_data sa;
     535                 :          0 :         struct apparmor_audit_data aad = {0,};
     536                 :          0 :         char *command, *args = value;
     537                 :            :         size_t arg_size;
     538                 :            :         int error;
     539                 :            : 
     540         [ #  # ]:          0 :         if (size == 0)
     541                 :            :                 return -EINVAL;
     542                 :            :         /* args points to a PAGE_SIZE buffer, AppArmor requires that
     543                 :            :          * the buffer must be null terminated or have size <= PAGE_SIZE -1
     544                 :            :          * so that AppArmor can null terminate them
     545                 :            :          */
     546         [ #  # ]:          0 :         if (args[size - 1] != '\0') {
     547         [ #  # ]:          0 :                 if (size == PAGE_SIZE)
     548                 :            :                         return -EINVAL;
     549                 :          0 :                 args[size] = '\0';
     550                 :            :         }
     551                 :            : 
     552                 :            :         /* task can only write its own attributes */
     553         [ #  # ]:          0 :         if (current != task)
     554                 :            :                 return -EACCES;
     555                 :            : 
     556                 :          0 :         args = value;
     557                 :          0 :         args = strim(args);
     558                 :          0 :         command = strsep(&args, " ");
     559         [ #  # ]:          0 :         if (!args)
     560                 :            :                 return -EINVAL;
     561                 :          0 :         args = skip_spaces(args);
     562         [ #  # ]:          0 :         if (!*args)
     563                 :            :                 return -EINVAL;
     564                 :            : 
     565                 :          0 :         arg_size = size - (args - (char *) value);
     566         [ #  # ]:          0 :         if (strcmp(name, "current") == 0) {
     567         [ #  # ]:          0 :                 if (strcmp(command, "changehat") == 0) {
     568                 :          0 :                         error = aa_setprocattr_changehat(args, arg_size,
     569                 :            :                                                          !AA_DO_TEST);
     570         [ #  # ]:          0 :                 } else if (strcmp(command, "permhat") == 0) {
     571                 :          0 :                         error = aa_setprocattr_changehat(args, arg_size,
     572                 :            :                                                          AA_DO_TEST);
     573         [ #  # ]:          0 :                 } else if (strcmp(command, "changeprofile") == 0) {
     574                 :          0 :                         error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
     575                 :            :                                                              !AA_DO_TEST);
     576         [ #  # ]:          0 :                 } else if (strcmp(command, "permprofile") == 0) {
     577                 :          0 :                         error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
     578                 :            :                                                              AA_DO_TEST);
     579                 :            :                 } else
     580                 :            :                         goto fail;
     581         [ #  # ]:          0 :         } else if (strcmp(name, "exec") == 0) {
     582         [ #  # ]:          0 :                 if (strcmp(command, "exec") == 0)
     583                 :          0 :                         error = aa_setprocattr_changeprofile(args, AA_ONEXEC,
     584                 :            :                                                              !AA_DO_TEST);
     585                 :            :                 else
     586                 :            :                         goto fail;
     587                 :            :         } else
     588                 :            :                 /* only support the "current" and "exec" process attributes */
     589                 :            :                 return -EINVAL;
     590                 :            : 
     591         [ #  # ]:          0 :         if (!error)
     592                 :          0 :                 error = size;
     593                 :          0 :         return error;
     594                 :            : 
     595                 :            : fail:
     596                 :          0 :         sa.type = LSM_AUDIT_DATA_NONE;
     597                 :          0 :         sa.aad = &aad;
     598                 :          0 :         aad.profile = aa_current_profile();
     599                 :          0 :         aad.op = OP_SETPROCATTR;
     600                 :          0 :         aad.info = name;
     601                 :          0 :         aad.error = -EINVAL;
     602                 :          0 :         aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
     603                 :          0 :         return -EINVAL;
     604                 :            : }
     605                 :            : 
     606                 :          0 : static int apparmor_task_setrlimit(struct task_struct *task,
     607                 :            :                 unsigned int resource, struct rlimit *new_rlim)
     608                 :            : {
     609                 :            :         struct aa_profile *profile = __aa_current_profile();
     610                 :            :         int error = 0;
     611                 :            : 
     612         [ -  + ]:       4541 :         if (!unconfined(profile))
     613                 :          0 :                 error = aa_task_setrlimit(profile, task, resource, new_rlim);
     614                 :            : 
     615                 :       4541 :         return error;
     616                 :            : }
     617                 :            : 
     618                 :            : static struct security_operations apparmor_ops = {
     619                 :            :         .name =                         "apparmor",
     620                 :            : 
     621                 :            :         .ptrace_access_check =          apparmor_ptrace_access_check,
     622                 :            :         .ptrace_traceme =               apparmor_ptrace_traceme,
     623                 :            :         .capget =                       apparmor_capget,
     624                 :            :         .capable =                      apparmor_capable,
     625                 :            : 
     626                 :            :         .path_link =                    apparmor_path_link,
     627                 :            :         .path_unlink =                  apparmor_path_unlink,
     628                 :            :         .path_symlink =                 apparmor_path_symlink,
     629                 :            :         .path_mkdir =                   apparmor_path_mkdir,
     630                 :            :         .path_rmdir =                   apparmor_path_rmdir,
     631                 :            :         .path_mknod =                   apparmor_path_mknod,
     632                 :            :         .path_rename =                  apparmor_path_rename,
     633                 :            :         .path_chmod =                   apparmor_path_chmod,
     634                 :            :         .path_chown =                   apparmor_path_chown,
     635                 :            :         .path_truncate =                apparmor_path_truncate,
     636                 :            :         .inode_getattr =                apparmor_inode_getattr,
     637                 :            : 
     638                 :            :         .file_open =                    apparmor_file_open,
     639                 :            :         .file_permission =              apparmor_file_permission,
     640                 :            :         .file_alloc_security =          apparmor_file_alloc_security,
     641                 :            :         .file_free_security =           apparmor_file_free_security,
     642                 :            :         .mmap_file =                    apparmor_mmap_file,
     643                 :            :         .mmap_addr =                    cap_mmap_addr,
     644                 :            :         .file_mprotect =                apparmor_file_mprotect,
     645                 :            :         .file_lock =                    apparmor_file_lock,
     646                 :            : 
     647                 :            :         .getprocattr =                  apparmor_getprocattr,
     648                 :            :         .setprocattr =                  apparmor_setprocattr,
     649                 :            : 
     650                 :            :         .cred_alloc_blank =             apparmor_cred_alloc_blank,
     651                 :            :         .cred_free =                    apparmor_cred_free,
     652                 :            :         .cred_prepare =                 apparmor_cred_prepare,
     653                 :            :         .cred_transfer =                apparmor_cred_transfer,
     654                 :            : 
     655                 :            :         .bprm_set_creds =               apparmor_bprm_set_creds,
     656                 :            :         .bprm_committing_creds =        apparmor_bprm_committing_creds,
     657                 :            :         .bprm_committed_creds =         apparmor_bprm_committed_creds,
     658                 :            :         .bprm_secureexec =              apparmor_bprm_secureexec,
     659                 :            : 
     660                 :            :         .task_setrlimit =               apparmor_task_setrlimit,
     661                 :            : };
     662                 :            : 
     663                 :            : /*
     664                 :            :  * AppArmor sysfs module parameters
     665                 :            :  */
     666                 :            : 
     667                 :            : static int param_set_aabool(const char *val, const struct kernel_param *kp);
     668                 :            : static int param_get_aabool(char *buffer, const struct kernel_param *kp);
     669                 :            : #define param_check_aabool param_check_bool
     670                 :            : static struct kernel_param_ops param_ops_aabool = {
     671                 :            :         .flags = KERNEL_PARAM_FL_NOARG,
     672                 :            :         .set = param_set_aabool,
     673                 :            :         .get = param_get_aabool
     674                 :            : };
     675                 :            : 
     676                 :            : static int param_set_aauint(const char *val, const struct kernel_param *kp);
     677                 :            : static int param_get_aauint(char *buffer, const struct kernel_param *kp);
     678                 :            : #define param_check_aauint param_check_uint
     679                 :            : static struct kernel_param_ops param_ops_aauint = {
     680                 :            :         .set = param_set_aauint,
     681                 :            :         .get = param_get_aauint
     682                 :            : };
     683                 :            : 
     684                 :            : static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp);
     685                 :            : static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp);
     686                 :            : #define param_check_aalockpolicy param_check_bool
     687                 :            : static struct kernel_param_ops param_ops_aalockpolicy = {
     688                 :            :         .flags = KERNEL_PARAM_FL_NOARG,
     689                 :            :         .set = param_set_aalockpolicy,
     690                 :            :         .get = param_get_aalockpolicy
     691                 :            : };
     692                 :            : 
     693                 :            : static int param_set_audit(const char *val, struct kernel_param *kp);
     694                 :            : static int param_get_audit(char *buffer, struct kernel_param *kp);
     695                 :            : 
     696                 :            : static int param_set_mode(const char *val, struct kernel_param *kp);
     697                 :            : static int param_get_mode(char *buffer, struct kernel_param *kp);
     698                 :            : 
     699                 :            : /* Flag values, also controllable via /sys/module/apparmor/parameters
     700                 :            :  * We define special types as we want to do additional mediation.
     701                 :            :  */
     702                 :            : 
     703                 :            : /* AppArmor global enforcement switch - complain, enforce, kill */
     704                 :            : enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
     705                 :            : module_param_call(mode, param_set_mode, param_get_mode,
     706                 :            :                   &aa_g_profile_mode, S_IRUSR | S_IWUSR);
     707                 :            : 
     708                 :            : /* Debug mode */
     709                 :            : bool aa_g_debug;
     710                 :            : module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
     711                 :            : 
     712                 :            : /* Audit mode */
     713                 :            : enum audit_mode aa_g_audit;
     714                 :            : module_param_call(audit, param_set_audit, param_get_audit,
     715                 :            :                   &aa_g_audit, S_IRUSR | S_IWUSR);
     716                 :            : 
     717                 :            : /* Determines if audit header is included in audited messages.  This
     718                 :            :  * provides more context if the audit daemon is not running
     719                 :            :  */
     720                 :            : bool aa_g_audit_header = 1;
     721                 :            : module_param_named(audit_header, aa_g_audit_header, aabool,
     722                 :            :                    S_IRUSR | S_IWUSR);
     723                 :            : 
     724                 :            : /* lock out loading/removal of policy
     725                 :            :  * TODO: add in at boot loading of policy, which is the only way to
     726                 :            :  *       load policy, if lock_policy is set
     727                 :            :  */
     728                 :            : bool aa_g_lock_policy;
     729                 :            : module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy,
     730                 :            :                    S_IRUSR | S_IWUSR);
     731                 :            : 
     732                 :            : /* Syscall logging mode */
     733                 :            : bool aa_g_logsyscall;
     734                 :            : module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR);
     735                 :            : 
     736                 :            : /* Maximum pathname length before accesses will start getting rejected */
     737                 :            : unsigned int aa_g_path_max = 2 * PATH_MAX;
     738                 :            : module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR);
     739                 :            : 
     740                 :            : /* Determines how paranoid loading of policy is and how much verification
     741                 :            :  * on the loaded policy is done.
     742                 :            :  */
     743                 :            : bool aa_g_paranoid_load = 1;
     744                 :            : module_param_named(paranoid_load, aa_g_paranoid_load, aabool,
     745                 :            :                    S_IRUSR | S_IWUSR);
     746                 :            : 
     747                 :            : /* Boot time disable flag */
     748                 :            : static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
     749                 :            : module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);
     750                 :            : 
     751                 :          0 : static int __init apparmor_enabled_setup(char *str)
     752                 :            : {
     753                 :            :         unsigned long enabled;
     754                 :            :         int error = strict_strtoul(str, 0, &enabled);
     755         [ #  # ]:          0 :         if (!error)
     756                 :          0 :                 apparmor_enabled = enabled ? 1 : 0;
     757                 :          0 :         return 1;
     758                 :            : }
     759                 :            : 
     760                 :            : __setup("apparmor=", apparmor_enabled_setup);
     761                 :            : 
     762                 :            : /* set global flag turning off the ability to load policy */
     763                 :          0 : static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
     764                 :            : {
     765         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     766                 :            :                 return -EPERM;
     767         [ #  # ]:          0 :         if (aa_g_lock_policy)
     768                 :            :                 return -EACCES;
     769                 :          0 :         return param_set_bool(val, kp);
     770                 :            : }
     771                 :            : 
     772                 :          0 : static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
     773                 :            : {
     774         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     775                 :            :                 return -EPERM;
     776                 :          0 :         return param_get_bool(buffer, kp);
     777                 :            : }
     778                 :            : 
     779                 :          0 : static int param_set_aabool(const char *val, const struct kernel_param *kp)
     780                 :            : {
     781         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     782                 :            :                 return -EPERM;
     783                 :          0 :         return param_set_bool(val, kp);
     784                 :            : }
     785                 :            : 
     786                 :          0 : static int param_get_aabool(char *buffer, const struct kernel_param *kp)
     787                 :            : {
     788         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     789                 :            :                 return -EPERM;
     790                 :          0 :         return param_get_bool(buffer, kp);
     791                 :            : }
     792                 :            : 
     793                 :          0 : static int param_set_aauint(const char *val, const struct kernel_param *kp)
     794                 :            : {
     795         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     796                 :            :                 return -EPERM;
     797                 :          0 :         return param_set_uint(val, kp);
     798                 :            : }
     799                 :            : 
     800                 :          0 : static int param_get_aauint(char *buffer, const struct kernel_param *kp)
     801                 :            : {
     802         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     803                 :            :                 return -EPERM;
     804                 :          0 :         return param_get_uint(buffer, kp);
     805                 :            : }
     806                 :            : 
     807                 :          0 : static int param_get_audit(char *buffer, struct kernel_param *kp)
     808                 :            : {
     809         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     810                 :            :                 return -EPERM;
     811                 :            : 
     812         [ #  # ]:          0 :         if (!apparmor_enabled)
     813                 :            :                 return -EINVAL;
     814                 :            : 
     815                 :          0 :         return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]);
     816                 :            : }
     817                 :            : 
     818                 :          0 : static int param_set_audit(const char *val, struct kernel_param *kp)
     819                 :            : {
     820                 :            :         int i;
     821         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     822                 :            :                 return -EPERM;
     823                 :            : 
     824         [ #  # ]:          0 :         if (!apparmor_enabled)
     825                 :            :                 return -EINVAL;
     826                 :            : 
     827         [ #  # ]:          0 :         if (!val)
     828                 :            :                 return -EINVAL;
     829                 :            : 
     830         [ #  # ]:          0 :         for (i = 0; i < AUDIT_MAX_INDEX; i++) {
     831         [ #  # ]:          0 :                 if (strcmp(val, audit_mode_names[i]) == 0) {
     832                 :          0 :                         aa_g_audit = i;
     833                 :          0 :                         return 0;
     834                 :            :                 }
     835                 :            :         }
     836                 :            : 
     837                 :            :         return -EINVAL;
     838                 :            : }
     839                 :            : 
     840                 :          0 : static int param_get_mode(char *buffer, struct kernel_param *kp)
     841                 :            : {
     842         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     843                 :            :                 return -EPERM;
     844                 :            : 
     845         [ #  # ]:          0 :         if (!apparmor_enabled)
     846                 :            :                 return -EINVAL;
     847                 :            : 
     848                 :          0 :         return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]);
     849                 :            : }
     850                 :            : 
     851                 :          0 : static int param_set_mode(const char *val, struct kernel_param *kp)
     852                 :            : {
     853                 :            :         int i;
     854         [ #  # ]:          0 :         if (!capable(CAP_MAC_ADMIN))
     855                 :            :                 return -EPERM;
     856                 :            : 
     857         [ #  # ]:          0 :         if (!apparmor_enabled)
     858                 :            :                 return -EINVAL;
     859                 :            : 
     860         [ #  # ]:          0 :         if (!val)
     861                 :            :                 return -EINVAL;
     862                 :            : 
     863         [ #  # ]:          0 :         for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) {
     864         [ #  # ]:          0 :                 if (strcmp(val, aa_profile_mode_names[i]) == 0) {
     865                 :          0 :                         aa_g_profile_mode = i;
     866                 :          0 :                         return 0;
     867                 :            :                 }
     868                 :            :         }
     869                 :            : 
     870                 :            :         return -EINVAL;
     871                 :            : }
     872                 :            : 
     873                 :            : /*
     874                 :            :  * AppArmor init functions
     875                 :            :  */
     876                 :            : 
     877                 :            : /**
     878                 :            :  * set_init_cxt - set a task context and profile on the first task.
     879                 :            :  *
     880                 :            :  * TODO: allow setting an alternate profile than unconfined
     881                 :            :  */
     882                 :          0 : static int __init set_init_cxt(void)
     883                 :            : {
     884                 :          0 :         struct cred *cred = (struct cred *)current->real_cred;
     885                 :            :         struct aa_task_cxt *cxt;
     886                 :            : 
     887                 :          0 :         cxt = aa_alloc_task_context(GFP_KERNEL);
     888         [ #  # ]:          0 :         if (!cxt)
     889                 :            :                 return -ENOMEM;
     890                 :            : 
     891                 :          0 :         cxt->profile = aa_get_profile(root_ns->unconfined);
     892                 :          0 :         cred_cxt(cred) = cxt;
     893                 :            : 
     894                 :          0 :         return 0;
     895                 :            : }
     896                 :            : 
     897                 :          0 : static int __init apparmor_init(void)
     898                 :            : {
     899                 :            :         int error;
     900                 :            : 
     901 [ #  # ][ #  # ]:          0 :         if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) {
     902                 :          0 :                 aa_info_message("AppArmor disabled by boot time parameter");
     903                 :          0 :                 apparmor_enabled = 0;
     904                 :          0 :                 return 0;
     905                 :            :         }
     906                 :            : 
     907                 :          0 :         error = aa_alloc_root_ns();
     908         [ #  # ]:          0 :         if (error) {
     909         [ #  # ]:          0 :                 AA_ERROR("Unable to allocate default profile namespace\n");
     910                 :            :                 goto alloc_out;
     911                 :            :         }
     912                 :            : 
     913                 :          0 :         error = set_init_cxt();
     914         [ #  # ]:          0 :         if (error) {
     915         [ #  # ]:          0 :                 AA_ERROR("Failed to set context on init task\n");
     916                 :            :                 goto register_security_out;
     917                 :            :         }
     918                 :            : 
     919                 :          0 :         error = register_security(&apparmor_ops);
     920         [ #  # ]:          0 :         if (error) {
     921                 :          0 :                 struct cred *cred = (struct cred *)current->real_cred;
     922                 :          0 :                 aa_free_task_context(cred_cxt(cred));
     923                 :          0 :                 cred_cxt(cred) = NULL;
     924         [ #  # ]:          0 :                 AA_ERROR("Unable to register AppArmor\n");
     925                 :            :                 goto register_security_out;
     926                 :            :         }
     927                 :            : 
     928                 :            :         /* Report that AppArmor successfully initialized */
     929                 :          0 :         apparmor_initialized = 1;
     930         [ #  # ]:          0 :         if (aa_g_profile_mode == APPARMOR_COMPLAIN)
     931                 :          0 :                 aa_info_message("AppArmor initialized: complain mode enabled");
     932         [ #  # ]:          0 :         else if (aa_g_profile_mode == APPARMOR_KILL)
     933                 :          0 :                 aa_info_message("AppArmor initialized: kill mode enabled");
     934                 :            :         else
     935                 :          0 :                 aa_info_message("AppArmor initialized");
     936                 :            : 
     937                 :          0 :         return error;
     938                 :            : 
     939                 :            : register_security_out:
     940                 :          0 :         aa_free_root_ns();
     941                 :            : 
     942                 :            : alloc_out:
     943                 :          0 :         aa_destroy_aafs();
     944                 :            : 
     945                 :          0 :         apparmor_enabled = 0;
     946                 :          0 :         return error;
     947                 :            : }
     948                 :            : 
     949                 :            : security_initcall(apparmor_init);

Generated by: LCOV version 1.9