Archives

Creative Commons License
This blog is licensed under a Creative Commons License.

Script of the week: verify

| 2 Comments | No TrackBacks

This week’s script uses Leopard’s new xattr tool to store MD5 checksum information alongside any file you wish. Later, you can run the same script to ensure that the checksum has not changed.

For example, I run it on my Applications directory like this:

find /Applications -print0 | sudo xargs -0 verify

The first time it’s run, verify will print a lot of warnings about how none of the files currently have checksum information. But the second time you run it, it silently verifies that none of the files have changed. If there are changes, an error is printed to standard error, and an exit code of 1 is returned. It’s like a poor man’s Tripwire, but no external databases or configuration files are necessary, since it stores the checksum data in an extended attribute called “checksum”. This means that if you later move the file (even to another HFS+ volume), the verify script will still verify the current contents against the initial checksum.

If the file has legitimately changed, you must first delete the old checksum:

xattr -d checksum FILE

I recommend using this script on archival data, where changes are never expected. I created this script because I recently discovered some data corruption in a set of ISO files, and found to my dismay that ISO offers no built-in scheme to determine which files had been corrupted and which hadn’t, forcing me to throw out almost the entire lot. Now I use verify to ensure that from this point on, the ISO never changes.

On a security note: This scheme offers no security, unlike systems like Tripwire. A hacker who has permissions to change the file will also have permissions to reset the extended attribute containing the checksum.

Here’s the bash script:

#!/bin/bash

# verify, version 1.0
#   by John Wiegley <johnw@newartisans.com>

if [[ -z "$1" ]]; then
    echo "usage: verify [OPTIONS] <FILE ...>"
    echo "options:"
    echo "  -v     be verbose about verification/checksum setting"
    echo "  -s     only set checksums for files which don't have them"
    echo "  -f     force setting the checksum even if file doesn't match"
    exit 1
fi

ATTRNAME=checksum

verbose=false
setonly=false
force=false

while [[ "${1:0:1}" == "-" ]]; do
    if [[ "$1" == "-v" ]]; then
        verbose=true
        shift 1
    elif [[ "$1" == "-s" ]]; then
        setonly=true
        shift 1
    elif [[ "$1" == "-f" ]]; then
        force=true
        shift 1
    else
        break
    fi
done

error=false

for file in "$@"; do
    name=$(basename "$file")
    if [[ -f "$file" && "$name" != ".DS_Store" && \
          "$name" != ".localized" ]]; then
        CHKSUM=$(xattr -p $ATTRNAME "$file" 2> /dev/null)
        if [[ -z "$CHKSUM" ]]; then
            if [[ $verbose == true || $setonly == true ]]; then
                echo "Note: No existing checksum for $file, setting..."
            fi
            CURSUM=$(md5 -q "$file")
            xattr -w $ATTRNAME $CURSUM "$file"
            CHKSUM=$CURSUM
        elif [[ $setonly == false ]]; then
            CURSUM=$(md5 -q "$file")
            if [[ $CURSUM != $CHKSUM ]]; then
                echo "Error: Checksum mismatch for $file: $CURSUM != $CHKSUM" \
                    > /dev/stderr
                error=true
                if [[ $force == true ]]; then
                    xattr -w $ATTRNAME $CURSUM "$file"
                fi
            elif [[ $verbose == true ]]; then
                echo "Verified: $CHKSUM  $file"
            fi
        fi
    fi
done

[[ $error == true ]] && exit 1

exit 0

No TrackBacks

TrackBack URL: http://www.newartisans.com/mt/mt-tb.cgi/19

2 Comments

A simple way to add security would be to add a secret to the file data, e.g. “(echo -n $SECRET; cat $file) | md5”. Since the attacker (hopefully) won’t know the secret, they can’t calculate a valid checksum.

That’s a rather good idea. Actually, any string the attacker doesn’t know would be as secure, I could choose any random MD5 hash.

About this Entry

This page contains a single entry by John Wiegley published on January 6, 2008 8:09 PM.

FP techniques in Lisp: Data sharing was the previous entry in this blog.

A Ledger success story is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Recent Comments

  • Curt Sampson: That there’s “no state” in Haskell is quite wrong; in read more
  • rv: Hi. I wanted to drop you a quick note to read more
  • John Wiegley: It’s here: http://ftp.newartisans.com/pub/python/modpython_gateway.py read more
  • Leon: The file “modpython_gateway.py” Is no longer available in the downloads read more
  • Kathy: Well, the article is really the sweetest on this laudable read more
  • mr.design: Hi John, I just started to read your GFTBU, it’s read more
  • yoman: “Barfin”? “Slurping”? “Slime” “Hunchentoot” ??? What in the T.F. world read more
  • John Wiegley: Something like this is slated for the next release of read more
  • womens health: According to me, Apple has implemented something called blocks, which read more
  • Bjorn Tipling: Why would you add instructions for installing an editor when read more
OpenID accepted here Learn more about OpenID
Powered by Movable Type 4.261