#!/bin/bash #------------------------------------------------------------------------------ # File eoslog # Futhor Geoffray Adde - CERN 2013 #------------------------------------------------------------------------------ #/************************************************************************ # * EOS - the CERN Disk Storage System * # * Copyright (C) 2013 CERN/Switzerland * # * * # * 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 .* # ************************************************************************/ #------------------------------------------------------------------------------ # Description: Script used to bind credentials to the shared eos fuse mount #------------------------------------------------------------------------------ function CheckCredDir { if [[ `stat -c "%a" /var/run/eos/credentials` != "1777" ]]; then logger -s -t eosfusebind -p authpriv.err -- "credential directory is not ready"; exit 1; fi if [[ `stat -c "%a" /var/run/eos/credentials/store` != "1777" ]]; then logger -s -t eosfusebind -p authpriv.err -- "credential store directory is not ready"; exit 2; fi } function ExitUsage { local exename=`basename $0` local nl=${#exename} local f="%${nl}.${nl}s" echo printf "$f %s\\n" $exename "[-g|--global] [krb5|x509] [credfile]" printf "$f %s\\n\n" "" "bind the current session to given credentials" printf "$f %s\\n\n" "" "[-g] makes a global binding : bind the user instead of the session" printf "$f %s\\n" $exename "-u|--unbind-session" printf "$f %s\\n\n" "" "unbind current session from any previsouly bound credentials" printf "$f %s\\n" $exename "-U|--unbind-user" printf "$f %s\\n\n" "" "unbind all sessions of the current user from any previously bound credentials" printf "$f %s\\n" $exename "-ug|--unbind-global" printf "$f %s\\n" $exename "-s|--show-session" printf "$f %s\\n\n" "" "show current session binding" printf "$f %s\\n" $exename "-S|--show-user" printf "$f %s\\n" "" "show bindings of all sessions of current user" printf "$f %s\\n" $exename "--pam" printf "$f %s\\n" "" "PAM module mode using environment variable PAM_TYPE. Recognized values for this variable are open_session and close_session" echo exit 3 } function ParseArgs { REQULNKGLB=0 REQULNKUSR=0 REQULNKSES=0 REQLSUSR=0 REQLSSES=0 REQGLOBBIND=0 AUTHTYPE="krb5" CREDTAG="" CREDISFILE="" if [[ $1 = "--acron" ]]; then # do some acron specfifcs shift 1 fi if [[ $# = 1 && ( $1 = "--pam" ) ]]; then if [[ "$PAM_TYPE" = "close_session" ]]; then CURRENTSID=`ps -q $$ o sid=` # we unbind the global binding if there is no other running session than the current one if [[ "`ps o sid= | grep -v $CURRENTSID | wc -l`" = "0" ]]; then REQULNKGLB=1; REQULNK=1; fi return elif [[ "$PAM_TYPE" = "open_session" ]]; then REQGLOBBIND=1; return else logger -s -t eosfusebind -p authpriv.err -- "PAM mode: Unknown PAM_TYPE='$PAM_TYPE'"; exit 4 fi fi if [[ $# = 1 && ( $1 = "-h" || $1 = "--help" ) ]]; then ExitUsage; fi if [[ $# = 1 && ( $1 = "-u" || $1 = "--unbind-session" ) ]]; then REQULNKSES=1; REQULNK=1; return; fi if [[ $# = 1 && ( $1 = "-U" || $1 = "--unbind-user" ) ]]; then REQULNKUSR=1; REQULNK=1; return; fi if [[ $# = 1 && ( $1 = "-ug" || $1 = "--unbind-global" ) ]]; then REQULNKGLB=1; REQULNK=1; return; fi if [[ $# = 1 && ( $1 = "-s" || $1 = "--show-session" ) ]]; then REQLSSES=1; REQLS=1; return; fi if [[ $# = 1 && ( $1 = "-S" || $1 = "--show-user" ) ]]; then REQLSUSR=1; REQLS=1; return; fi if [[ $1 = "-g" || $1 = "--global" ]]; then REQGLOBBIND=1; shift; fi if [[ $# -gt 1 ]]; then CREDTAG=$2; fi if [[ $# -gt 0 ]]; then AUTHTYPE=$1; fi } function UpdateCredIsFile { [[ "$AUTHTYPE" == "x509" ]] || [[ `echo $CREDTAG | grep -c ':'` = "0" || `echo $CREDTAG | cut -c1-5` = "FILE:" ]] CREDISFILE=$? # get rid of the 'FILE:' if it's specified if [[ "$CREDISFILE" = "0" && `echo $CREDTAG | cut -c1-5` = "FILE:" ]]; then CREDTAG=$(echo ${CREDTAG} | tail -c+6) # if the mentioned file does not exist, discard its name stat $CREDTAG >/dev/null 2>&1 [[ "$?" == "1" ]] && CREDTAG=""; fi; } function CheckArgs { if [[ "$EUID" = "0" && "$REQGLOBBIND" = "1" ]]; then logger -s -t eosfusebind -p authpriv.err -- "global user binding forbidden as root"; exit 5; fi if [[ "$AUTHTYPE" != "krb5" && "$AUTHTYPE" != "x509" ]]; then logger -s -t eosfusebind -p authpriv.err -- "invalid authentication type $AUTHTYPE"; exit 6; fi UpdateCredIsFile if [[ "$CREDISFILE" = "0" ]]; then if [[ "X$CREDTAG" != "X" && `stat -c "%a" $CREDTAG` != "600" ]]; then logger -s -t eosfusebind -p authpriv.err -- "credential file $CREDTAG does not exist or has bad permissions"; exit 7; fi fi } function GetKlistKrb5CredFile { which klist >/dev/null 2>&1 if [[ "$?" = "0" ]]; then CREDTAG=$(klist | head -1 | awk '{ print $3 }') else CREDTAG=$KRB5CCNAME fi } function GetDefaultKrb5CredFile { # if there is more than one credential, cannot chose a 'default' klist -s if [[ "$?" = "1" ]] ; then return; # there is not ticket to grab there fi NCRED=$(klist -l | tail -n +3 | wc -l) if [[ "$NCRED" != "1" ]]; then return; fi CREDTAG=$(klist -l | tail -1 | awk '{print $2}') } function GetAutoKrb5CredFile { GetKlistKrb5CredFile UpdateCredIsFile if [[ "X$CREDTAG" = "X" ]] || [[ "${CREDISFILE}" = "0" && `stat -c "%a" $CREDTAG` != "600" ]]; then GetDefaultKrb5CredFile; fi UpdateCredIsFile if [[ "X$CREDTAG" = "X" ]] || [[ "${CREDISFILE}" = "0" && `stat -c "%a" $CREDTAG` != "600" ]]; then logger -s -t eosfusebind -p authpriv.err -- "could not find automatically a credential file"; exit 8; fi #echo "using krb5 cache credential file $CREDTAG" } function GetEnvX509CredFile { CREDTAG=$X509_USER_PROXY } function GetDefaultX509CredFile { CREDTAG=/tmp/x509up_u$UID } function GetAutoX509CredFile { GetEnvX509CredFile if [[ "X$CREDTAG" = "X" || `stat -c "%a" $CREDTAG` != "600" ]]; then GetDefaultX509CredFile; fi if [[ "X$CREDTAG" = "X" || `stat -c "%a" $CREDTAG` != "600" ]]; then logger -s -t eosfusebind -p authpriv.err -- "could not find automatically a credential file"; exit 9; fi #echo "using x509 user proxy file $CREDTAG" } function GetSymlinkBaseNameUser { if test ! -n SymlinkBaseNameUser ; then return; fi SymlinkBaseNameUser="/var/run/eos/credentials/uid${UID}" } function GetSymlinkBaseNameSession { if test ! -n SymlinkBaseNameSession ; then return; fi GetSymlinkBaseNameUser PID=$$ PROCPIDSTATCONTENT=$(cat /proc/$PID/stat 2> /dev/null) PROCPIDSTATCONTENT=`echo $PROCPIDSTATCONTENT | sed 's/([^)]*)/execname/'` SID=`echo $PROCPIDSTATCONTENT | awk '{ print $6 }'` PROCPIDSTATCONTENT=$(cat /proc/$SID/stat 2> /dev/null) PROCPIDSTATCONTENT=`echo $PROCPIDSTATCONTENT | sed 's/([^)]*)/execname/'` SUT=`echo $PROCPIDSTATCONTENT | awk '{ print $22 }'` let "SUT/=`getconf CLK_TCK`" SymlinkBaseNameSession="${SymlinkBaseNameUser}_sid${SID}_sst${SUT}" } function CheckSessionLeader { if test ! -n SUT ; then logger -s -t eosfusebind -p authpriv.err -- "the session leader for the current session is not alive"; exit 10; fi } function ShowLink { local LINK="$1" local SID=`ls $LINK | grep -oh "sid[0-9]*" | cut -c 4-` local AUTHMET="${LINK##*.}" local FILE=`readlink -s $LINK` printf "%5d %4s %s\\n" $SID $AUTHMET $FILE } function ShowGlob { local LINK="$1" local AUTHMET="${LINK##*.}" local FILE=`readlink -s $LINK` printf "%4s %s\\n" $AUTHMET $FILE } function ShowHeader { echo echo "current session id is $SID" echo " session credentials are:" printf "%5s %4s %s\\n" sid type credfile } function ShowHeaderGlob { echo echo "current user is $USER" echo " user-global credentials are:" printf "%4s %s\\n" type credfile } function GetActiveLinkedSessions { if test ! -n ActiveLinkedSessions ; then return; fi ActiveLinkedSessions="" UnactiveLinkedSessions="" local FILELIST=$(shopt -s nullglob; echo ${SymlinkBaseNameUser}_*) if [[ "X$FILELIST" = "X" ]]; then return ; fi for LINK in `echo $FILELIST` ; do local lSID=`ls $LINK | grep -oh "sid[0-9]*" | cut -c 4-` local lSUT=`ls $LINK | grep -oh "sst[0-9]*" | cut -c 4-` # check if the process is active if test ! -d "/proc/$lSID/"; then UnactiveLinkedSessions="${UnactiveLinkedSessions} $LINK"; continue; fi local lPROCPIDSTATCONTENT=$(cat /proc/$lSID/stat 2> /dev/null) local lPROCPIDSTATCONTENT=`echo $lPROCPIDSTATCONTENT | sed 's/([^)]*)/execname/'` local lPROCSUT=`echo $lPROCPIDSTATCONTENT | awk '{ print $22 }'` let "lPROCSUT/=`getconf CLK_TCK`" # check the process has the same starting time if [ $lPROCSUT != $lSUT ]; then UnactiveLinkedSessions="${UnactiveLinkedSessions} $LINK"; continue; fi ActiveLinkedSessions="${ActiveLinkedSessions} $LINK" done } function ShowUserGlob { GetSymlinkBaseNameUser ShowHeaderGlob local FILELIST=$(shopt -s nullglob; echo ${SymlinkBaseNameUser}.*) #echo "FILELIST=$FILELIST" local NOTHING="" for LINK in `echo $FILELIST` ; do # don't show lock files ! [[ $LINK =~ .*\.lock ]] && continue ShowGlob $LINK ; NOTHING="FALSE" done if [[ "X$NOTHING" = "X" ]]; then echo "[none]"; return ; fi echo } function ShowSession { GetSymlinkBaseNameSession # if no session leader alive, nothing to show if [[ "X$SUT" = "X" ]]; then return ; fi local FILELIST=$(shopt -s nullglob; echo ${SymlinkBaseNameSession}*) if [[ "X$FILELIST" != "X" ]]; then ShowHeader for LINK in `echo $FILELIST` ; do ShowLink $LINK ; done echo return fi GetSymlinkBaseNameUser FILELIST=$(shopt -s nullglob; echo ${SymlinkBaseNameUser}.*) if [[ "X$FILELIST" = "X" ]]; then echo "The current session is not bound to any credentials" return fi ShowUserGlob } function ShowUser { GetSymlinkBaseNameSession GetActiveLinkedSessions ShowUserGlob ShowHeader local FILELIST=$(shopt -s nullglob; echo ${ActiveLinkedSessions}) if [[ "X$FILELIST" = "X" ]]; then echo "[none]"; return ; fi for LINK in `echo $FILELIST` ; do ShowLink $LINK ; done echo } function LinkSession { GetSymlinkBaseNameSession CheckSessionLeader local EXTENSION="${AUTHTYPE}" if [[ "$AUTHTYPE" == "krb5" && "${CREDISFILE}" != "0" ]]; then local EXTENSION="krk5" fi if [[ "X$CREDTAG" = "X" ]] || [[ "${CREDISFILE}" = "0" && `stat -c "%a" $CREDTAG` != "600" ]]; then logger -s -t eosfusebind -p authpriv.err -- "could not find automatically a credential file" exit 11 fi SymlinkName="${SymlinkBaseNameSession}.${EXTENSION}" #echo "ln -sf $CREDTAG $SymlinkName" # create the symlink and then rename it so the overwrite is atomic ln -sf $CREDTAG $SymlinkName.tmp if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Error creating credentials symlink." fi mv -Tf $SymlinkName.tmp $SymlinkName if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Error renaming temporary credentials symlink." fi } function LinkUserGlob { GetSymlinkBaseNameSession local EXTENSION="${AUTHTYPE}" local CFILE="$CREDTAG" if [[ "$AUTHTYPE" == "krb5" && "${CREDISFILE}" != "0" ]]; then local EXTENSION="krk5" fi if [[ "X$CREDTAG" = "X" ]] || [[ "${CREDISFILE}" = "0" && `stat -c "%a" $CREDTAG` != "600" ]]; then logger -s -t eosfusebind -p authpriv.err -- "could not find automatically a credential file" exit 12 fi SymlinkPathName="${SymlinkBaseNameUser}.${EXTENSION}" SymlinkName=$(basename $SymlinkPathName) if [[ "${CREDISFILE}" = "0" ]]; then # Create a temporary file TEMP_FILE=$(mktemp /var/run/eos/credentials/store/temp-XXXXXXXXXXX) if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Could not create temporary file inside /var/run/eos/credentials/store, something is wrong with the credential store." exit 13 fi # Ensure permissions are sane, should never fail chmod 0600 "${TEMP_FILE}" if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Could not chmod ${TEMP_FILE}, something is wrong in the credential store." stat "${TEMP_FILE}" >&2 rm -f ${TEMP_FILE} exit 14 fi # Replace contents of temporary file, should never fail cp -f "${CREDTAG}" "${TEMP_FILE}" if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Could not replace contents of ${TEMP_FILE}, something is wrong in the credential store." stat "${TEMP_FILE}" >&2 rm -f ${TEMP_FILE} exit 15 fi # Atomically replace CFILE with TEMP_FILE CFILE="/var/run/eos/credentials/store/$SymlinkName" mv -f --no-target-directory "${TEMP_FILE}" "${CFILE}" if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Could not replace ${CFILE} with ${TEMP_FILE}, something is wrong in the credential store." stat "${TEMP_FILE}" >&2 stat "${CFILE}" >&2 rm -f ${TEMP_FILE} exit 16 fi fi #echo "ln -sf $CFILE $SymlinkPathName" # create the symlink and then rename it so the overwrite is atomic ln -sf $CFILE $SymlinkPathName.tmp if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Error creating credentials symlink." fi mv -Tf $SymlinkPathName.tmp $SymlinkPathName if [ $? -ne 0 ]; then logger -s -t eosfusebind -p authpriv.err -- "Error renaming temporary credentials symlink." fi # cleanup the other credentials in the store as only one global credential is required at a time find /var/run/eos/credentials/store -uid ${UID} -not -name $( basename ${CFILE} ) -exec rm -f \{\} \; >/dev/null 2>&1 } function UnlinkUserGlob { GetSymlinkBaseNameUser if [[ "X$SymlinkBaseNameUser" = "X" ]]; then "error unbinding user"; exit 17; fi for symlink in ${SymlinkBaseNameUser}.*; do # remove the file from the store local file=$(readlink -f "$symlink") rm -f "$file" rm -f "$symlink" done; #echo rm -f ${SymlinkBaseNameUser}.* rm -f ${SymlinkBaseNameUser}.* # cleanup the other credentials in the store as no credentials is needed if there is no user global find /var/run/eos/credentials/store -uid ${UID} -exec rm -f \{\} \; >/dev/null 2>&1 } function UnlinkUserSessions { GetSymlinkBaseNameUser CheckSessionLeader #echo rm -f ${SymlinkBaseNameUser}_* rm -f ${SymlinkBaseNameUser}_* } function UnlinkSession { GetSymlinkBaseNameSession if [[ "X$SymlinkBaseNameSession" = "X" ]]; then "error unbinding session"; exit 18; fi #echo rm -f ${SymlinkBaseNameSession}* rm -f ${SymlinkBaseNameSession}* } function UnlinkUnactiveSessions { GetSymlinkBaseNameSession GetActiveLinkedSessions local FILELIST=$(shopt -s nullglob; echo ${UnactiveLinkedSessions}) if [[ "X$FILELIST" = "X" ]]; then return ; fi #echo rm -f $FILELIST rm -f $FILELIST } # if this a pam call, re-issue the command as the right user if [[ ( "$#" = 1 ) && ( "$1" = "--pam" ) && ( "$EUID" = "0" ) ]]; then if [[ ( "$PAM_USER" != "0" ) && ( "$PAM_USER" != "root" ) ]]; then export PATH=$PATH:/usr/sbin:/sbin runuser -f $PAM_USER -c "$0 $*" exit $? else logger -s -t eosfusebind -p authpriv.info -- "note: eosfusebind not running for user root from pam." exit 0 fi fi # re-iisue the call with a file lock to avoid race conditions if [[ "A${EOSFUSEBIND_NOLOCK}" != "A1" ]]; then GetSymlinkBaseNameUser env EOSFUSEBIND_NOLOCK=1 flock ${SymlinkBaseNameUser}.lock $0 $* exit $? fi ParseArgs $* CheckCredDir if [[ $REQULNK = 1 ]]; then if [[ $REQULNKSES = 1 ]]; then UnlinkSession; fi if [[ $REQULNKUSR = 1 ]]; then UnlinkUserSessions; fi if [[ $REQULNKGLB = 1 ]]; then UnlinkUserGlob; fi elif [[ $REQLS = 1 ]]; then if [[ $REQLSSES = 1 ]]; then ShowSession; fi if [[ $REQLSUSR = 1 ]]; then ShowUser; fi else CheckArgs $* UnlinkUnactiveSessions if [[ "$REQGLOBBIND" = "0" ]]; then UnlinkSession fi if [ "X$CREDTAG" = "X" ]; then if [ "$AUTHTYPE" = "krb5" ]; then GetAutoKrb5CredFile; fi if [ "$AUTHTYPE" = "x509" ]; then GetAutoX509CredFile; fi fi if [[ "$REQGLOBBIND" = "1" ]]; then LinkUserGlob else LinkSession fi fi