#!/bin/sh # # usb-storage keyloader: script for loading ssh keys off of usb media # version 0.5 # # copyright (c) 2004, sean finney # this script is redistributable under the "give sean a free # pizza and you can do whatever the hell you want with it" license, # until sean recieves his first free pizza, or demand is sufficient # to license it otherwise. sean really would like a free pizza... # # this script is used to automatically load up ssh keys into a user's # ssh-agent session when inserted, and (by default) unload the keys # from the session when the usb media is unplugged # # this script requires that the keychain # (http://www.gentoo.org/projects/keychain.html) package is installed, # and that the user calls keychain from their .xsession/.xinitrc, # or otherwise somehow gets their ssh-agent info into a keychain # file (via the --inherit option, for example) # # this is how i do it in my .xsession: # # keychain -q --clear # . $HOME/.keychain/`uname -n`-sh # (window manager and other apps go here) # keychain -k # # users can specify preferences in ~/.keyloader (in /bin/sh sourceable # format), such as the names of keys on the usb keystick to load, # whether or not to let the key persist in ssh-agent when the # stick is removed, et c. # # specifically, set KEYS to be a whitespace delimited list of # paths to private keys, relative to the root of the usb drive # both the private and public key should be on the drive (i don't # believe the current code supports directories underneath the top # level one, but i could be wrong) # # to the security conscious: # # this script, though run as root, attempts to do as little as # possible while holding onto uid 0. whenever possible (and whenever # sourcing a user-writable file) the script is actually running # as the uid of the user on the desktop (see USERADDSCRIPT for # an example of how i'm doing this) # # so actually, this is in fact three scripts in one. you have the # "keyloader-master" which runs as root and calls the other two # scripts, which are dynamically generated and then run as the # user in question (so hopefully there shouldn't be any major security # concerns). # # TODO: let user specify partition number # TODO: determine or let user set usb filesystem type (currently assumes vfat) # there are a bunch of lines like these commented out throughout the script. # uncomment them and look at the corresponding files if you're interested # in debugging or understanding what's going on. #set -x #exec 1>/tmp/keyloader-master 2>&1 # where do we store the transient scripts executed by the desktop user? # KEYLOADERTMP is for scripts etc that are deleted when this script exits KEYLOADERTMP=/var/tmp # KEYLOADERCACHE is for scripts etc that are deleted at a later point KEYLOADERCACHE=/var/cache/keyloader # safely create some temporary files MNT=`mktemp -d $KEYLOADERTMP/keyloader-mnt-XXXXXX` USERADDSCRIPT=`mktemp $KEYLOADERTMP/keyloader-load-XXXXXX` USERDELSCRIPT=`mktemp $KEYLOADERCACHE/keyloader-unload-XXXXXX` # trap to remove temp files trap "rm -f $USERADDSCRIPT; rmdir $MNT" 0 # this is a check to prevent a clever user from filling up /var :) KEYSIZELIMIT=4096 # the default display, don't see why this would need to be overridden, # but it could be in the user prefs. DISPLAY=:0 # figure out who's running the X display OWNER=`w | awk '{print $1" "$2}' | grep "$DISPLAY\$" | awk '{print $1}'` # pubkeycache is where we store a copy of the public key, in case the # user wants to remove it later mkdir -p $KEYLOADERCACHE/pubkeys/$OWNER || true pubkeycache=$KEYLOADERCACHE/pubkeys/$OWNER # self explanatory. host=`hostname` # if ssh-askpass exists, load it into the environment if [ -z "$SSH_ASKPASS" ]; then SSH_ASKPASS=`which ssh-askpass` fi # if SSHASKPASS is null, then we'll need to pop up and xterm for to prompt # the user. otherwise just a simple ssh-add will suffice. if [ "$SSH_ASKPASS" ]; then export SSH_ASKPASS else SSHADDTERM="xterm -e" fi # # this creates a script run by the user in question that safely gives # us a way to let them put their config in a sourceable file. # (we later execute this script under the uid of the user in question) # cat << EOF > $USERADDSCRIPT #!/bin/sh #set -x #exec 1>/tmp/keyloader-useradd 2>&1 DISPLAY=$DISPLAY SSHASKPASS=$SSHASKPASS export DISPLAY SSHASKPASS export SSH_AGENT_PID export SSH_AUTH_SOCK . \$HOME/.keychain/$host-sh . \$HOME/.keyloader || true cd $MNT for key in \$KEYS; do $SSHADDTERM ssh-add "\$key" $USERDELSCRIPT #!/bin/sh #set -x #exec 1>/tmp/keyloader-userdel 2>&1 DISPLAY=$DISPLAY . \$HOME/.keychain/$host-sh . \$HOME/.keyloader || true cd $pubkeycache for key in \$KEYS; do ssh-add -d "\$key.pub" rm -f "\$key.pub" done EOF chmod a+rx $USERDELSCRIPT # figure out the emulated scsi device id according to dmesg # yeah, this is a hack. but i couldn't find a better way. scsi_id=`dmesg | grep "SCSI emulation for USB Mass Storage devices" |\ tail -1 | awk '{print $1}'` sleep 1 # and then use the scsi id to find the scsi device # again, an ugly hack. scsi_dev="/dev/"`dmesg | grep "Attached scsi removable disk [a-z]* at $scsi_id" | sed -e 's/Attached scsi removable disk \([^ ]*\) at.*$/\1/' | tail -1`"1" case $ACTION in add) chown $OWNER $MNT # a bit racy, eh... while ! mount -t vfat -o ro,noatime,nosuid,nodev $scsi_dev $MNT && [ "$try" != "xxx" ]; do sleep 5 try=x$try done chown -R $OWNER $pubkeycache su - $OWNER -c "$USERADDSCRIPT" wait umount $MNT # this is the removing script that's actually called by hotplug, which # should just run the userdelscript as the user in question echo $REMOVER cat << EOF > $REMOVER #!/bin/sh #set -x #exec 1>/tmp/keyloader-remover 2>&1 su - $OWNER -c "$USERDELSCRIPT" rm -f "$USERDELSCRIPT" EOF chmod a+rx $REMOVER ;; # should never actually make it here *) echo unhandled action $ACTION ;; esac