Print this page
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs (cleanup)
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs (cleanup)
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs (part 2)
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs (part 2)
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-10069 ZFS_READONLY is a little too strict
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
OS-158 zfs_zaccess_delete() comments do not accurately reflect delete permissions for ACLs
OS-40 zfs issues with inheritance flags during chmod(2) with aclmode=passthrough
OS-139 POSIX write should imply DELETE_CHILD on directories - and some additional considerations (fix lint)
OS-123 aclinherit=restricted masks inherited permissions by group perms (groupmask)
OS-139 POSIX write should imply DELETE_CHILD on directories - and some additional considerations
Fixup merge results
re #12585 rb4049 ZFS++ work port - refactoring to improve separation of open/closed code, bug fixes, performance improvements - open code
re #6815 rb1758 need WORM in nza-kernel (4.0)

@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/time.h>

@@ -52,10 +52,12 @@
 #include <sys/dnode.h>
 #include <sys/zap.h>
 #include <sys/sa.h>
 #include "fs/fs_subr.h"
 #include <acl/acl_common.h>
+#include <c2/audit.h>
+#include <c2/audit_kernel.h>
 
 #define ALLOW   ACE_ACCESS_ALLOWED_ACE_TYPE
 #define DENY    ACE_ACCESS_DENIED_ACE_TYPE
 #define MAX_ACE_TYPE    ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
 #define MIN_ACE_TYPE    ALLOW

@@ -1186,11 +1188,10 @@
         zfs_acl_locator_cb_t    locate = { 0 };
         uint64_t                mode;
         sa_bulk_attr_t          bulk[5];
         uint64_t                ctime[2];
         int                     count = 0;
-        zfs_acl_phys_t          acl_phys;
 
         mode = zp->z_mode;
 
         mode = zfs_mode_compute(mode, aclp, &zp->z_pflags,
             zp->z_uid, zp->z_gid);

@@ -1233,10 +1234,11 @@
                 SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_COUNT(zfsvfs),
                     NULL, &aclp->z_acl_count, sizeof (uint64_t));
         } else { /* Painful legacy way */
                 zfs_acl_node_t *aclnode;
                 uint64_t off = 0;
+                zfs_acl_phys_t acl_phys;
                 uint64_t aoid;
 
                 if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs),
                     &acl_phys, sizeof (acl_phys))) != 0)
                         return (error);

@@ -2081,28 +2083,26 @@
  * working_mode is not a denied access mask upon exit if the function
  * is used in this manner.
  */
 static int
 zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
