24 February 2011

File Integrity Checks via Package DB

After a system has been installed, files tend to change over time.
These changes may be deliberate, part of normal host operation, the
result of an errant 'chmod', etc.  Tools like Samhain, AIDE, and Tripwire
(note 0) have been devised specifically to identify such changes which
they do well.  The only problem is that these tools need to be installed
and configured.  Though by no means a full substitute for any of the tools
above, in both Solaris and Linux, we can refer to their respective package
databases for simple file integrity checks and attribute restoration.
Our host details are:
        HOSTS:          sunspot, cobblepot
        PROMPT:         host [0]
        OSes:           Solaris 10, CentOS 5.5
        PACKAGE:        op (privately compiled and packaged installation of
                        op-1.32)
        NOTES:          The details that follow should be relevant on
                        previous OS versions as the respective package
                        systems are relatively mature and stable.
Before continuing, it should be noted that the following is only relevant
to files installed by a package registered by the packaging system in use.
The following will be of no use for files otherwise created.  Also,
Solaris is used for the main discussion, followed by a sampling in Linux.

Solaris:

Diving right in, let's have a look at the what the package (pkg) system
has to say about '/usr/local/man/man1/op.1' (see note 1):
        sunspot [0] /usr/sbin/pkgchk -l -p /usr/local/man/man1/op.1
        Pathname: /usr/local/man/man1/op.1
        Type: regular file
        Expected mode: 0644
        Expected owner: root
        Expected group: other
        Expected file size (bytes): 10339
        Expected sum(1) of contents: 51920
        Expected last modification: Jan 08 10:56:03 AM 2008
        Referenced by the following packages:
                SOCop
        Current status: installed
The expected mode for this file is "0644" as detailed above.  Next,
we'll change it to "0755" and have 'pkgchk' audit the file's attributes
(for 'pkgchk' flags used, see note 2):
        sunspot [0] /bin/chmod 0755 /usr/local/man/man1/op.1
        sunspot [0] /usr/sbin/pkgchk -ap /usr/local/man/man1/op.1
        ERROR: /usr/local/man/man1/op.1
            permissions <0644> expected <0755> actual
After updating the mode (permissions) of 'op.1', our subsequent 'pkgchk'
successfully identified the disparity and alerts us to it.  Illustrated
below, we have 'pkgchk' audit the full package (SOCop) to which 'op.1'
belongs and verify with 'ls':
        sunspot [1] /usr/sbin/pkgchk -a SOCop
        ERROR: /usr/local/man/man1/op.1
            permissions <0644> expected <0755> actual
        sunspot [1] /bin/ls -ld /usr/local/man/man1/op.1
        -rwxr-xr-x   1 root     other      10339 Jan  8  2008 /usr/local/man/man1/op.1
To restore the appropriate mode of 'op.1' we could reference the expected
mode from the output above and supply it to 'chmod'.  Even easier would
be to allow 'pkgchk' to fix it for us:
        sunspot [0] /usr/sbin/pkgchk -fp /usr/local/man/man1/op.1
        sunspot [0] /bin/ls -ld /usr/local/man/man1/op.1
        -rw-r--r--   1 root     other      10339 Jan  8  2008 /usr/local/man/man1/op.1
After running 'pkgchk', we verify with 'ls' and see that the mode has
been restored.  Just as we can audit a full package, we can also restore
file attributes to full packages.  The following example picks up after
setting the permissions of 'op.1' to "0755".
        sunspot [0] /usr/sbin/pkgchk -f SOCop
        ERROR: /usr/local/etc/op.conf
            file size <512> expected <640> actual
            file cksum <42802> expected <53200> actual
        sunspot [1] /bin/ls -ld /usr/local/man/man1/op.1
        -rw-r--r--   1 root     other      10339 Jan  8  2008 /usr/local/man/man1/op.1
