Date: Thu, 28 Mar 2024 21:03:53 +0000 (UTC) Message-ID: <189920642.1.1711659833484@e95f621b173a> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_0_763086305.1711659833469" ------=_Part_0_763086305.1711659833469 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
Besides being a unique identifier attribute that has many good propertie= s (opaque, revocable, targeted), the persistent Name Identifier can also be= used for account checking.
To illustrate this, consider an e-learning platform that is used by thou= sands of Shibboleth users from many different organizations. For each user = the platform will most likely allocate an internal account that is created = using a Shibboleth identifier attribute or NameID as well as any common Shi= bboleth attribute like given name, surname, mail etc. Also assume that this= platform has been used for quite some years. It then is clear that the tot= al number of internal accounts will be growing constantly. In fact it may b= e the case that accounts never will be deleted because the administrators o= f this platform cannot know which accounts and related data on their e-lear= ning platform can be deleted. Even if the administrators have a last login = timestamp of an account, they cannot be sure that the person that owns this= account never will log in again on their platform. Whether such accounts (= and related data) can be deleted is not trivial to find out.
In order to determine whether an internal account of the above-mentioned= e-learning platform or another service can be deleted, the administrator f= irst must know whether the user behind that account still has an account at= his home organization. This could be easily checked if the administrator c= ould force the users in question to authenticate again and access the platf= orm. This however requires user interaction that cannot be enforced and pro= bablay wouldn't be very reliable anyway.
However, assuming the administrator knows the NameID of his users and as= suming this NameID is persistent, the administrator can relatively easy det= ermine whether an account still exists for a user at his home organization.= The basic principle here is to do an attribute request and ask the user's = home organization for his attributes as if the user just had accessed the p= latform. If the user's account still exists the attribute request will retu= rn the user's attributes that this SP is allowed to receive. Based on these= attributes the administrator or a script then can define a condition that = must be met in order to assume that an account still exists.
The Service Provider 2.x already comes with a handy tool called resolver= test that can be used to do the attribute query. All that is needed then in= order to find out whether a user's account still is active is the user's p= ersistent NameID. The above-mentioned condition that should be met will&nbs= p; most likely be something of the sort 'If the user's eduPersonPrincipal o= r mail attribute is returned, his account probably still exists'. Of course= it must be made sure that the Service Provider which does the attribute re= quest would receive the eduPersonPrincipal or mail attribute for a user bec= ause the Identity Provider will only return attributes that this SP is allo= wed to receive according to rules in the attribute-filter.xml.
Below you find a script that can be used to check whether a user's accou= nt still exists at his home organization (Identity Provider). Read the code= or execute
$ ./Ac= countChecker.sh -h
for details on how to use it:
#!/bin= /sh=20 #set -x ###########################################################################= ##### DESCRIPTION=3D" This shell script will check whether a user account identified by a SAML2 NameID exists at a given Identity Provider. accountChecking.sh -h Print these instructions -w Print instructions for setuid wrapper script -v Enable verbose mode -s Enable silent mode and just output status code -i NameID value to check=20 -f File containing NameIDs to check accountChecking.sh -h accountChecking.sh -w accountChecking.sh [-v] [-s] -i <NameID> accountChecking.sh [-v] [-s] -f <file> The script will do an attribute query using a provided NameID and the resolvertest binary that comes with every Shibboleth Service Provider. If the Identity Provider then returns at least one of the personal=20 attributes that can be configured below, the script assumes that=20 the account still exists. In order to execute the script, it must be copied to a host where=20 a Shibboleth Service Provider (SP) 2.x is installed because it requires the resolvertest binary that comes bundled with the SP. The script has to be called as user that is able to read the private key that is used by the Service Provider. Otherwise, resolvertest will output errors saying it cannot read the private key. The script can be executed with a NameID or a file as an argument. If the value that is provided to the script does not consist of the tripel= =20 'SP!IdP!NameID', the entityID of the default application of this SP is used as well as a default IdP that can be configured in this script. When called with the -i option the script returns the following status code= s: 0 If account probably doesn't exist anymore 1 If account still exists 255 If an error occured If the script is called with a file as argument (-f), it assumes that each = line of the file consists either of a NameID or the tripel 'SP!IdP!NameID'. It t= hen=20 will check each NameID and create a comma separated copy of the given file = with=20 the suffix '.result.csv'. This new file contains an additonal column where= =20 the numbers stand for the above-mentioned status codes. Status: Beta Version: 20091102 Authors: lukas.haemmerle@switch.ch " ###########################################################################= ##### ### CONFIG SECTION START ### # Path to Service Provider's resolvertest binary RESOLVERTEST=3D/usr/bin/resolvertest # NameID format to use FORMAT=3Durn:oasis:names:tc:SAML:2.0:nameid-format:persistent # Default IdP to use if IdP is not provided as argument IDP=3Dhttps://aai-logon.vho-switchaai.ch/idp/shibboleth # Personal attributes to check as proof of account's existence # The following values should correspond to the IDs or aliases of attribute # definitions in Shibboleth's attribute-map.xml file ATTRIBUTES=3D( eppn=20 uniqueID Shib-SwissEP-UniqueID=20 mail Shib-InetOrgPerson-mail=20 givenName Shib-InetOrgPerson-givenName surname Shib-Person-surname=20 Shib-SwissEP-DateOfBirth dateOfBirth=20 employeeNumber Shib-InetOrgPerson-employeeNumber) ### CONFIG SECTION END ### ###########################################################################= ##### SCRIPTPATH=3D"`pwd`/$(basename $0)" WRAPPERDESCRIPTION=3D" Account Checking SETUID Wrapper Script: ###########################################################################= #### USE THIS AT YOUR OWN RISK AND BE AWARE THAT FOLLOWING THESE INSTRUCTIONS COULD RESULT IN POTENTIAL SECURITY HOLES! ###########################################################################= #### It might be that you have to run the script as root user in order to allow= =20 access to the Service Provider's private key. If this is the case, you coul= d=20 use the following procedure: 1. Create a file called runAccountCheckingScript.c with this content:=20 -----------------------8<----------------------------- #include <unistd.h> #include <errno.h> main(int argc, char** argv) { if( setgid(getegid()) ) perror( \"setgid\" ); if( setuid(geteuid()) ) perror( \"setuid\" ); execv(\"$SCRIPTPATH\", argv); return errno; } -----------------------8<----------------------------- 2. Compile the C file with: $ gcc runAccountCheckingScript.c -o runAccountCheckingScript 3. Change owner of binary to root user: $ chown root:root runAccountCheckingScript 4. Set the setuid bit with: $ chmod 4755 runAccountCheckingScript Now any user can exeucte runAccountCheckingScript as root user who can read the Service Provider's private key.=20 " # Reset IFS for security IFS=3D$' \t\n' # Prevent all sorts of alias trickery unset -f unalias \unalias -a=20 unset -f command # Reset path variables for security path=3D/bin:/usr/bin #--------------------------------------------------------------------------= ----- # Checks whether attributes are returned for NameID parseNameID(){ =09 =09#Filter any suspicous characters=09 =09TLINE=3D`/bin/echo "$1" | sed 's/[$;]//g'` =09# Parse complete NameID =09NAMEIDP1=3D`/bin/echo "$TLINE" | cut -d! -f1` =09NAMEIDP2=3D`/bin/echo "$TLINE" | cut -d! -f2` =09NAMEIDP3=3D`/bin/echo "$TLINE" | cut -d! -f3` =09 =09# Check if we got the full NameID =09if [ "$NAMEIDP1" !=3D "$NAMEIDP3" ] ; then =09=09ID=3D$NAMEIDP3 =09=09IDP=3D$NAMEIDP1 =09else=20 =09=09ID=3D$NAMEIDP1 =09fi =09 =09if [ $VERBOSE -eq 1 -a $ID ] ; then =09=09/bin/echo "Checking NameID: $ID against Identity Provider: $IDP" =09fi =09 =09(CheckNameID "$ID" "$IDP") =09RETURNCODE=3D$?=09 =09if [ $SILENT -eq 1 ] ; then =09=09/bin/echo $RETURNCODE =09fi =09return $RETURNCODE } #--------------------------------------------------------------------------= ----- # Checks whether attributes are returned for NameID CheckNameID(){ =09TID=3D$1 =09TIDP=3D$2 =09 =09RESULT=3D"`\ $RESOLVERTEST \ -saml2 \ -f $FORMAT \ -n $TID \ -i $TIDP \ `" =09 =09ERRORS=3D"`/bin/echo \"$RESULT\" | /bin/grep ERROR`" =09 =09if [ "$ERRORS" !=3D "" -a "$NAMEID" !=3D "" -a $SILENT -eq 0 ] ; then =09=09/bin/echo "Erorr occured:" =09=09/bin/echo "$ERRORS" =09=09return 255 =09elif [ "$ERRORS" !=3D "" ] ; then =09=09return 255 =09fi =09 =09for name in ${ATTRIBUTES[@]} =09do =09=09FOUND=3D"`/bin/echo \"$RESULT\" | /bin/grep \"[^ID: $name:|^$name: ]\= "`" =09=09if [ "$FOUND" !=3D "" ] ; then =09=09=09if [ $VERBOSE -eq 1 ] ; then =09=09=09=09/bin/echo "Found personal attribute $name -> User account wi= th NameID $TID still exists"; =09=09=09fi =09=09return 1 =09fi =09done =09 =09if [ $VERBOSE -eq 1 ] ; then =09=09/bin/echo "No personal attributes found for user account with NameID= $TID -> User account probably doesn't exist anymore" =09fi =09return 0 } #--------------------------------------------------------------------------= ----- # Init some variables VERBOSE=3D0; SILENT=3D0; # Parse arguments while getopts hvswf:i: c do =09case $c in =09=09v) VERBOSE=3D1;; =09=09s) SILENT=3D1;; =09=09f) FILE=3D$OPTARG;; =09=09i) NAMEID=3D$OPTARG;; =09=09w) /bin/echo "$WRAPPERDESCRIPTION" =09=09exit 0;; =09=09h) =09=09/bin/echo "$0 [-h] [-v] -i persistentID" =09=09/bin/echo "$0 [-h] [-v] -f file" =09=09/bin/echo "$DESCRIPTION" =09=09exit 0;; =09=09[?])=20 =09=09/bin/echo "$0 [-h] [-v] -i persistentID" =09=09/bin/echo "$0 [-h] [-v] -f file" =09=09/bin/echo "$DESCRIPTION" =09=09exit 255;; =09esac done if [ "$FILE" =3D=3D "" -a "$NAMEID" =3D=3D "" ] ; then =09/bin/echo "You must provide at least the options -i or -f" =09exit 1 fi if [ "$FILE" !=3D "" -a "$NAMEID" !=3D "" ] ; then =09/bin/echo "You can only use -i or -f as options!" =09exit 1 fi # Parse file if [ "$FILE" !=3D "" ] ; then =09if [ ! -f $FILE ] ; then =09=09/bin/echo "File $FILE doesn't exist!" =09=09exit 1 =09fi=20 =09 =09if [ $VERBOSE -eq 1 -a $FILE ] ; then =09=09/bin/echo "Parsing file: $FILE" =09fi =09 =09RESULTFILE=3D"$FILE.result.csv" =09/bin/echo "#NAMEID,#RESULT" > $RESULTFILE =09while read LINE ;do =09=09(parseNameID "$LINE") =09=09/bin/echo "$LINE,$?" >> $RESULTFILE =09done < $FILE =09exit 0; elif [ "$NAMEID" !=3D "" ] ; then =09# Parse single NameID =09(parseNameID "$NAMEID") =09exit $? else =09/bin/echo "Something unexpected happened :-(" =09exit 255 fi #--------------------------------------------------------------------------= -----
You will of course need a *NIX operating system to execute the script. T= he script assumes that tools like grep and cut are available as well as the= resolvertest binary, which comes with the Service Provider 2.x. All of the= se binaries should be in the search path of the shell script.