#!/bin/bash # Smart Card KDE3.5 Authentication Script (c) 2010 Timothy Pearson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Maximum number of virtual terminals on this system MAXIMUM_VTS=49 # The [secure] temporary directory for authentication SECURE_DIRECTORY=/tmp/smartauth hexcvt () { echo ""$1" "16" o p" | dc } # Create the secure directory and lock it down rm -rf $SECURE_DIRECTORY mkdir -p $SECURE_DIRECTORY chown root $SECURE_DIRECTORY chgrp root $SECURE_DIRECTORY chmod 600 $SECURE_DIRECTORY SECURE_DIRECTORY=$(mktemp /tmp/smartauth/smartauthmon.XXXXXXXXXX) rm -rf $SECURE_DIRECTORY mkdir -p $SECURE_DIRECTORY chown root $SECURE_DIRECTORY chgrp root $SECURE_DIRECTORY chmod 600 $SECURE_DIRECTORY # Restart PCSCD and kill spurious processes killall -9 pcscd /etc/init.d/pcscd restart /etc/init.d/pcscd-nodbus restart # See if required programs are installed scriptor=$(whereis scriptor) if [[ $scriptor == "scriptor:" ]]; then echo "ERROR: scriptor is not installed! This program cannot continue!" exit fi opensc=$(whereis opensc-explorer) if [[ $opensc == "opensc-explorer:" ]]; then echo "ERROR: opensc-explorer is not installed! This program cannot continue!" exit fi get_file () { if [[ $COMMAND_MODE == "acos" ]]; then # Select EF $1 under DF 1000 echo "$SELECT_FILE $1" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 echo $(cat $SECURE_DIRECTORY/response2) # Read binary echo "$READ_BINARY" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 authokresponse="90 00 : Normal processing" response1=$(cat $SECURE_DIRECTORY/response2 | grep "$authokresponse") if [[ $response1 != "" ]]; then cat $SECURE_DIRECTORY/response2 | tr -d '\n' > $SECURE_DIRECTORY/response4 stringtoreplace="Using T=0 protocol00 B0 00 00 FF> 00 B0 00 00 FF< " newstring="" sed -i "s#${stringtoreplace}#${newstring}#g" $SECURE_DIRECTORY/response4 stringtoreplace=" 90 00 : Normal processing." newstring="" sed -i "s#${stringtoreplace}#${newstring}#g" $SECURE_DIRECTORY/response4 if [[ $2 == "text" ]]; then stringtoreplace=" 00" newstring="" sed -i "s#${stringtoreplace}#${newstring}#g" $SECURE_DIRECTORY/response4 fi echo $(cat $SECURE_DIRECTORY/response4) rm -f $SECURE_DIRECTORY/lukskey xxd -r -p $SECURE_DIRECTORY/response4 $SECURE_DIRECTORY/lukskey RESPONSE=$SECURE_DIRECTORY/lukskey fi fi if [[ $COMMAND_MODE == "cryptoflex" ]]; then FILE=${1/ /} echo "get $FILE" | opensc-explorer RESPONSE="3F00_$FILE" fi } update_file () { if [[ $COMMAND_MODE == "acos" ]]; then # Select EF $1 under DF 1000 echo "$SELECT_FILE $1" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 echo $(cat $SECURE_DIRECTORY/response2) # Update existing file # Zero pad input file dd if=/dev/zero of=$SECURE_DIRECTORY/response2 bs=1 count=255 dd if=$2 of=$SECURE_DIRECTORY/response2 bs=1 count=255 conv=notrunc # Truncate to 255 bytes and expand to standard hex listing format xxd -l 255 -ps -c 1 $SECURE_DIRECTORY/response2 > $SECURE_DIRECTORY/response cat $SECURE_DIRECTORY/response | tr '\n' ' ' > $SECURE_DIRECTORY/hexready echo "$UPDATE_BINARY $(cat $SECURE_DIRECTORY/hexready)" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 2>/dev/null echo $(cat $SECURE_DIRECTORY/response2) fi if [[ $COMMAND_MODE == "cryptoflex" ]]; then # Delete old file echo "$DELETE_FILE $1" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 2>/dev/null echo $(cat $SECURE_DIRECTORY/response2) # Create new file createfile "FF" $1 FILE=${1/ /} echo "put $FILE $2" | opensc-explorer fi } oldsmartcard_username="" echo "Ready..." while [[ 1 == 1 ]]; do sleep 1 echo "exit" | scriptor 2>/dev/null 1>/dev/null OUTPUT=$? if [[ $OUTPUT -eq 0 ]]; then echo "Card inserted!" echo "TAuthenticating SmartCard..." > /tmp/ksocket-global/kdesktoplockcontrol & # Get card ATR echo "RESET" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 authokresponse="OK: " response1=$(cat $SECURE_DIRECTORY/response2 | grep "$authokresponse") if [[ $response1 != "" ]]; then cat $SECURE_DIRECTORY/response2 | tr -d '\n' > $SECURE_DIRECTORY/response4 stringtoreplace="Using T=0 protocolRESET> RESET< OK: " newstring="" sed -i "s#${stringtoreplace}#${newstring}#g" $SECURE_DIRECTORY/response4 smartatr=$(cat $SECURE_DIRECTORY/response4) echo "Got ATR: $smartatr" if [[ $smartatr == "3B BE 18 00 00 41 05 10 00 00 00 00 00 00 00 00 00 90 00 " ]]; then echo "Detected ACOS5 card" COMMAND_MODE="acos" fi if [[ $smartatr == "3B 02 14 50 " ]]; then echo "Detected Schlumberger CryptoFlex card" COMMAND_MODE="cryptoflex" fi else echo "No card detected!" fi if [[ $COMMAND_MODE == "cryptoflex" ]]; then GET_CHALLENGE="C0 84 00 00 08" EXTERNAL_AUTH="C0 82 00 00 07 01" SELECT_FILE="C0 A4 00 00 02" DELETE_FILE="F0 E4 00 00 02" fi if [[ $COMMAND_MODE == "acos" ]]; then GET_CHALLENGE="00 84 00 00 08" EXTERNAL_AUTH="00 82 00 82 08" # Key 2 SELECT_FILE="00 A4 00 00 02" DELETE_FILE="00 E4 00 00 00" READ_BINARY="00 B0 00 00 FF" UPDATE_BINARY="00 D6 00 00 FF" ACTIVATE_FILE="00 44 00 00 02" fi # Authenticate card if [[ $COMMAND_MODE == "acos" ]]; then # Select MF echo "00 A4 00 00 00" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 echo $(cat $SECURE_DIRECTORY/response2) # Select DF 1000 under MF echo "$SELECT_FILE 10 00" > $SECURE_DIRECTORY/query scriptor $SECURE_DIRECTORY/query 1> $SECURE_DIRECTORY/response2 echo $(cat $SECURE_DIRECTORY/response2) fi echo $GET_CHALLENGE > $SECURE_DIRECTORY/authscript scriptor $SECURE_DIRECTORY/authscript | grep 'Normal processing' > $SECURE_DIRECTORY/challenge perl -pi -e 's/ //g' $SECURE_DIRECTORY/challenge perl -pi -e 's/:Normalprocessing.//g' $SECURE_DIRECTORY/challenge perl -pi -e 's/ -iv 1 if [[ $COMMAND_MODE == "acos" ]]; then # Truncate to 8 bytes dd if=$SECURE_DIRECTORY/response of=$SECURE_DIRECTORY/response2 bs=1 count=8 # Expand to standard hex listing format xxd -g 1 $SECURE_DIRECTORY/response2 $SECURE_DIRECTORY/response dd if=$SECURE_DIRECTORY/response of=$SECURE_DIRECTORY/response2 bs=1 count=23 skip=9 fi if [[ $COMMAND_MODE == "cryptoflex" ]]; then # Truncate to 6 bytes dd if=$SECURE_DIRECTORY/response of=$SECURE_DIRECTORY/response2 bs=1 count=6 # Expand to standard hex listing format xxd -g 1 $SECURE_DIRECTORY/response2 $SECURE_DIRECTORY/response dd if=$SECURE_DIRECTORY/response of=$SECURE_DIRECTORY/response2 bs=1 count=17 skip=9 fi # Assemble the response file response2=$(cat $SECURE_DIRECTORY/response2) response1="$EXTERNAL_AUTH ${response2}" echo $response1 > $SECURE_DIRECTORY/response # Send the response! scriptor $SECURE_DIRECTORY/response > $SECURE_DIRECTORY/response2 # Get the result authokresponse="< 90 00 : Normal processing" response1=$(cat $SECURE_DIRECTORY/response2 | grep "$authokresponse") echo $response1 if [[ $response1 != "" ]]; then echo "Smart card validation successfull!" # Get username and password get_file "10 02" "text" smartcard_username=$(cat $RESPONSE) get_file "10 03" "text" mv $RESPONSE $SECURE_DIRECTORY/password get_file "10 04" "text" smartcard_slave=$(cat $RESPONSE) if [[ $smartcard_slave == "SLAVE" ]]; then get_file "10 05" "text" smartcard_minutes=$(cat $RESPONSE) get_file "10 06" "text" internet_minutes=$(cat $RESPONSE) fi else echo "This card does not recognize this system!" echo "EInvalid SmartCard Inserted" > /tmp/ksocket-global/kdesktoplockcontrol & sleep 1 smartcard_username="" rm -f $SECURE_DIRECTORY/password smartcard_slave="" fi if [[ $smartcard_slave == "SLAVE" ]]; then if [[ $smartcard_minutes == "" ]]; then smartcard_minutes=1 fi # Decrement minutes on card if [[ $smartcard_minutes -gt 0 ]]; then let "smartcard_minutes=smartcard_minutes-1" echo $smartcard_minutes > $SECURE_DIRECTORY/minutes update_file "10 05" "$SECURE_DIRECTORY/minutes" fi if [[ $smartcard_minutes -eq 0 ]]; then echo "Minutes have been used up!" # Prohibit logon smartcard_username="" rm $SECURE_DIRECTORY/password fi mkdir -p /etc/smartmon echo $smartcard_minutes > /etc/smartmon/minutesremaining chmod 755 /etc/smartmon/minutesremaining fi # Initialize variables loginok=1 # Try to do the authentication result="" timeout=0 errcode=0 waserror=0 noactivesessions=0 result_is_consistent=0 while [[ $result_is_consistent == 0 ]]; do result_one=$(/opt/kde3/bin/kdmctl -g list) sleep 0.1 result_two=$(/opt/kde3/bin/kdmctl -g list) sleep 0.1 result_three=$(/opt/kde3/bin/kdmctl -g list) sleep 0.1 result_four=$(/opt/kde3/bin/kdmctl -g list) if [[ $result_one == $result_two ]]; then if [[ $result_one == $result_three ]]; then if [[ $result_one == $result_four ]]; then result=$result_one result_is_consistent=1 fi fi fi done if [[ $result == "ok" ]]; then noactivesessions=1 result="okbutempty" fi echo $result resultbkp=$result if [[ $errcode -eq 0 ]]; then # Allow KDM to finish starting if [[ $waserror -eq 1 ]]; then sleep 10 fi # Zero the desktop array index=0 while [[ $index != $MAXIMUM_VTS ]]; do darray[index]="" index=$((index+1)) done if [[ result != "okbutempty" ]]; then posone="0" posone=$(expr index "$result" " :") postwo="0" postwo=$(expr index "$result" ",") while [[ $posone != "0" ]]; do length=$((postwo-posone-1)) terminals="${result:posone:length}" echo $terminals # Delete the terminal we just got from the list of terminals result="${result:postwo}" postwo=$(expr index "$result" ",") result="${result:postwo}" postwo=$(expr index "$result" ",") length=$((postwo-1)) username="${result:0:length}" darray[terminals]=$username # Save username of this terminal echo $username result="${result:postwo}" postwo=$(expr index "$result" ",") result="${result:postwo}" # Now see if there might be ANOTHER terminal active or not posone="0" posone=$(expr index "$result" " :") postwo="0" postwo=$(expr index "$result" ",") done fi # See if the desired user is already logged in index=0 foundsession=0 while [[ $index != $MAXIMUM_VTS ]]; do if [[ ${darray[index]} == $smartcard_username ]]; then if [[ ${darray[index]} != "" ]]; then echo "Found existing session on desktop: ${index}" foundsession=1 # Check password lverify=$(/usr/bin/smartauthckpasswd -u ${darray[index]} -p $(cat $SECURE_DIRECTORY/password)) cverify="User:${darray[index]}" udisplay=":${index}" if [[ $lverify == $cverify ]]; then su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface quit" su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface enable false" /opt/kde3/bin/kdmctl activate $udisplay else echo "EUnauthorized SmartCard Inserted" > /tmp/ksocket-global/kdesktoplockcontrol & fi else echo "Username not specified" foundsession=2 sleep 1 fi fi index=$((index+1)) done if [[ $foundsession == "0" ]]; then echo "Existing session not found, starting new..." # Make sure that this is not display :0 (default login screen). # If it is, execute login. If not, create new session, then execute login usebasedisplay=0 if [[ $noactivesessions -eq 1 ]]; then newdisplay=$(ls /var/run/xdmctl/ | grep 'xdmctl-:0') echo $newdisplay if [[ $newdisplay != "" ]]; then usebasedisplay=1 fi fi vtsessions=$(echo "$resultbkp" | grep ',vt') if [[ $vtsessions == "" ]]; then newdisplay=$(ls /var/run/xdmctl/ | grep 'xdmctl-:0') echo $newdisplay if [[ $newdisplay != "" ]]; then usebasedisplay=1 fi fi echo "Creating new session" # Attempt login ls /var/run/xdmctl > $SECURE_DIRECTORY/originalxdm # Set loop separator to end of line BAKIFS=$IFS IFS=$(echo -en "\n\b") exec 3<&0 exec 0<"$SECURE_DIRECTORY/originalxdm" newdisplayfound=0 newdisplay=-1 while read -r line do # use $line variable to process lines line=$(echo $line | grep 'xdmctl-:' | sed -e 's/xdmctl-://') if [ "`expr $line - $line 2>/dev/null`" == "0" ]; then echo "Found active display on $line" if [[ $newdisplayfound -eq 0 ]]; then tempnewdisplay=$((newdisplay + 1)) if [[ $line -eq $tempnewdisplay ]]; then echo "Sequential display $line found after display $newdisplay..." newdisplay=$line fi fi fi done exec 0<&3 newdisplay=$(($newdisplay + 1)) newdisplay=":$newdisplay" echo "The next display to start will be $newdisplay" rm $SECURE_DIRECTORY/originalxdm /opt/kde3/bin/kdmctl -g reserve /opt/kde3/bin/kdmctl -g login $newdisplay now $smartcard_username $(cat $SECURE_DIRECTORY/password) sleep 2 /opt/kde3/bin/kdmctl -g activate $newdisplay udisplay=$newdisplay fi if [[ $smartcard_slave == "SLAVE" ]]; then if [[ $smartcard_minutes -lt 5 ]]; then su $smartcard_username -c "export DISPLAY=$udisplay; zenity --warning --text 'You have less than 5 minutes of computer time remaining' || exit 0" & fi fi rm -f $SECURE_DIRECTORY/password #if [[ loginok -eq 1 ]]; then # Wait for SmartCard removal echo "C" > /tmp/ksocket-global/kdesktoplockcontrol & TIMER=60 OUTPUT=0 while [[ $OUTPUT -eq 0 ]]; do sleep 1 su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface quit" su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface enable false" echo "exit" | scriptor 2>/dev/null 1>/dev/null OUTPUT=$? if [[ $smartcard_slave == "SLAVE" ]]; then TIMER=$(( TIMER - 1 )) if [[ $TIMER -eq 0 ]]; then # 60 seconds have passed, decrement minutes on card let "smartcard_minutes=smartcard_minutes-1" echo $smartcard_minutes > /etc/smartmon/minutesremaining chmod 755 /etc/smartmon/minutesremaining TIMER=60 echo $smartcard_minutes > $SECURE_DIRECTORY/minutes update_file "10 05" "$SECURE_DIRECTORY/minutes" if [[ $smartcard_minutes -eq 0 ]]; then echo "Minutes have been used up!" # Prohibit logon smartcard_username="" rm $SECURE_DIRECTORY/password fi mkdir -p /etc/smartmon echo $smartcard_minutes > /etc/smartmon/minutesremaining chmod 755 /etc/smartmon/minutesremaining if [[ $smartcard_minutes -eq 5 ]]; then su $smartcard_username -c "export DISPLAY=$udisplay; zenity --warning --text 'You have less than 5 minutes of computer time remaining' || exit 0" & fi if [[ $smartcard_minutes -eq 0 ]]; then echo "Minutes have been used up!" echo "Beginning logoff process" OUTPUT=254 fi fi fi done echo "Card removed!" # Is the user still logged in? result="ok" timeout=0 errcode=0 result_is_consistent=0 while [[ $result_is_consistent == 0 ]]; do result_one=$(/opt/kde3/bin/kdmctl -g list) sleep 0.1 result_two=$(/opt/kde3/bin/kdmctl -g list) sleep 0.1 result_three=$(/opt/kde3/bin/kdmctl -g list) sleep 0.1 result_four=$(/opt/kde3/bin/kdmctl -g list) if [[ $result_one == $result_two ]]; then if [[ $result_one == $result_three ]]; then if [[ $result_one == $result_four ]]; then result=$result_one result_is_consistent=1 fi fi fi done if [[ $result == "ok" ]]; then noactivesessions=1 result="okbutempty" fi echo $result # Zero the desktop array index=0 while [[ $index != $MAXIMUM_VTS ]]; do darray[index]="" index=$((index+1)) done posone="0" posone=$(expr index "$result" " :") postwo="0" postwo=$(expr index "$result" ",") while [[ $posone != "0" ]]; do length=$((postwo-posone-1)) terminals="${result:posone:length}" echo $terminals # Delete the terminal we just got from the list of terminals result="${result:postwo}" postwo=$(expr index "$result" ",") result="${result:postwo}" postwo=$(expr index "$result" ",") length=$((postwo-1)) username="${result:0:length}" darray[terminals]=$username # Save username of this terminal echo $username result="${result:postwo}" postwo=$(expr index "$result" ",") result="${result:postwo}" # Now see if there might be ANOTHER terminal active or not posone="0" posone=$(expr index "$result" " :") postwo="0" postwo=$(expr index "$result" ",") done # See if the desired user is still logged in index=0 foundsession=0 while [[ $index != $MAXIMUM_VTS ]]; do if [[ ${darray[index]} == $smartcard_username ]]; then if [[ ${darray[index]} != "" ]]; then echo "Found existing session on desktop: ${index}" udisplay=":${index}" foundsession=1 errcode=1 timeout=0 blankresult="" while [[ $blankresult != "true" ]]; do /opt/kde3/bin/kdmctl -g activate $udisplay su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface enable true" su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface lock" blankresult=$(su $smartcard_username -c "export DISPLAY=$udisplay; /opt/kde3/bin/dcop kdesktop KScreensaverIface isBlanked") if [[ $? != 0 ]]; then blankresult="true" fi logouttest=$(echo $blankresult | grep 'target display has no VT assigned') if [[ "$logouttest" != "" ]]; then echo "User has logged out" blankresult="true" fi done else echo "Username not specified!" sleep 1 fi fi index=$((index+1)) done #fi fi smartcard_username="" rm -rf /etc/smartmon/minutesremaining echo "C" > /tmp/ksocket-global/kdesktoplockcontrol & fi done