Of note, we get an error on the file size and checksum for 'op.conf',
another file in the 'SOCop' pkg.  This is because the contents of
'op.conf' have been updated.  While 'pkgchk -f' has alerted us to this,
the '-f' flag only corrects a file's attributes not including size or
checksum (see note 3).  As a result 'op.conf' was left alone.  For a
final example, the following updates user:group ownership and the mode of
'op.1', then runs through an audit and repair of those attributes:
        sunspot [0] /bin/chown bin:bin /usr/local/man/man1/op.1
        sunspot [0] /bin/chmod 0755 /usr/local/man/man1/op.1
        sunspot [0] /usr/sbin/pkgchk -a SOCop
        ERROR: /usr/local/man/man1/op.1
            permissions <0644> expected <0755> actual
            group name <other> expected <bin> actual
            owner name <root> expected <bin> actual
        sunspot [0] /bin/ls -ld /usr/local/man/man1/op.1
        -rwxr-xr-x   1 bin      bin        10339 Jan  8  2008 /usr/local/man/man1/op.1
        sunspot [0] /usr/sbin/pkgchk -fp /usr/local/man/man1/op.1
        sunspot [0] /bin/ls -ld /usr/local/man/man1/op.1
        -rw-r--r--   1 root     other      10339 Jan  8  2008 /usr/local/man/man1/op.1
Linux:

Identifying 'op-1.32/README' pkg and attributes (for 'rpm' dump format,
see note 4):
        cobblepot [0] /bin/rpm -qf /usr/local/share/doc/op-1.32/README
        op-1.32-1
        cobblepot [0] /bin/rpm -q --dump op-1.32 | /bin/grep 'op-1.32/README'
        /usr/local/share/doc/op-1.32/README 4551 1208465130 c8f9993fe17411a5ff1f1381d27a9c7d 0100644 root root 0 0 0 X
Verifying the mode (0100644) of 'README' with 'ls':
        cobblepot [0] /bin/ls -ld /usr/local/share/doc/op-1.32/README
        -rw-r--r-- 1 root root 4551 Apr 17  2008 /usr/local/share/doc/op-1.32/README
Modifying 'README' mode via 'chmod'; verifying the change with 'ls'
and disparity with 'rpm -V' (for 'rpm' options used, see note 5):
        cobblepot [0] /bin/chmod 0444 /usr/local/share/doc/op-1.32/README
        cobblepot [0] /bin/ls -ld /usr/local/share/doc/op-1.32/README
        -r--r--r-- 1 root root 4551 Apr 17  2008 /usr/local/share/doc/op-1.32/README
        cobblepot [0] /bin/rpm -Vf /usr/local/share/doc/op-1.32/README
        S.5....T    /usr/local/etc/op.conf
        .M......    /usr/local/share/doc/op-1.32/README
Note, 'rpm' verify and set commands operate on all files in a package, not
just a specified file.  This is the reason the output above details both
'op.conf' and 'README'.  The output for 'op.conf' indicates a difference
between the rpm database and the file for size, MD5 checksum, and mtime.
For 'README', the difference is only mode (see note 6).  The following
updates the mode (permissions) to all files in pkg 'op-1.32-1' and
verifies the update with 'rpm' and 'ls':
        cobblepot [1] /bin/rpm --setperms op-1.32-1
        cobblepot [0] /bin/rpm -Vf /usr/local/share/doc/op-1.32/README
        S.5....T    /usr/local/etc/op.conf
        cobblepot [0] /bin/ls -ld /usr/local/share/doc/op-1.32/README
        -rw-r--r-- 1 root root 4551 Apr 17  2008 /usr/local/share/doc/op-1.32/README
    Alternatively, one can update a pkg's files by identifying a file
    from the intended pkg:
        cobblepot [0] /bin/rpm --setperms -f /usr/local/share/doc/op-1.32/README