-    boolean_t anyaccess, cred_t *cr)
+    boolean_t anyaccess, cred_t *cr, boolean_t audit)
 {
         zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
         zfs_acl_t       *aclp;
         int             error;
-        uid_t           uid = crgetuid(cr);
-        uint64_t        who;
+        uint64_t        who;            /* FUID from the ACE */
         uint16_t        type, iflags;
         uint16_t        entry_type;
         uint32_t        access_mask;
         uint32_t        deny_mask = 0;
+        uint32_t        sys_smask = 0;
+        uint32_t        sys_fmask = 0;
         zfs_ace_hdr_t   *acep = NULL;
-        boolean_t       checkit;
-        uid_t           gowner;
-        uid_t           fowner;
+        boolean_t       checkit;        /* ACE ID matches */
+        t_audit_data_t *tad;
 
-        zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
-
         mutex_enter(&zp->z_acl_lock);
 
         error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_FALSE);
         if (error != 0) {
                 mutex_exit(&zp->z_acl_lock);

@@ -2121,76 +2121,98 @@
                 if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
                         continue;
 
                 /* Skip ACE if it does not affect any AoI */
                 mask_matched = (access_mask & *working_mode);
-                if (!mask_matched)
+                if ((type == DENY || type == ALLOW) && !mask_matched)
                         continue;
+                if (!audit && type != DENY && type != ALLOW)
+                        continue;
 
                 entry_type = (iflags & ACE_TYPE_FLAGS);
 
                 checkit = B_FALSE;
 
                 switch (entry_type) {
                 case ACE_OWNER:
-                        if (uid == fowner)
-                                checkit = B_TRUE;
+                        who = zp->z_uid;
+                        /*FALLTHROUGH*/
+                case 0: /* USER Entry */
+                        checkit = zfs_user_in_cred(zfsvfs, who, cr);
                         break;
                 case OWNING_GROUP:
-                        who = gowner;
+                        who = zp->z_gid;
                         /*FALLTHROUGH*/
                 case ACE_IDENTIFIER_GROUP:
                         checkit = zfs_groupmember(zfsvfs, who, cr);
                         break;
                 case ACE_EVERYONE:
                         checkit = B_TRUE;
                         break;
 
-                /* USER Entry */
                 default:
-                        if (entry_type == 0) {
-                                uid_t newid;
-
-                                newid = zfs_fuid_map_id(zfsvfs, who, cr,
-                                    ZFS_ACE_USER);
-                                if (newid != IDMAP_WK_CREATOR_OWNER_UID &&
-                                    uid == newid)
-                                        checkit = B_TRUE;
-                                break;
-                        } else {
+                        /*
+                         * The zfs_acl_valid_ace_type check above
+                         * should make this case impossible.
+                         */
                                 mutex_exit(&zp->z_acl_lock);
                                 return (SET_ERROR(EIO));
                         }
-                }
 
                 if (checkit) {
-                        if (type == DENY) {
+                        switch (type) {
+                        case DENY:
                                 DTRACE_PROBE3(zfs__ace__denies,
                                     znode_t *, zp,
                                     zfs_ace_hdr_t *, acep,
                                     uint32_t, mask_matched);
                                 deny_mask |= mask_matched;
-                        } else {
+                                *working_mode &= ~mask_matched;
+                                break;
+                        case ACE_SYSTEM_AUDIT_ACE_TYPE:
+                        case ACE_SYSTEM_ALARM_ACE_TYPE:
+                                DTRACE_PROBE3(zfs__ace__audit,
+                                    znode_t *, zp,
+                                    zfs_ace_hdr_t *, acep,
+                                    uint32_t, access_mask);
+                                if ((iflags &
+                                    ACE_SUCCESSFUL_ACCESS_ACE_FLAG) != 0)
+                                        sys_smask |= access_mask;
+                                if ((iflags & ACE_FAILED_ACCESS_ACE_FLAG) != 0)
+                                        sys_fmask |= access_mask;
+                                break;
+                        case ALLOW:
+                        default:
                                 DTRACE_PROBE3(zfs__ace__allows,
                                     znode_t *, zp,
                                     zfs_ace_hdr_t *, acep,
                                     uint32_t, mask_matched);
                                 if (anyaccess) {
                                         mutex_exit(&zp->z_acl_lock);
                                         return (0);
                                 }
-                        }
                         *working_mode &= ~mask_matched;
+                                break;
                 }
+                }
 
-                /* Are we done? */
-                if (*working_mode == 0)
+                /*
+                 * Are we done? If auditing, process the entire list
+                 * to gather all audit ACEs
+                 */
+                if (!audit && *working_mode == 0)
                         break;
         }
 
         mutex_exit(&zp->z_acl_lock);
 
+        if (audit) {
+                tad = T2A(curthread);
+                tad->tad_sacl_mask.tas_smask = sys_smask;
+                tad->tad_sacl_mask.tas_fmask = sys_fmask;
+        }
+
         /* Put the found 'denies' back on the working mode */
         if (deny_mask) {
                 *working_mode |= deny_mask;
                 return (SET_ERROR(EACCES));
         } else if (*working_mode) {

@@ -2207,11 +2229,11 @@
 boolean_t
 zfs_has_access(znode_t *zp, cred_t *cr)
 {
         uint32_t have = ACE_ALL_PERMS;
 
-        if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
+        if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr, B_FALSE) != 0) {
                 uid_t owner;
 
                 owner = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER);
                 return (secpolicy_vnode_any_access(cr, ZTOV(zp), owner) == 0);
         }

