1#!/bin/bash 2# 3# sc_auth - smart card authorization setup script 4# 5# You can log in with a smart card if the authentication_authority field 6# of your user record contains an entry of the form 7# ;pubkeyhash;THEHASH 8# where THEHASH is the hex encoding of the SHA1 of the public key to be used. 9# (In keychains, this is the value in the Label attribute of keys, and of 10# the PublicKeyHash # attribute of certificate records.) 11# 12# This script allows you to get the hash from a smartcard, and to create 13# the appropriate authority entry in a user account. It also lets you list 14# and delete them. It works as is for (local) NetInfo directories. If you 15# use LDAP or more exotic directory sources, you'll have to find your own 16# way to store the authentication_authority information, but the workflow 17# is the same. Feel free to hack. 18# 19# This script assumes the Tiger version of the /usr/bin/security command. 20# It will probably not work (without modification) with future versions. 21# 22# This script has been updated to use the dscl command in place of the 23# deprecated nicl command. To use the standard name in the header file: 24# /System/Library/Frameworks/DirectoryService.framework/Headers/DirServicesConst.h 25# we have replaced "authentication_authority" with "AuthenticationAuthority" 26 27#set -x 28 29# general functions 30die() { echo "$*" 1>&2; exit 1; } 31note() { [ $verbose = yes ] && echo "$*" 1>&2; } 32 33usage() { 34cat <<EOU 35Usage: $(basename $0) accept [-v] [-u user] [-d domain] [-k keyname] # by key on inserted card(s) 36 $(basename $0) accept [-v] [-u user] [-d domain] -h hash # by known pubkey hash 37 $(basename $0) remove [-v] [-u user] [-d domain] # remove all public keys for this user 38 $(basename $0) hash [-k keyname] # print hashes for keys on inserted card(s) 39 $(basename $0) list [-v] [-u user] [-d domain] # list pubkey hashes that can authenticate this user 40EOU 41exit 2 42} 43 44# first argument is a command word 45[ -n "$1" ] || usage 46command=$1; shift 47 48# parse options 49user=${USER:-$(logname)} 50keyname= 51hash= 52verbose=no 53domain="." 54while getopts d:h:k:u:v arg; do 55 case $arg in 56 d) domain="$OPTARG";; 57 h) hash="$OPTARG";; 58 k) keyname="$OPTARG";; 59 u) user="$OPTARG";; 60 v) verbose=yes;; 61 esac 62done 63shift $(($OPTIND - 1)) 64 65 66# 67# Using "security dump-keychain", extract the public key hash for a key 68# on a smartcard and print it to stdout. 69# The optional argument is a regular expression to match against the 70# print name of the key. 71# Prints all matching keys; aborts if none are found. 72# 73hash_for_key() { 74 # hash_for_key [string in name] 75 string=${1:-'.*'} 76 HOME=/no/where /usr/bin/security dump-keychain | 77 awk -v RE="$string" ' 78 /^ 0x00000001/ { 79 if (matched = ($2 ~ RE)) { name=$0; sub("^.*<blob>=\"", "", name); sub("\"$", "", name); count++; }} 80 /^ 0x00000006/ { 81 if (matched) { hash=$2; sub("<blob>=0x", "", hash); print hash, name; }} 82 ' 83 HOME=/no/where /usr/bin/security dump-keychain | 84 awk -v RE="$string" ' 85 /^ 0x01000000/ { 86 if (matched = ($2 ~ RE)) { name=$0; sub("^.*<blob>=\"", "", name); sub("\"$", "", name); count++; }} 87 /^ 0x06000000/ { 88 if (matched) { hash=$2; sub("<blob>=0x", "", hash); print hash, name; }} 89 ' 90} 91 92 93get_hash() { 94 if [ -n "$hash" ]; then # passed in 95 echo "$hash" 96 else # find it 97 hash_for_key "$keyname" | 98 ( 99 read hash rest 100 [ -n "$hash" ] || die "No matching keys found" 101 [ $verbose = yes ] && note "Using key \"$rest\"" 102 echo $hash 103 ) 104 fi 105} 106 107 108accept_user() { 109 local hash="$1" 110 [ -n "$hash" ] || die "No hash specified" 111 dscl "$domain" -append "/Users/$user" AuthenticationAuthority ";pubkeyhash;$hash" 112} 113 114remove_user() { 115 set -- $(dscl "$domain" -read "/Users/$user" AuthenticationAuthority) 116 shift # skip authentication_authority: header 117 while [ -n "$1" ]; do 118 case "$1" in 119 \;pubkeyhash\;*) 120 dscl "$domain" -delete "/Users/$user" AuthenticationAuthority "$1" 121 [ $verbose = yes ] && note "Removed $1" 122 ;; 123 esac 124 shift 125 done 126} 127 128list_hashes() { 129 set -- $(dscl "$domain" -read "/Users/$user" AuthenticationAuthority) 130 shift # skip authentication_authority: header 131 while [ -n "$1" ]; do 132 case "$1" in 133 \;pubkeyhash\;*) 134 echo $1 | sed -e 's/;pubkeyhash;//' 135 ;; 136 esac 137 shift 138 done 139} 140 141 142case "$command" in 143 hash) hash_for_key "$keyname";; 144 accept) accept_user $(get_hash);; 145 remove) remove_user;; 146 list) list_hashes;; 147 *) usage;; 148esac 149