Another example, modifying mode and ownership of 'README':
        cobblepot [0] /bin/chmod 0444 /usr/local/share/doc/op-1.32/README
        cobblepot [0] /bin/chown bin:bin /usr/local/share/doc/op-1.32/README
        cobblepot [0] /bin/ls -ld /usr/local/share/doc/op-1.32/README
        -r--r--r-- 1 bin bin 4551 Apr 17  2008 /usr/local/share/doc/op-1.32/README
Verifying the difference via selected file or pkg:
        cobblepot [0] /bin/rpm -Vf /usr/local/share/doc/op-1.32/README
        S.5....T    /usr/local/etc/op.conf
        .M...UG.    /usr/local/share/doc/op-1.32/README

        cobblepot [1] /bin/rpm -V op-1.32-1
        S.5....T    /usr/local/etc/op.conf
        .M...UG.    /usr/local/share/doc/op-1.32/README
Using 'setperms' and setugids' to reset the mode and ownership,
respectively, then verify:
        cobblepot [1] /bin/rpm --setperms -f /usr/local/share/doc/op-1.32/README
        cobblepot [0] /bin/rpm --setugids -f /usr/local/share/doc/op-1.32/README
        cobblepot [0] /bin/rpm -Vf /usr/local/share/doc/op-1.32/README
        .M......    /usr/local/bin/op
        S.5....T    /usr/local/etc/op.conf
        cobblepot [1] /bin/ls -ld /usr/local/share/doc/op-1.32/README
        -rw-r--r-- 1 root root 4551 Apr 17  2008 /usr/local/share/doc/op-1.32/README
After resetting the mode of the 'op-1.32-1' files, 'rpm' alerts us that
the mode for 'op' has changed.  Unfortunately, rather than updating
only those files which differ, 'rpm' applies the attributes found within
the pkg database for each respective file.  Equally unfortunate, 'rpm'
doesn't address suid, sgid, or sticky bits when setting the mode so we
have to reapply the suid bit to 'op' (see note 3):
        cobblepot [1] /bin/ls -ld /usr/local/bin/op
        -rwxr-xr-x 1 root root 159098 Apr 17  2008 /usr/local/bin/op*
        cobblepot [0] /bin/chmod u+s /usr/local/bin/op
        cobblepot [0] /bin/rpm -Vf /usr/local/share/doc/op-1.32/README
        S.5....T    /usr/local/etc/op.conf

NOTES