@@ -2222,10 +2244,11 @@
 zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
     boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
 {
         zfsvfs_t *zfsvfs = zp->z_zfsvfs;
         int err;
+        boolean_t audit = B_FALSE;
 
         *working_mode = v4_mode;
         *check_privs = B_TRUE;
 
         /*

@@ -2267,11 +2290,20 @@
             (ZTOV(zp)->v_type != VDIR) &&
             (zp->z_pflags & ZFS_READONLY)) {
                 return (SET_ERROR(EPERM));
         }
 
-        return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
+        if (cr != zone_kcred() && AU_ZONE_AUDITING(NULL)) {
+                t_audit_data_t *tad = T2A(curthread);
+                if (tad->tad_sacl_ctrl != SACL_AUDIT_NONE &&
+                    auditev(AUE_SACL, cr) != 0) {
+                        audit = B_TRUE;
+                        tad->tad_sacl_ctrl = SACL_AUDIT_NONE;
+                }
+        }
+
+        return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr, audit));
 }
 
 static int
 zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
     cred_t *cr)

@@ -2440,10 +2472,14 @@
                         VN_RELE(ZTOV(xzp));
                 return (error);
         }
 
         if (error && (flags & V_APPEND)) {
+                /*
+                 * If zfs_zaccess_common checked aces, then we won't audit here.
+                 * Otherwise, we'll try and get audit masks here.
+                 */
                 error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
         }
 
         if (error && check_privs) {
                 mode_t          checkmode = 0;

@@ -2606,10 +2642,11 @@
         uint32_t dzp_working_mode = 0;
         uint32_t zp_working_mode = 0;
         int dzp_error, zp_error;
         boolean_t dzpcheck_privs;
         boolean_t zpcheck_privs;
+        t_audit_data_t *tad;
 
         if (zp->z_pflags & (ZFS_IMMUTABLE | ZFS_NOUNLINK))
                 return (SET_ERROR(EPERM));
 
         /*

@@ -2646,10 +2683,15 @@
          * Case 2b is handled with wanted_dirperms.
          */
         wanted_dirperms = ACE_DELETE_CHILD;
         if (zfs_write_implies_delete_child)
                 wanted_dirperms |= ACE_WRITE_DATA;
+        /* never audit the parent directory access check */
+        if (AU_ZONE_AUDITING(NULL)) {
+                tad = T2A(curthread);
+                tad->tad_sacl_ctrl = SACL_AUDIT_NONE;
+        }
         dzp_error = zfs_zaccess_common(dzp, wanted_dirperms,
             &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
         if (dzp_error == EACCES) {
                 /* We hit a DENY ACE. */
                 if (!dzpcheck_privs)

@@ -2732,17 +2774,27 @@
 zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
     znode_t *tzp, cred_t *cr)
 {
         int add_perm;
         int error;
+        t_audit_data_t *tad;
+        sacl_audit_ctrl_t do_audit;
 
         if (szp->z_pflags & ZFS_AV_QUARANTINED)
                 return (SET_ERROR(EACCES));
 
         add_perm = (ZTOV(szp)->v_type == VDIR) ?
             ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE;
 
+        if (AU_ZONE_AUDITING(NULL)) {
+                tad = T2A(curthread);
+                do_audit = tad->tad_sacl_ctrl;
+        } else {
+                tad = NULL;
+                do_audit = SACL_AUDIT_NONE;
+        }
+
         /*
          * Rename permissions are combination of delete permission +
          * add file/subdir permission.
          */
 

@@ -2750,23 +2802,45 @@
          * first make sure we do the delete portion.
          *
          * If that succeeds then check for add_file/add_subdir permissions
          */
 
-        if (error = zfs_zaccess_delete(sdzp, szp, cr))
+        if (do_audit == SACL_AUDIT_NO_SRC)
+                tad->tad_sacl_ctrl = SACL_AUDIT_NONE;
+        error = zfs_zaccess_delete(sdzp, szp, cr);
+
+        if (do_audit == SACL_AUDIT_ALL) {
+                tad->tad_sacl_mask_src = tad->tad_sacl_mask;
+                tad->tad_sacl_mask.tas_smask = 0;
+                tad->tad_sacl_mask.tas_fmask = 0;
+        }
+        if (error != 0)
                 return (error);
 
+        if (do_audit != SACL_AUDIT_NONE)
+                tad->tad_sacl_ctrl = do_audit;
+
         /*
          * If we have a tzp, see if we can delete it?
          */
         if (tzp) {
-                if (error = zfs_zaccess_delete(tdzp, tzp, cr))
+                error = zfs_zaccess_delete(tdzp, tzp, cr);
+                if (do_audit != SACL_AUDIT_NONE) {
+                        tad->tad_sacl_mask_dest = tad->tad_sacl_mask;
+                        tad->tad_sacl_mask.tas_smask = 0;
+                        tad->tad_sacl_mask.tas_fmask = 0;
+                }
+                if (error != 0)
                         return (error);
+                if (do_audit != SACL_AUDIT_NONE)
+                        tad->tad_sacl_ctrl = do_audit;
         }
 
         /*
          * Now check for add permissions
          */
         error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
 
+        /* do_audit: leave directory audit info in sacl_mask. */
+
         return (error);
 }