Note 0: File Integrity Tools:
        Samhain         (http://la-samhna.de/samhain/index.html) opensource
        AIDE            (http://aide.sourceforge.net/) opensource
        Tripwire        (http://sourceforge.net/apps/wordpress/tripwire/) opensource
                        (http://www.tripwire.com/) commercial
Note 1: Solaris stores its package database in readable flat files.
    The following is sample output from a package's stored 'pkgmap'
    file and the global pkg database file (contents):
    'pkgmap':

        - format is 'type class path mode owner group [size(bytes) sum(1) timestamp(epoch)]'

        sunspot [0] /bin/cat /var/sadm/pkg/SOCop/save/pspool/SOCop/pkgmap
        : 1 410
        1 d none bin 0755 root other
        1 f none bin/op 4755 root root 132576 4780 1199807701
        1 d none etc 0755 bin bin
        1 f none etc/op.conf 0600 root root 512 42802 1199808350
        1 d none man 0755 root other
        1 d none man/man1 0755 root other
        1 f none man/man1/op.1 0644 root other 10339 51920 1199807763
        1 i pkginfo 406 35258 1199808730
        1 d none share 0755 bin bin
        1 d none share/doc 0755 bin bin
        1 d none share/doc/op-1.32 0755 root other
        1 f none share/doc/op-1.32/AUTHORS 0644 root other 126 10963 1199807791
        1 f none share/doc/op-1.32/COPYING 0644 root other 1192 27717 1199807791
        1 f none share/doc/op-1.32/ChangeLog 0644 root other 7553 56702 1199807791
        1 f none share/doc/op-1.32/README 0644 root other 4551 1459 1199807791
        1 f none share/doc/op-1.32/op.conf 0644 root other 366 30654 1199807791
        1 f none share/doc/op-1.32/op.conf.complex 0644 root other 2656 27735 1199807791
        1 f none share/doc/op-1.32/op.pam 0644 root other 67 5744 1199807791
        1 f none share/doc/op-1.32/op.paper 0644 root other 14827 4222 1199807791

    'contents':

        - format is 'path type class mode owner group pkg [pkg pkg ...]'

        sunspot [0] /bin/grep SOCop /var/sadm/install/contents
        /usr/local/bin d none 0755 bin bin CUDLimlib2 CUDLft2 SMClibpng SMCncurs SMCslang SMCzlib SMCliconv SMCpcre SMCmutt SMCautoc SMCautom \
                SMCscreen SOCop SOCnc SMCjpeg SMCgcrypt SMClgpger SMCpango SMCtiff SMCfontc SMCglib SMCgnutls SMCgtk SMCwires SMCpidgin
        /usr/local/bin/op f none 4755 root root 132576 4780 1199807701 SOCop
        /usr/local/etc d none 0755 bin bin SMCslang SMCmutt SMCscreen SOCop SMCpango SMCfontc SMCgtk SMCpidgin
        /usr/local/etc/op.conf f none 0600 root root 512 42802 1199808350 SOCop
        /usr/local/man d none 0755 bin bin SMClibpng SMCncurs SMCslang SMCzlib SMCliconv SMCpcre SMCmutt SMCautoc SMCautom SMCscreen SOCop \
                SMCjpeg SMCpango SMCreadl SMCtiff SMCfontc SMCglib SMCgnutls SMCpidgin
        /usr/local/man/man1 d none 0755 bin bin SMCncurs SMCslang SMCliconv SMCpcre SMCmutt SMCautoc SMCscreen SOCop SMCjpeg SMCpango SMCtiff \
                 SMCglib SMCgnutls SMCpidgin
        /usr/local/man/man1/op.1 f none 0644 root other 10339 51920 1199807763 SOCop
        /usr/local/share d none 0755 bin bin CUDLft2 SMCncurs SMCslang SMCliconv SMCmutt SMCautoc SMCautom SMCscreen SOCop SOCnc SOCdelegate \
                SMCgcrypt SMClgpger SMCpango SMCrender SMCrenpro SMCtiff SMCatk SMCfontc SMCglib SMCgnutls SMCcairo SMCgtk SMCwires SMCpidgin
        /usr/local/share/doc d none 0755 bin bin SMCslang SMCliconv SOCop SOCdelegate SMCrender SMCrenpro SMCtiff SMCfontc
        /usr/local/share/doc/op-1.32 d none 0755 root other SOCop
        /usr/local/share/doc/op-1.32/AUTHORS f none 0644 root other 126 10963 1199807791 SOCop
        /usr/local/share/doc/op-1.32/COPYING f none 0644 root other 1192 27717 1199807791 SOCop
        /usr/local/share/doc/op-1.32/ChangeLog f none 0644 root other 7553 56702 1199807791 SOCop
        /usr/local/share/doc/op-1.32/README f none 0644 root other 4551 1459 1199807791 SOCop
        /usr/local/share/doc/op-1.32/op.conf f none 0644 root other 366 30654 1199807791 SOCop
        /usr/local/share/doc/op-1.32/op.conf.complex f none 0644 root other 2656 27735 1199807791 SOCop
        /usr/local/share/doc/op-1.32/op.pam f none 0644 root other 67 5744 1199807791 SOCop
        /usr/local/share/doc/op-1.32/op.paper f none 0644 root other 14827 4222 1199807791 SOCop
Note 2: 'pkgchk' options used:
        -a              audit file attrs (ownership / permissions)
        -f              correct file attributes if possible (does not account
                        for suid, sgid, or sticky bits, but takes care of all
                        other permissions / ownership)
        -l              list information on selected package or files (with -p)
        -p path         file path listing (multiples comma delimited)
Note 3: 'pkgchk' and 'rpm' will not correct suid, sgid, or sticky bits,
    though will handle all other modes.  Use 'chmod' to set suid, sgid,
    or sticky bits.

Note 4: sample 'rpm' dump output against a package or file (of note,
    'rpm' stores its package database in binary files):
        - format is 'path size mtime(epoch) md5sum mode owner group isconfig isdoc rdev symlink'

        cobblepot [0] /bin/rpm -q --dump op-1.32
        /usr/local/bin/op 159098 1208465003 3f0d9e96216ff29094bb5a90064fb314 0104755 root root 0 0 0 X
        /usr/local/etc/op.conf 658 1208465076 66e39e519cf98c494d1ed9b6866f7bdf 0100600 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/AUTHORS 126 1208465130 d86048753af9455c097dfd291e69b78a 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/COPYING 1192 1208465130 5c262c13b60ebefe3060aed37d334ab6 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/ChangeLog 7553 1208465130 8c4520c50f5fad805313c388f498c000 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/README 4551 1208465130 c8f9993fe17411a5ff1f1381d27a9c7d 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.conf 366 1208465130 c77ab047e3179da123694576715c2bb7 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.conf.complex 2656 1208465130 5958643c2bd5048436b322da820538cb 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.pam 67 1208465130 4e7ad4ec8f2fe6a40e12bcb2c0b256e3 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.paper 14827 1208465130 fb8a0d127d67e57184dc34949d39e42d 0100644 root root 0 0 0 X

        cobblepot [0] /bin/rpm -q --dump -f /usr/local/share/doc/op-1.32/README
        /usr/local/bin/op 159098 1208465003 3f0d9e96216ff29094bb5a90064fb314 0104755 root root 0 0 0 X
        /usr/local/etc/op.conf 658 1208465076 66e39e519cf98c494d1ed9b6866f7bdf 0100600 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/AUTHORS 126 1208465130 d86048753af9455c097dfd291e69b78a 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/COPYING 1192 1208465130 5c262c13b60ebefe3060aed37d334ab6 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/ChangeLog 7553 1208465130 8c4520c50f5fad805313c388f498c000 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/README 4551 1208465130 c8f9993fe17411a5ff1f1381d27a9c7d 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.conf 366 1208465130 c77ab047e3179da123694576715c2bb7 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.conf.complex 2656 1208465130 5958643c2bd5048436b322da820538cb 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.pam 67 1208465130 4e7ad4ec8f2fe6a40e12bcb2c0b256e3 0100644 root root 0 0 0 X
        /usr/local/share/doc/op-1.32/op.paper 14827 1208465130 fb8a0d127d67e57184dc34949d39e42d 0100644 root root 0 0 0 X
Note 5: 'rpm' options used:
        -q              query
        -f file         select package based on specified file
        --dump          dump file information for pkg files, format is:
                                path size mtime md5sum mode owner group isconfig isdoc rdev symlink
                        example: 
                                /usr/local/share/doc/op-1.32/README 4551 1208465130 c8f9993fe17411a5ff1f1381d27a9c7d 0100644 root root 0 0 0 X
        -V              verify
        --setperms      reset permissions of all of a pkg's files based
                        on those in pkg DB (does not account for suid
                        / sgid)
        --setugids      reset user / group ownership of all of a pkg's
                        files based on those in pkg DB
Note 6: 'rpm' verify output values (example: S.5....T):
        S               file size differs
        M               mode differs (permissions / file type)
        5               MD5 sum differs
        D               device major / minor number mismatch 
        L               readlink(2) path mismatch
        U               user ownership differs
        G               group ownership differs
        T               mtime differs

see also:
    Fixing an Overly Eager chown in Linux
    Fixing an Overly Eager chmod in Linux