Pages

Thursday, October 22, 2015

WARNING: The access control entry defines the ObjectType 'GUID' that can't be resolved.

I am getting the warning message after upgrading the exchange 2010 to exchange 2013 as follows:


WARNING: The access control entry defines the ObjectType 'GUID' that can't be resolved.

Solution:

1. Finding the Corrupted ObjectType in the Exchange 2013.


     Get-AdPermission "dc=example,dc=edu"

     After executing the Above command it will display the ACL entries for that object and also it will display the corrupted objectType. The sample output is given below:

example.edu     Everyone             True  False
example.edu     Everyone             False False
example.edu     NT AUTHORITY\ENTE... False False
example.edu     NT AUTHORITY\Auth... False False
example.edu     NT AUTHORITY\SYSTEM  False False
example.edu     BUILTIN\Administr... False False
example.edu     S-1-5-32-554         False False
example.edu     S-1-5-32-554         False False 


WARNING: The object example.edu has been corrupted, and it's in an inconsistent state. The following validation happened:
WARNING: The access control entry defines the ObjectType 'acd46e6d7-8d45-4516-a4b3-61c0e509b5be' that can't be resolved..

2. Finding the Corrupted ACL Entry

Get-ACl "AD:\Dc=example,dc=edu" | Select Access -ExpandProperty Access | Where-Object {$_.ObjectType -eq "'acd46e6d7-8d45-4516-a4b3-61c0e509b5be"} | Export-csv "acl.csv"

"ActiveDirectoryRights","InheritanceType","ObjectType","InheritedObjectType","ObjectFlags","AccessControlType","IdentityReference","IsInherited","InheritanceFlags","PropagationFlags"

"ExtendedRight","All","acd46e6d7-8d45-4516-a4b3-61c0e509b5be","00000000-0000-0000-0000-000000000000","ObjectAceTypePresent","Allow","example\testGroup","False","ContainerInherit","None"


3. Finding the Corresponding Corrupted Permissions

Get-ADPermission "dc=example,dc=edu" | Where-Object {$_.User -like "*testGroup"} ft identity,user,extendedrights,accessrights

Identity                      User                          ExtendedRights                AccessRights
--------                      ----                          --------------                ------------
example.edu              example\testGroup          {Change Password}  {ExtendedRight}
example.edu              example\testGroup                                           {ExtendedRight}

I have highlighted the corrupted ACL entry in the example.edu container.

4. Removing the Corrupted ACL entry in ADUC User Interface.

Login as as a domain admin and remove the acl entry as follows:

Right Click on example.ed domain --> Properties --> Security -->  Advanced --> Select the Corrupted ACL Entry --> Remove

The issue will be resolved after removing the corrupted acl entry.





Wednesday, August 19, 2015

BDB1538 Program version 5.3 doesn't match environment version 4.7

Hi,

I am getting the following exception in open ldap after upgrading the red hat 6 to 7:

 BDB1538 Program version 5.3 doesn't match environment version 4.7

Cause: 


Berkeley Data Base  upgraded from 4.7 to 5.3 while upgrading the redhat 6 to 7.

Solution:


Go to the /var/lib/ldap directory and upgrade the database file version from 4.7 to 5.3 as follows:

Currently I have the following files in the /var/lib/ldap directory

cn.bdb
dn2id.bdb
id2entry.bdb
objectClass.bdb
ou.bdb

Execute the following command to upgrade the database files as follows:

db_upgrade  cn.bdb
db_upgrade dn2id.bdbdb_upgrade id2entry.bdb
db_upgrade objectClass.bdb
db_upgrade ou.bdb

After execute the above commands, you need to change the ownership for the files as foolows:

chown ldap:ldap * -R

After executing the above command, it will change the ownership of the files.


Restart the service systemctl restart slapd. The ldap service restart successfully and the issue will be resolved.




Tuesday, July 14, 2015

Segmentation fault error 4 in liblftp-network.so

Hi,

I am getting the following errors in message log while mirroring the red-hat 7 latest packages using the mrepo and lftp 4.0.9. They are

Error:


lftp[26710]: segfault at 0 ip (null) sp 00007fffcd10e5e8 error 14 in lftp[400000+b000]
lftp[26769]: segfault at 18 ip 00007fdb03ff4808 sp 00007fff2270cda0 error 4 in liblftp-network.so[7fdb03fe2000+20000]

Cause:


Bug in lftp 4.0.9 package.

Solution:


To resolve the segmentation fault error, you need to install the latest lftp packages from the yum repo or download the source code to build and install lftp in the local system as follows:


Updating the lftp from repo

yum update lftp


Download the lftp

Download the lftp source code following url:

http://lftp.yar.ru/ftp/lftp-4.6.3a.tar.gz

Build the lftp:

Install the lftp from the source code as follows:

1. Pre-Requisite

Install the following packages to  install the lftp package:

yum install readline-devel gnutils-devel

2. Build Source Code

After installing the pre-requisite packages, please follow the procedure to build the lftp packages from the source code as follows:

./configure --prefix=/apps/lftp/ --with-gnutls=yes --with-openssl=yes
make
make install

It will install the lftp package in /apps/lftp directory.



Configuring lftp command

1. Edit the /etc/mrepo.conf and add replace the lftpcmd value with /apps/lftp/bin/lftp
2. Edit the /apps/lftp/etc/lftp.conf and add the following lines to download the redhat 7 packages from the cdn.redhat.com

set ssl:ca-file "/root/certs/redhat-uep.pem"
set ssl:key-file "/root/certs/cert-key.pem"
set ssl:cert-file "/root/certs/cert.pem"
set net:timeout 86400

3. Execute the following command to mirroring the redhat 7 packages:

    mrepo -guvvv redhat-7Server

    It will mirror the red-hat packages from the cdn.redhat.com

    Please refer my existing blog how to setup mrepo redhat 7
 

After installing the lftp latest 4.6.3a version, the segmentation issues are resolved.

Saturday, July 11, 2015

How to setup mrepo Redhat 7


The mrepo is a repository building tool for the synchronizing the remote repository to local yum or apt repositories.  The following configuration needs to be performed to configure mrepo local repositories in the redhat linux 7. They

1. Pre-requisite

Install the Red hat 7 Enterprise Edition.

2. Registering the System

Run the following command to register the system in red hat network.

subscription-manager register --username <Red Hat User Name> --password <Red Hat User Password> --auto-attach

After executing the above command, it will generate the client-key, client-cert and redhat-ca cert as follows:

Client Key : /etc/pki/entitlement/<Numeric Number>-key.pem
Client Cert:/etc/pki/entitlement/<Numberic Number>.pem
Redhat CA Cert: /etc/rhsm/ca/redhat-uep.pem

It will also generate the /etc/yum.repos.d/redhat.repo file.

3. Installing the Packages

 Run the yum update command to update the latest packages from the redhat network.

The following packages are required to configure the mrepo. They are

 3.1 lftp


       lftp is a ftp tool to get the repositories from the remote system. It supports ftp, sftp, http, and https protocols.

 3.2 httpd

       The httpd is a apache web server and it is being used to update the red hat systems in your network using yum repo.

 3.3 mrepo

      The mrepo is repository building tool and it being used to synchronize the remote system to local system repository using various protocols such as ftp,sftp, reposync, reposyncs, http and https.

 3.4 Installing lftp and httpd

    Run the  yum install lftp httpd command and it will install the lftp and httpd component in the linx system.

 3.5 Installing the mrepo

 By default mrepo package is not available in the redhat network and it is available in the repofroge repository.

Execute the following command download the mrepo:

wget http://pkgs.repoforge.org/mrepo/mrepo-0.8.8-0.pre1.el6.rft.noarch.rpm

After Downloading the mrepo rpm and execute the following command to install the mrepo.

yum install mrepo-0.8.8-0.pre1.el6.rft.noarch.rpm



4. Creating the Red Hat mrepo conf


Red Hat repository file needs to be created in the /etc/mrepo.conf.d/ directory and file name is redhat-7-x86_64.conf. This configuration is required because it will synchronize the remote repository to your local repository. Add the following content to the redhat-7-x86_64.conf file.

[redhat-7]
name = Red Hat Enterprise Linux Server $release ($arch)
release = 7
arch = x86_64
metadata = yum repomd

fastrack = https://cdn.redhat.com/rhel-x86_64-server-fastrack-7
os = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/os/Packages
extras = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/extras/os/Packages
rhn-tools = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/rhn-tools/os/Packages
oracle-java = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/oracle-java/os/Packages
rh-common = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/rh-common/os/Packages
supplementary = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/supplementary/os/Packages
optional = https://cdn.redhat.com/content/dist/rhel/server/7/7Server/x86_64/optional/os/Packages


5. Configuring the lftp.conf

The lftp.conf file needs to be configured because to store the lftp ssl parameter permanently in the global config. This configuration is required for to establish the connectivity from your local repository to remote repository.

5.1 SSL Configuration


set ssl:ca-file "/etc/rhsm/ca/redhat-uep.pem"
set ssl:key-file "/etc/pki/entitlement/<Numeric Number>-key.pem"
set ssl:cert-file "/etc/pki/entitlement/<Numeric Number>.pem"

5.2 Network Time Out


set net:timeout 86400

5.3 Testing the LFTP Connectivity

After configuring the step 5.1 and 5.2, you can verify the connectivity from local repository to remote repository as follows:

lftp  https://cdn.redhat.com/rhel-x86_64-server-fastrack-7

The above command will open the remote connectivity and you can navigate the files using lftp commands. For example, I want to list the data from the remote repository using ls command as follows:

ls -ltr

The above command will retrieve the data from the cdn.redhat.com network.


6. Configuring the Apache httpd conf

The mrepo.conf file needs to be modified in /etc/httpd/conf.d to display the repository info into the local repository web site. Edit the mrepo.conf file and search the Red Hat Enterprise Server5 string into the file and add next line to the Red Hat Enterprise Server 5.


AddDecription "Red Hat Enterprise Server 7 for x86_64" redhat-7-x86_64

 The redhat-7-x86_64 content is configured in the mrepo config file.  The redhat-7 is the section name and x86_64 is architecture. Both will be appended and created as directory /var/mrepo/redhat-7-x86_64 after synchronizing the repo using the mrepo command.


7. Synchronizing the Repository

Execute the following command to synchronize the red hat 7 repository to your local repository:

mrepo -guvv  <Repo Name>

For example my repo section name is redhat-7. The command look like this.

mrepo -guvv redhat-7

The above command will synchronize the remote repository to local repository.




8. Scheduling the Cron Job for Mrepo


After synchronizing the remote repository to local repository,  we need to synchronize the latest updates from remote repository to local repository using the Linux Daily Cron Job. For example my cron job will run every day at 2.30 AM and pull the changes from the redhat network.

We need to create  mrepo file in /etc/cron.d/ and add the following content to configure the cron job.

 30 2 * * * root /usr/bin/mrepo -q –ug


9. Enabling the Http Service

We need to enable the httpd service to restart automatically after rebooting the server. Execute the following command to enable the httpd service:

systemctl enable httpd

It will enable the service and also it create the symlinks for the httpd service as follows:

ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'


10. Configuring the Se-Linux Context

This step is mandatory only if your enable the se-linux. Configuring the selinux context for httpd  mrepo as follows:

semanage fcontext -a -t httpd_sys_content_t "/var/mrepo(/.*)?"

After executing the above command and it will configure the selinux context for mrepo and also it will persist the configuration details into the /etc/selinux/targeted/contexts/files/file_contexts.local file.

restorecon -R /var/mrepo

After executing the above command and it will read the configuration from the /etc/selinux/targeted/contexts/files/file_contexts.local file and enable the selinux context for the directory /var/mrepo

  


Monday, June 29, 2015

Kernal Panic - Not syncing : VFS: unable to mount root fs on unknown-block (0,0)

Hi,

I am getting the following error after updating the Linux System using yum update and rebooting the Linux Kernel Cent OS 7.

Kernal Panic - Not syncing : VFS: unable to mount root fs on unknown-block (0,0)

Solution:

1. Reboot the Linux Cent OS Virtual Machine.

2. Select the Existing Kernel from the Down Arrow.

3. Open the Terminal Window and Login as a root.

4. Remove the current kernel because the kernel got corrupted and execute the following command to remove the current kernel:

   yum remove kernel

5. Remove the yum cache because it will refer the older repositories from the cache. Execute the following command to clear the yum cache from the system:

   yum clean all

7. Update the Linux System because it will get the latest kernel from the yum repository. Execute the following command and it will update the kernel:

   yum update

8. Reboot the Virtual Machine and issue will be resolved. 

Thursday, June 18, 2015

How to setup a Cron Schedule job in Linux

I would like to setup Cron Schedule job to execute every minute in Red hat Linux. The following tasks needs to be performed to setup the Cron Schedule Job.

1. Create or Edit the Cron Tab File


Execute the following command to edit the cron tab file..

crontab -e

After executing the above command it will edit crontab file.


2. Schedule the Cron Job.

     2.1 Cron Tab Format

      The following format is being used to setup the cron job in Linux.

       2.1.1 Minute (0-59)
       2.1.2 Hour (0-23)
       2.1.3 Day Of Month (1-31)
       2.1.4 Month (1-12)
       2.1.5 Day of Week (0-6)
       2.1.6 Command

In my example I am going to setup the cron schedule job to run every minute and append the display output to a file as follows:

  * * * * * /home/testuser/display.sh >>display.out

 in the above example, I have used * * * * * because My Job needs to be run every minute.

The display.sh content is given below:

echo "Diaplying Message"


3. Listing the Cron Tab


After setting the cron job, you can list the all available jobs in your user space as follows:

 crontab -l

  After executing the above command it will display the following output:

* * * * * /home/testuser/display.sh >>display.out

4. Cron Job Store Location

After creating the Cron Schedule Job, Job details are stored in the following location with username as a file and this file owned by root:root.

 /var/spool/cron/testuser

 Login as a root and edit the /var/spool/cron/testuser file and verify the cron job content. In my example step 2 content and step 4 content are identical.


4. Logging

The cron schedule job running info will be displayed in the following log file:

/var/log/cron 

5. Error Messages

If your cron job throwing any error or cron job is not running due to fatal errors or command not found. The error messages are displayed in the following location.

/var/spool/mail/testuser

The sample error message is given below:

From root@test.ad.example.edu  Thu Jun 18 09:01:02 2015
Return-Path: <root@test.ad.example.edu>
X-Original-To: testuser
Delivered-To: testuser@test.ad.example.edu
Received: by test.ad.example.edu (Postfix, from userid 501)
id 385CE20357; Thu, 18 Jun 2015 09:01:02 -0700 (PDT)
From: root@test.ad.example.edu (Cron Daemon)
To: testuser@test.ad.example.edu
Subject: Cron <testuser@sssdlab-test> testuser /home/testuser/display.sh >>display.out
Content-Type: text/plain; charset=UTF-8
Auto-Submitted: auto-generated
X-Cron-Env: <LANG=en_US.UTF-8>
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/home/testuser>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=testuser>
X-Cron-Env: <USER=testuser>
Message-Id: <20150618160102.385CE20357@test.ad.example.edu>
Date: Thu, 18 Jun 2015 09:01:02 -0700 (PDT)

/bin/sh: testuser: command not found


I have setup the cron job to throw the error in the following format:

* * * * * testuser /home/testuser/display.sh >>display.out

In the above format command is testuser, for that reason, cron job throwing error message is "/bin/sh: testuser: command not found" 

The expected result is it will create a display.out file and the content is not appended to a file.

Thursday, June 4, 2015

SSSD Linux System Integrate With Active Directory


I would like to integrate Linux System Authentication against the centralized Active Directory using System Security Service Daemon (SSSD). SSSD supports two kinds mechanisms to integrate Linux System Authentication against AD for authentication. They are:

1. ID Mapping using ObjectSID in AD
2. Posix Attribute Mapping using posixAccount and posixGroup Object classes.


To implement the above mechanisms you need to configure the SSSD in the Linux System as a root user as follows:

1. Installing Packages

yum install sssd sssd-client krb5-workstation samba openldap-clients oddjob-mkhomedir sssd-libwbclient sssd-libwbclient-devel

2. Configuring the DNS Resolver


Edit the /etc/resolv.conf and verify the DNS Server entries are point to the naming servers and also search domain.  For example sample resolve.conf file is given below:

domain ad.example.edu
search ad.example.edu
nameserver ns1.example.edu
nameserver ns2.example.edu

Based on the above configuration it will obtain the ip address of the linux machine host from the domain naming service (DNS).

3. Configuring the Kerberos

This configuration is required for Active Directory Authentication using Kerberos protocol.
Edit the /etc/krb5.conf file and configure the Active Directory logging, libdefaults, realms, and domain realm sections as follows:

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = AD.EXAMPLE.EDU
 dns_lookup_realm = true
 dns_lookup_kdc = true
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true
 rdns = false

[realms]
 AD.EXAMPLE.EDU = {
  kdc = dcs01.ad.example.edu
  admin_server = dcs01.ad.example.edu
  default_domain = ad.example.edu
 }

[domain_realm]
 .ad.example.edu =AD.EXAMPLE.EDU
 ad.example.edu = AD.EXAMPLE.EDU


The realms and domain_realm section is required only if the domain discover service not available.

In the libdefaults section change the default_realm parameter according your environment.

In the realms section change  AD.EXAMPLE.EDU realms with your actual realm, The realm parameters kdc, admin_server, and default_domain values needs to be changed according to your environment.

In domain_realm section change realm names according to your environment.

4. Configuring the Samba

Configure the Samba Server to connect the Active Directory server. Edit the /etc/samba/smb.conf and add the following content under [global] section.

[global]
workgroup = EXAMPLE
client signing = yes
client use spnego = yes
kerberos method = secrets and keytab
log file = /var/log/samba/%m.log
password server = dcs01.ad.example.edu
realm = AD.EXAMPLE.EDU
security = ads

In the global section you need to modify the workgroup, password server, and realm according to your environment. The workgroup details are available for the ad object in the Active Directory User and Computer Console as follows: 

Right Click your Suffix (ad.example.edu) --> Find --> Enter Name (Search User Name) -->  Search Result --> Click Search Result --> Click Account --> User Logon Name (pre-Windows 2000).

The User Logon Name (pre-Windows 2000) value is your workgroup value.

The password server value is your ad domain controller.
The realm is your ad realm.

   

5. Joining the Linux Machine with Domain Controller

  After configuring the Steps 3 and 4, you need to perform the following tasks to join the Linux Machine with AD Domain Controller:

  5.1 Obtain Kerberos Credentials:

 The following command being used to obtain the kerberos credentials from the AD domain controller:

   kinit administrator
   
   After executing the above command and it will append the default realm with administrator account. For example user name will be administrator@AD.EXAMPLE>EDU. It will also ask the administrator password to authenticate user against AD domain controller. After successful authentication, kerberos key tab file will be generated in the /etc/krb5.keytab.

   If you found any issue while obtaining the Kerberos credentials from the AD domain controllers, you can execute the following command to trace the kerberos authentication errors in the console.

KRB5_TRACE=/dev/stdout kinit administrator 

 5.2 Joining Linux Machine to Domain Controller:

   After executing the step 5.1 successfully, you can execute the following command to join the Linux Computer to the AD Domain controller.

net ads join -k

After executing the above command successfully, the Linux machine will be added into the AD Domain controller. You can find the Linux machine name in the DomainJoinStaging orgainizational unit under ad.example.edu. The sample computer path is CN=testlinuxmachine,OU=DomainJoinStaging,DC=ad,DC=example,DC=edu

The CN is your Linux Machine Name. In my example my machine name is testlinuxmachine.ad.example.edu

You can verify computer name according your environment.



6. Configuring the SSSD authentication

Execute the following command to enable the SSSD authentication against the Active Directory:

authconfig --update --enablesssd --enablesssdauth –-enablemkhomedir

It will enable the sssd authentication and the changes will be written into the /etc/pam.d/ directory and also it will generate the nsswitch.conf file in the /etc/ directory.

7. Implementing the SSSD Mechanisms

After executing the step 6 it will enable the sssd authentication for the Linux Machine against with AD domain controller. But it will not create the /etc/sssd/sssd.conf file. You need to create the sssd.conf file under /etc/sssd/ directory and add the following content in the sssd.conf file.


[sssd]
domains =  ad.example.edu
services = nss, pam
config_file_version = 2

[domain/ad.example.edu]
id_provider = ad
auth_provider = ad
access_provider = ad
chpass_provider = ad
ad_domain = ad.example.edu
ad_server = dcs01.ad.example.edu
ad_hostname = dcs01.ad.example.edu
ldap_schema = ad
cache_credentials = true
debug_level = 1
ldap_idmap_default_domain_sid = <Domain SID>

krb5_auth_timeout = 30
krb5_realm = ad.example.edu

In the sssd section you need to modify domains, and services according to your environment. In my example domains value is ad.exampel.edu and services values are nss, pam, and pac.

The naming service switch (nss) being used to retrieve the Active Directory Identity Objects. For example id and groups.

The Pluggable Authentication Module (PAM) is being used to authenticate the user against the active directory.

You can configure the additional services as per your requirement. For example sudo service is being used to grant the Admin Sudo Access for certain users in the Linux System.

The [domain/ad.example.edu] section you need to modify provider name parameters for authentication, authorization, and changing passwords. In my example I have selected my provider name is ad because I am going to integrate Linux System with Active Directory. This provider is a generic provider for ldap. The example providers given below 

id_provider = ad
auth_provider = ad
access_provider = ad
chpass_provider = ad

You need to modify the ad domain controller parameter as follows:

ad_domain = ad.example.edu
ad_server = dcs01.ad.example.edu


ad_hostname = dcs01.ad.example.edu
ldap_idmap_default_domain_sid = <Domain SID>
krb5_realm = ad.example.edu

You need to modify the cache credential parameter because user needs to login off line.

    7.1 ID Mapping Using ObjectSID in AD

     This configuration copied under   [domain/ad.example.edu] section and it is required because it will create the user home directory and also it will generate the uid and guid based on the active directory objectSid parameter.

     ldap_idmap_range_min = 1824600000
     ldap_idmap_range_size = 2000000
     ldap_id_mapping = true
     fallback_homedir = /home/%u
   
     In the above configuration ldap_idmap_range_min and ldap_idmap_range_size parameters are optional. If you getting any ObjectSid to UID or GID conversion error, you need to include ldap idmap range parameters, otherwise it not required.
 
    You can specify the default home directory parameter fallback_homedir value to create the home directory after user login successfully to the Linux System.

   The ldap_id_mapping parameter value is true by default.
 

    7.2 Posix Attribute Mapping using posixAccount and posixGroup Object classes

    The following parameters needs to be modified when implementing the Posix Attribute mechanism.

      ldap_id_mapping = false
      ldap_user_home_directory = unixHomeDirectory
      

        7.2.1 Active Directory User Configuration

        The following modification needs to be done to implement with Posix Attribute Mechanism for the Active Directory User.
          a) You need to include the PosixAccount Object class in the User Object
          b) You need to add the following attributes in the user object after including the PosixAccount Object class. They are
          
           uidNumber:  The uidNumber is mapped to the unix system user uid
           gidNumber:  The gidNumber is mapped to the unix system group gid. This is a primary group id for the unix system and it is a single valued attribute. This value should map with active directory group gidNumber
           unixHomeDirectory :  The unixHomeDirectory mapped to the unix System User Home Directory
               loginShell:  The loginshell mapped to default shell for the linux system user.

              The user modification ldif script is given below using posixAccount object class for the existing user:

ldapmodify -x -D <Admin DN> -W -h <Ldap Host> -p 389
dn: CN=12345678,OU=people,DC=ad,DC=example,DC=edu
changetype: modify
add: objectClass
objectClass: posixAccount
-
replace: gidNumber
gidNumber: 50001
-
replace: uidNumber
uidNumber: 12345678
-
replace: unixHomeDirectory
unixHomeDirectory: /home/12345678
-
add: loginShell
loginShell: /bin/bash 



        7.2.2 Active Directory Group Configuration

        The following modification needs to be done to implement with Posix Attribute Mechanism for the Active Directory Group.
          a) You need to include the PosixGroup Object class in the Group Object
          b) You need to add the following attributes in the group object after including the PosixGroup Object class. They are
          
           gidNumber:  The gidNumber is mapped to the unix system group gid
           member :  The member field will mapped to the Linux User Groups.

           The group modification ldif script is given below using posixGroup object class for the existing group:

           dn: CN=developer,OU=Groups,DC=ad,DC=example,DC=edu
           changetype: modify

            replace: ObjectClass
            ObjectClass: top
            ObjectClass: Group
            ObjectClass: posixGroup
             -
            replace: gidNumber
            gidNumber: 50001
            -
            add: member
            Member: CN=12345678,OU=people,DC=ad,DC=example,DC=edu


           Note: The user object gidNumber must match with group object gidNumber. This values should be same while mapping the user to group to create the Linux System User.

  

8. Changing the Ownership

Execute the following command to change the Owner ship and also permission to read the sssd.conf file as a root user in the /etc/sssd directory.

chown root:root sssd.conf
chmod 600 sssd.conf

9. Restart the SSSD Service

Execute the following command to restart the sssd service.

service sssd restart

10. Testing the SSSD Authentication

Use any ssh client or putty. I am showing the example using the ssh in the command line to test the sssd authentication against active directory.

    ssh -l testuser@ad.example.edu sssdlab.ad.example.edu
 
   After entering the above command it will ask the user password and enter the user password and it will login to the Linux System and automatically create the home directory and default shell as you supplied in the sssd.conf file or from the ad if your using the posix attribute mechanism.
 

11. SSSD Log Location

If you found any error while authenticating the user against active directory. The following configuration needs to be done to logging the data as a debug mode.

   a) Edit the /etc/sssd/sssd.conf file
   b) Add the debug_level=9 parameter value under [domain/ad.example.edu] section
   c) The following log files will be created in the /var/log/sssd directory:

     krb5_child.log
     ldap_child.log
     sssd_ad.example.edu.log
     sssd.log
     sssd_nss.log
     sssd_pam.log
   
     If you found any error while starting the sssd service, check sssd.log file.
     If you found any authentication errors and getting the data from the AD, you can check sssd_ad.example.edu.log for authentication and extracting the data from AD.
     If you can verify request and response details from AD in the ldap_child.log
      If you can verify Kerberos request and response details from AD in the krb5_child.log
     You can check nss service error in sssd_nss.log and pam service errors in sssd_pam.log

12. Clearing SSSD Cache from memory.

      If you found any errors with related to ID Mapping after switching the ID Mapping to PosixAttribute Mapping vice versa. You need to clean the sssd data base and also sssd mem cache.
  
a) stop the sssd service as follows:

    service sssd stop

b) remove the database as follows:

    rm /var/lib/sssd/db/*

c) remove the mem cache as follows:

     rm /var/lib/sssd/mc/*
  
 d) Start the SSSD service as follows:

     service sssd start
   

13. Common Errors

13.1 pam_access(sshd:account): access denied for user `user1@example.edu' from `machine.example.edu'

   Cause: 

    The Linux System Enforce with Access Control Policies in the access.conf file.

   Solution:

    Add the User or Group info into the /etc/security/access.conf file and the issue will be resolved. The access.conf sample configuration is given below:

  + : (user1) : ALL

13.2 pam_sss(sshd:chauthtok): User info message: Password change failed. Server message: Old password not accepted

   The above error thrown while changing the password first logon in the SSSD Linux machine and also creating the home directory first time.

 Cause: 

 The Client Linux SSSD machine establish the connectivity from Work Station to Kerberos Authentication Server taking more than kerberos auth timeout value when changing the password on first logon. 

   Solution:

    Edit the /etc/sssd/sssd/conf file and increase the krb5_auth_timeout value. The default value is 6 seconds. You need to increase the timeout value according to your environment. For example  krb5_auth_timeout value is 60 seconds.
 

Tuesday, May 19, 2015

How to Export Active Directory Permissions


I want to export the Active Directory permissions using Get-ADPermission Command let as follows:

$orgunit = "Ou=Groups,dc=example,dc=edu"
$filename="Groups-Permissions.csv"



Get-AdObject -Filter 'Name -like "*"' -SearchBase $orgunit -SearchScope "Subtree" -Properties CanonicalName | Select @{Name="Identity";Expression={($_.CanonicalName)}} | Get-ADPermission | Select @{ Name='AccessRights';Expression={[string]::join(",", ($_.AccessRights))}},@{ Name='ExtendedRights';Expression={[string]::join(",", ($_.ExtendedRights))}},@{ Name='ChildObjectTypes';Expression={[string]::join(",", ($_.ChildObjectTypes))}},InheritedObjectType,@{ Name='Properties';Expression={[string]::join(",", ($_.Properties))}},Deny,InheritanceType,User,Identity,IsInherited,IsValid |  Export-csv $filename

In the above command let I have used the following command lets to extract the ad permissions from ou=groups container:

1. Get-ADObject
2. Select
3. Get-ADPermission
4. Select
5. Export-CSV

1. Get-ADObject

 Above command lets being used to retrieve all the objects under the ou=groups container.

 Get-AdObject -Filter 'Name -like "*"' -SearchBase $orgunit -SearchScope "Subtree" -Properties CanonicalName

 I have selected the property called CanonicalName, It contains the Identity of the AD Object. For example:

DistinguishedName: ou=Groups,dc=example,dc=edu
CanonicalName : example.edu/Groups

2. Select

  Select @{Name="Identity";Expression={($_.CanonicalName)}}

 Mapping the  CanonicalName to Identity attribute because Get-AdPermission command lets expecting the Identity value to retrieve the AD Permissions from the object.

3. Get-ADPermission

Retrieving all the permission in the container based on the Identity attribute from the step 2.

Get-ADPermission


4. Select


It is being used to select the attributes from the Get-ADPermission command let object.

Select @{ Name='AccessRights';Expression={[string]::join(",", ($_.AccessRights))}},@{ Name='ExtendedRights';Expression={[string]::join(",", ($_.ExtendedRights))}},@{ Name='ChildObjectTypes';Expression={[string]::join(",", ($_.ChildObjectTypes))}},InheritedObjectType,@{ Name='Properties';Expression={[string]::join(",", ($_.Properties))}},Deny,InheritanceType,User,Identity,IsInherited,IsValid

5. Export-CSV 

Exporting the selected attributes to a csv file:

 Export-csv $filename

Friday, February 27, 2015

How to develop SAML-ECP Custom Client

Pre-Requisite:

1. Installed and Configured Shibboleth IDP 3.0
2. Installed and Configured Shibboleth native SP 2.5.3 with ECP Enabled

Environment:

1. JDK 1.7
2. Eclipse
3. Gradle 1.5
4. Open SAML2.6.4
5. Apache Http Client 4.4
6. SL4J 1.7.10


Developing SAMl-ECP client

The following tasks needs to be performed to build the ECP clients:

1. Sending Request from ECP  to SP 
2. Sending Request from ECP  to IDP
3. Sending Request from ECP  to SP  


1. Sending Request from ECP  to SP

 Accessing the Secure URL through Enhanced Client Proxy(ECP) using the Get Request. In get request you need to add two header fields in the request:

1. Accept: application/vnd.paos+xml
2. PAOS: ver='url:liberty:paos:2003-08';'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'

Please refer example code : EcpRequestInterceptor  for adding the header variable in the request.

 1.1 Send the Get Request Secure URL from ECP to SP with PAOS Header

   The PAOS header variables are added in the EcpRequestInterceptor class.
     
     public Envelope accessProtectedPage()
    {
        Envelope envelope=null;
        HttpGet request= new HttpGet(ecp.getProtectedUrl());
        try
        {
            CloseableHttpResponse httpResponse =  client.execute(request);
            if(httpResponse != null)
            {
                if(httpResponse.getStatusLine().getStatusCode()==200)
                {
                    HttpEntity entity = httpResponse.getEntity();
                    envelope=convertRequestToSoap(entity);
                    if (!envelope.getBody().getUnknownXMLObjects().isEmpty())
                    {
                        if(envelope.getBody().getUnknownXMLObjects(AuthnRequest.DEFAULT_ELEMENT_NAME) != null)
                        {
                            logger.info("Auethentication Access"+convertSoapToString(envelope));  
                        }
                        else
                        {
                            throw new RuntimeException("Invalid ECP Response");
                        }
                    }
                    else
                    {
                        throw new RuntimeException("Invalid ECP Response");
                    }
                    EntityUtils.consume(entity);
                }
                else
                {
                    throw new RuntimeException(httpResponse.getStatusLine().getStatusCode()+" - " +httpResponse.getStatusLine().getReasonPhrase());
                }
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envelope;
    }


  1.2 SP will send the SOAP Response to ECP with PAOS Request  in SOAP Headers and Authentication Request Statement in Soap Body.

The Ecp response from the SP is shown below.

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <paos:Request xmlns:paos="urn:liberty:paos:2003-08" S:actor="http://schemas.xmlsoap.org/soap/actor/next" S:mustUnderstand="1"
        responseConsumerURL="https://shib-sp.example.edu/Shibboleth.sso/SAML2/ECP" service="urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"/>
        <ecp:Request xmlns:ecp="urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" IsPassive="0" S:actor="http://schemas.xmlsoap.org/soap/actor/next" S:mustUnderstand="1">
        <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://shib-sp.example.edu/shibboleth</saml:Issuer>
            <samlp:IDPList xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
                <samlp:IDPEntry ProviderID="https://shib-idp.example.edu/idp/shibboleth"/>
            </samlp:IDPList>
        </ecp:Request>
        <ecp:RelayState xmlns:ecp="urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" S:actor="http://schemas.xmlsoap.org/soap/actor/next"
         S:mustUnderstand="1">ss:mem:ec7e3978ddd1958ca9efb27be7c9445017daf9068b62c647cc398a4dfc73573c</ecp:RelayState>
    </S:Header>
    <S:Body>
        <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
            AssertionConsumerServiceURL="https://shib-sp.example.edu/Shibboleth.sso/SAML2/ECP"
            ID="_c06ac9aa1e8e172b920bd32435547b05"    
            IssueInstant="2015-02-27T00:04:38Z"
            ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS"
            Version="2.0">
                <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://shib-sp.example.edu/shibboleth</saml:Issuer>
                <samlp:NameIDPolicy AllowCreate="1"/>
                <samlp:Scoping>
                    <samlp:IDPList>
                        <samlp:IDPEntry ProviderID="https://shib-idp.example.edu/idp/shibboleth"/>
                    </samlp:IDPList>
                </samlp:Scoping>
        </samlp:AuthnRequest>
    </S:Body>
</S:Envelope>
     

2. Sending the Authentication Request from ECP to IDP with PAOS and Authorization Headers

 2.1 Detach the SP Soap Body and Create a new Soap Envelop and set the detached soap body to new envelop

You need to remove the SOAP headers from the SP Initial Secure URL response or You can copy the authentication request from the  SP Initial Secure URL response and build a new Soap Envelope. I have detached (copy) from the initial soap response and building the new soap request and sample code is given below:

public Envelope buildIdpAuthRequest(Envelope envelope)
    {
       
        Envelope newAuthRequest = new EnvelopeBuilder().buildObject();
       
        Body authBody= envelope.getBody();
        authBody.detach();
        newAuthRequest.setBody(authBody);
       
        return newAuthRequest;
       
    }

Sample Soap XML : 

<?xml version="1.0" encoding="UTF-8"?>
<soap11:Envelope xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
        <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
            AssertionConsumerServiceURL="https://shib-sp.example.edu/Shibboleth.sso/SAML2/ECP"
            ID="_b2c92bc8216ff317a5c42d797a7cd8ca" IssueInstant="2015-02-27T18:49:37Z"
            ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS"
            Version="2.0">
            <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://shib-sp.example.edu/shibboleth</saml:Issuer>
            <samlp:NameIDPolicy AllowCreate="1"/>
                <samlp:Scoping>
                    <samlp:IDPList><samlp:IDPEntry ProviderID="https://shib-idp.example.edu/idp/shibboleth"/></samlp:IDPList>
                </samlp:Scoping>
        </samlp:AuthnRequest>
    </S:Body>
</soap11:Envelope>

2.2 Send the Authentication Request to IDP with PAOS and Authorization Headers

You need to add the following header variables into the post request because these variables are required  for IDP ECP profile:

1. Accept: application/vnd.paos+xml
2. PAOS: ver='url:liberty:paos:2003-08';'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'
3. Authorization: Basic  + User Name and password encode with base 64.

The sample code is given below:

    public Envelope sendAuthenticationToIdp(String authRequestXml)
    {
        logger.info("Authentication Request from ECP to IDP :" + authRequestXml);
        Envelope envelope=null;
        HttpPost request= new HttpPost(ecp.getIdpUrl());
        request.addHeader(HttpHeaders.AUTHORIZATION, "Basic "+ Base64.encodeBytes((ecp.getUserName()+":"+ecp.getPassword()).getBytes()));
        try
        {
            request.setEntity(new StringEntity(authRequestXml));
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        try
        {
            CloseableHttpResponse httpResponse =  client.execute(request);
            if(httpResponse != null)
            {
                if(httpResponse.getStatusLine().getStatusCode()==200)
                {
                    HttpEntity entity = httpResponse.getEntity();
                    envelope=convertRequestToSoap(entity);
                    if (!envelope.getBody().getUnknownXMLObjects().isEmpty())
                    {
                        if(envelope.getBody().getUnknownXMLObjects(Response.DEFAULT_ELEMENT_NAME) != null)
                        {
                            logger.info("Auethentication Access"+convertSoapToString(envelope));
                            org.opensaml.saml2.core.Response samlResp=(org.opensaml.saml2.core.Response)envelope.getBody().getUnknownXMLObjects(org.opensaml.saml2.core.Response.DEFAULT_ELEMENT_NAME).get(0);
                            if(samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI))
                            {
                                System.out.println("Status  :"+samlResp.getStatus().getStatusCode().getValue());   
                            }
                            else
                            {
                               
                                StatusDetail detail= (StatusDetail)samlResp.getStatus().getStatusDetail().getUnknownXMLObjects(StatusDetail.DEFAULT_ELEMENT_NAME).get(0);
                                throw new RuntimeException("SAML Authentication Failed "+samlResp.getStatus().getStatusCode().getValue() + " - ");
                            }
                           
                        }
                        else
                        {
                            throw new RuntimeException("Invalid IDP ECP Response");
                        }
                    }
                    else
                    {
                        throw new RuntimeException("Invalid ECP Response");
                    }
                    EntityUtils.consume(entity);
                }
                else
                {
                    // it will throw 403 error due to invalid password or invalid user name or authentication failed
                    throw new RuntimeException(httpResponse.getStatusLine().getStatusCode()+" - " +httpResponse.getStatusLine().getReasonPhrase());
                }
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envelope;
       
    }


2.3 IDP will send the SOAP response to ECP with SAML Response in soap body and ECP response in SOAP Headers.

The sample Authentication Successful response from IDP as follows:
  
<?xml version="1.0" encoding="UTF-8"?>
<soap11:Envelope xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
    <soap11:Header>
        <ecp:Response xmlns:ecp="urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"
            AssertionConsumerServiceURL="https://shib-sp.example.edu/Shibboleth.sso/SAML2/ECP"
            soap11:actor="http://schemas.xmlsoap.org/soap/actor/next" soap11:mustUnderstand="1"/>
        <samlec:GeneratedKey xmlns:samlec="urn:ietf:params:xml:ns:samlec"
            soap11:actor="http://schemas.xmlsoap.org/soap/actor/next">Saml keys</samlec:GeneratedKey>
    </soap11:Header>
    <soap11:Body>
        <saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
            Destination="https://shib-sp.example.edu/Shibboleth.sso/SAML2/ECP"
            ID="_c860f132c4115581218971d92a52f500"
            InResponseTo="_b2c92bc8216ff317a5c42d797a7cd8ca"
            IssueInstant="2015-02-27T18:49:37.867Z" Version="2.0">
                <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://shib-idp.example.edu/idp/shibboleth</saml2:Issuer>
                <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                    <ds:SignedInfo>
                        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                        <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
                        <ds:Reference URI="#_c860f132c4115581218971d92a52f500">
                            <ds:Transforms>
                                <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                                <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                            </ds:Transforms>
                            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
                            <ds:DigestValue>Digest Base 64 encoded value </ds:DigestValue>
                        </ds:Reference>
                    </ds:SignedInfo>
                    <ds:SignatureValue>Signature Value Base 64 encoded</ds:SignatureValue>
                    <ds:KeyInfo>
                        <ds:X509Data>
                            <ds:X509Certificate>Certificate</ds:X509Certificate>
                        </ds:X509Data>
                    </ds:KeyInfo>
                </ds:Signature>
                <saml2p:Status>
                    <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
                </saml2p:Status>
                <saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
                    <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
                        Id="_37f9853b6561b3722b3691392ace994b"
                        Type="http://www.w3.org/2001/04/xmlenc#Element">
                            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#aes128-gcm"/>
                            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                                <xenc:EncryptedKey Id="_7dc4db379206ea3e491f5e6e15bbf6fb"
                                    Recipient="https://shib-sp.example.edu/shibboleth">
                                        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#rsa-oaep">
                                            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                                            <xenc11:MGF xmlns:xenc11="http://www.w3.org/2009/xmlenc11#"
                                                Algorithm="http://www.w3.org/2009/xmlenc11#mgf1sha1"/>
                                        </xenc:EncryptionMethod>
                                        <ds:KeyInfo>
                                            <ds:X509Data>
                                                <ds:X509Certificate>Certificate</ds:X509Certificate>
                                            </ds:X509Data>
                                        </ds:KeyInfo>
                                        <xenc:CipherData>
                                            <xenc:CipherValue>Cipher Value</xenc:CipherValue>
                                        </xenc:CipherData>
                                </xenc:EncryptedKey>
                            </ds:KeyInfo>
                            <xenc:CipherData>
                                <xenc:CipherValue>Certificate</xenc:CipherValue>
                            </xenc:CipherData>
                    </xenc:EncryptedData>
                </saml2:EncryptedAssertion>
        </saml2p:Response>
    </soap11:Body>
</soap11:Envelope>

3. Sending the IDP Authentication Response from ECP to SP to access the Secure URL Access. 

3.1 Add Initial Secure URL Access Relay State in IDP Soap Response Headers

The RelayState information is extracted from the initial Sp Secure Access Response from the SP Soap Envelop. This information is required in the soap header because, the SP going to accept the idp authentication SAML response and also make a decision to allow or denied requested secure page.

            RelayState relayState= (RelayState)secureResourceResponse.getHeader().getUnknownXMLObjects(RelayState.DEFAULT_ELEMENT_NAME).get(0);
            relayState.detach();

            Header header = new HeaderBuilder().buildObject();
            header.getUnknownXMLObjects().clear();
            header.getUnknownXMLObjects().add(relayState);
            idpAuthResponse.setHeader(header);



3.2 Extract the AssertionConsumerService URL from the IDP Soap Response

This assertionConsumerUrl parameter extracted from the Soap Header from the IDP Authentication soap response. This assertionConsumerUrl is being used to post the IDP Authentication response with Relay State header from ECP to Service provider post request.

String assertionConsumerUrl=((Response)idpAuthResponse.getHeader().getUnknownXMLObjects(Response.DEFAULT_ELEMENT_NAME).get(0)).getAssertionConsumerServiceURL();

3.3 Send IDP Authentication response to Service Provider with PAOS and Conetent-Type headers.

Sample Code:


    public Envelope sendAuthResponseToSp(String authResponseXml,String consumeServiceUrl)
    {
        Envelope envelope=null;
        HttpPost request= new HttpPost(consumeServiceUrl);
        request.addHeader(HttpHeaders.CONTENT_TYPE, EcpFlowConstants.PAOS_CONTENT_TYPE);
       
        try
        {
            request.setEntity(new StringEntity(authResponseXml));
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        try
        {
            CloseableHttpResponse httpResponse =  client.execute(request);
            if(httpResponse != null)
            {
                if(httpResponse.getStatusLine().getStatusCode()==200)
                {
                    HttpEntity entity = httpResponse.getEntity();
                    entity.writeTo(System.out);
                    logger.info("Cookies   "+store.getCookies());
                    EntityUtils.consume(entity);
                   
                   
                }
                else
                {
                    throw new RuntimeException(httpResponse.getStatusLine().getStatusCode()+" - " +httpResponse.getStatusLine().getReasonPhrase());
                }
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envelope;
       
    }



Example Code:


I have classified the example code 4 classes. They are

1. ECP Object

   This object is being used to capture the ECP Endpoint IDP Url, Protected Resource from the SP, User and Password to authenticate the IDP Server

2.  EcpRequestInterceptor

    This object is being used to add the ECP headers in each request.

3. EcpClientFlow

    This object being used to send the GET, POST request to IDP and SP using apache HttpClient.

4. EcpClient

This object is being used to Initialize the SAML Boot strap, and Interacting with EcpClientFlow object invoking the various request such as Accessing the Protected Page, Authenticating User from the IDP, and Finally Send the Authenticated IDP Response to Service Provider.


The Sample Code is given below:





package edu.example.ecp.client;

public class Ecp
{
    private String idpUrl;
    private String protectedUrl;
    private String userName;
    private String password;
    public String getIdpUrl() {
        return idpUrl;
    }
    public void setIdpUrl(String idpUrl) {
        this.idpUrl = idpUrl;
    }
    public String getProtectedUrl() {
        return protectedUrl;
    }
    public void setProtectedUrl(String protectedUrl) {
        this.protectedUrl = protectedUrl;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
   
   
}
   


package edu.example.ecp.client;

import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.ProxySelector;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.apache.http.util.EntityUtils;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml1.core.AuthenticationStatement;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.Status;
import org.opensaml.saml2.core.StatusCode;
import org.opensaml.saml2.core.StatusDetail;
import org.opensaml.saml2.ecp.Response;
import org.opensaml.ws.soap.soap11.Body;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.ws.soap.soap11.impl.EnvelopeBuilder;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.parse.BasicParserPool;
import org.opensaml.xml.parse.ParserPool;
import org.opensaml.xml.parse.XMLParserException;
import org.opensaml.xml.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


package edu.example.ecp.client;

public class EcpClientFlow
{
    private Ecp ecp;
    private BasicParserPool pool;
    private CloseableHttpClient client;
    private CookieStore store;
    private Logger logger= LoggerFactory.getLogger(EcpClientFlow.class);
   
    public EcpClientFlow(Ecp ecp)
    {
        this.ecp=ecp;
       
        //Creating the Parse Object
       
        pool= new BasicParserPool();
        pool.setNamespaceAware(true);
       
        //Creating the Pooled Http Connection Manager
        PoolingHttpClientConnectionManager manager= new PoolingHttpClientConnectionManager();
        manager.setMaxTotal(20);
        manager.setDefaultMaxPerRoute(10);
       
        //Creating the Cookie Store store the Cookies
        store= new BasicCookieStore();
        //Creating the Browser Compatible Request Cookie Object
        RequestConfig config= RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT).build();
       
        // Building the Client Builder
       
        HttpClientBuilder clientBuildert= HttpClients.custom().setConnectionManager(manager)
                .setDefaultCookieStore(store)
                // Add the ECP Header every request
                .addInterceptorFirst(new EcpRequestInterceptor())
                // Redirecting the Request
                .setRedirectStrategy(new LaxRedirectStrategy())
                .setDefaultRequestConfig(config);
        client=clientBuildert.setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault())).build();
    }
   
    public Envelope accessProtectedPage()
    {
        Envelope envelope=null;
        HttpGet request= new HttpGet(ecp.getProtectedUrl());
        try
        {
            CloseableHttpResponse httpResponse =  client.execute(request);
            if(httpResponse != null)
            {
                if(httpResponse.getStatusLine().getStatusCode()==200)
                {
                    HttpEntity entity = httpResponse.getEntity();
                    envelope=convertRequestToSoap(entity);
                    if (!envelope.getBody().getUnknownXMLObjects().isEmpty())
                    {
                        if(envelope.getBody().getUnknownXMLObjects(AuthnRequest.DEFAULT_ELEMENT_NAME) != null)
                        {
                            logger.info("Auethentication Access"+convertSoapToString(envelope));   
                        }
                        else
                        {
                            throw new RuntimeException("Invalid ECP Response");
                        }
                    }
                    else
                    {
                        throw new RuntimeException("Invalid ECP Response");
                    }
                    EntityUtils.consume(entity);
                }
                else
                {
                    throw new RuntimeException(httpResponse.getStatusLine().getStatusCode()+" - " +httpResponse.getStatusLine().getReasonPhrase());
                }
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envelope;
    }
   
    public Envelope sendAuthenticationToIdp(String authRequestXml)
    {
        logger.info("Authentication Request from ECP to IDP :" + authRequestXml);
        Envelope envelope=null;
        HttpPost request= new HttpPost(ecp.getIdpUrl());
        request.addHeader(HttpHeaders.AUTHORIZATION, "Basic "+ Base64.encodeBytes((ecp.getUserName()+":"+ecp.getPassword()).getBytes()));
        try
        {
            request.setEntity(new StringEntity(authRequestXml));
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        try
        {
            CloseableHttpResponse httpResponse =  client.execute(request);
            if(httpResponse != null)
            {
                if(httpResponse.getStatusLine().getStatusCode()==200)
                {
                    HttpEntity entity = httpResponse.getEntity();
                    envelope=convertRequestToSoap(entity);
                    if (!envelope.getBody().getUnknownXMLObjects().isEmpty())
                    {
                        if(envelope.getBody().getUnknownXMLObjects(Response.DEFAULT_ELEMENT_NAME) != null)
                        {
                            logger.info("Auethentication Access"+convertSoapToString(envelope));
                            org.opensaml.saml2.core.Response samlResp=(org.opensaml.saml2.core.Response)envelope.getBody().getUnknownXMLObjects(org.opensaml.saml2.core.Response.DEFAULT_ELEMENT_NAME).get(0);
                            if(samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI))
                            {
                                System.out.println("Status  :"+samlResp.getStatus().getStatusCode().getValue());   
                            }
                            else
                            {
                               
                                StatusDetail detail= (StatusDetail)samlResp.getStatus().getStatusDetail().getUnknownXMLObjects(StatusDetail.DEFAULT_ELEMENT_NAME).get(0);
                                throw new RuntimeException("SAML Authentication Failed "+samlResp.getStatus().getStatusCode().getValue() + " - ");
                            }
                           
                        }
                        else
                        {
                            throw new RuntimeException("Invalid IDP ECP Response");
                        }
                    }
                    else
                    {
                        throw new RuntimeException("Invalid ECP Response");
                    }
                    EntityUtils.consume(entity);
                }
                else
                {
                    // it will throw 403 error due to invalid password or invalid user name or authentication failed
                    throw new RuntimeException(httpResponse.getStatusLine().getStatusCode()+" - " +httpResponse.getStatusLine().getReasonPhrase());
                }
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envelope;
       
    }
   
   
    public Envelope sendAuthResponseToSp(String authResponseXml,String consumeServiceUrl)
    {
        Envelope envelope=null;
        HttpPost request= new HttpPost(consumeServiceUrl);
        request.addHeader(HttpHeaders.CONTENT_TYPE, EcpFlowConstants.PAOS_CONTENT_TYPE);
       
        try
        {
            request.setEntity(new StringEntity(authResponseXml));
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        try
        {
            CloseableHttpResponse httpResponse =  client.execute(request);
            if(httpResponse != null)
            {
                if(httpResponse.getStatusLine().getStatusCode()==200)
                {
                    HttpEntity entity = httpResponse.getEntity();
                    entity.writeTo(System.out);
                    logger.info("Cookies   "+store.getCookies());
                    EntityUtils.consume(entity);
                   
                   
                }
                else
                {
                    // it will throw 403 error due to invalid password or invalid user name or authentication failed
                    throw new RuntimeException(httpResponse.getStatusLine().getStatusCode()+" - " +httpResponse.getStatusLine().getReasonPhrase());
                }
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envelope;
       
    }

   
    public Envelope buildIdpAuthRequest(Envelope envelope)
    {
       
        Envelope newAuthRequest = new EnvelopeBuilder().buildObject();
       
        Body authBody= envelope.getBody();
        authBody.detach();
        newAuthRequest.setBody(authBody);
       
        return newAuthRequest;
       
    }
   
    public Envelope convertRequestToSoap(HttpEntity entity)
    {
        Envelope envlope=null;
        Document soapDoc;
        try
        {
            soapDoc = pool.parse(entity.getContent());
            Unmarshaller unmarshall = Configuration.getUnmarshallerFactory().getUnmarshaller(soapDoc.getDocumentElement());
            envlope=(Envelope)unmarshall.unmarshall(soapDoc.getDocumentElement());
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (XMLParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnmarshallingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return envlope;
    }
   

    public String convertSoapToString(Envelope envelope)
    {
        String result=null;
        try
        {
            Marshaller marshaller= Configuration.getMarshallerFactory().getMarshaller(envelope);
            Element element= marshaller.marshall(envelope);
            Transformer transformer;
            transformer = TransformerFactory.newInstance().newTransformer();
            Source source = new DOMSource(element);
            StringWriter writer= new StringWriter();
            StreamResult output = new StreamResult(writer);
            transformer.transform(source, output);
            result=writer.getBuffer().toString();
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (MarshallingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TransformerFactoryConfigurationError e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TransformerException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

}


package edu.example.ecp.client;

import org.opensaml.common.xml.SAMLConstants;

public interface EcpFlowConstants
{
    public String PAOS_CONTENT_TYPE="application/vnd.paos+xml";
    public String PAOS_HEADER=SAMLConstants.PAOS_PREFIX.toUpperCase();
}

   
   
package edu.example.ecp.client;

   
import org.opensaml.DefaultBootstrap;
import org.opensaml.saml2.ecp.RelayState;
import org.opensaml.saml2.ecp.Response;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.ws.soap.soap11.Header;
import org.opensaml.ws.soap.soap11.impl.HeaderBuilder;
import org.opensaml.xml.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class EcpClient {

    private static Logger logger= LoggerFactory.getLogger(EcpClient.class);

    private static String SECURE_URL="https://shib-sp.example.edu/sample/";
    private static String IDP_URL="https://shib-idp.example.edu/idp/profile/SAML2/SOAP/ECP";
    private static String USER_NAME="<User Name>";
    private static String PASSWORD="<Password>";

    public static void main(String[] args)
    {
        /*
        System.setProperty("org.apache.commons.logging.Log","org.apache.commons.logging.impl.SimpleLog");
        System.setProperty("org.apache.commons.logging.simplelog.showdatetime","true");
        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http","DEBUG");
        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http.wire","DEBUG");
        System.setProperty("org.apache.commons.logging.de.tudarmstadt.ukp.shibhttpclient","DEBUG");
        */
        Ecp ecp= new Ecp();
        ecp.setProtectedUrl(SECURE_URL);
        ecp.setIdpUrl(IDP_URL);
        ecp.setUserName(USER_NAME);
        ecp.setPassword(PASSWORD);
       
        try
        {
            DefaultBootstrap.bootstrap();
           
            EcpClientFlow clientFlow= new EcpClientFlow(ecp);
            Envelope secureResourceResponse = clientFlow.accessProtectedPage();
            Envelope authRequest= clientFlow.buildIdpAuthRequest(secureResourceResponse);
            Envelope idpAuthResponse = clientFlow.sendAuthenticationToIdp(clientFlow.convertSoapToString(authRequest));
            String assertionConsumerUrl=((Response)idpAuthResponse.getHeader().getUnknownXMLObjects(Response.DEFAULT_ELEMENT_NAME).get(0)).getAssertionConsumerServiceURL();
           
            RelayState relayState= (RelayState)secureResourceResponse.getHeader().getUnknownXMLObjects(RelayState.DEFAULT_ELEMENT_NAME).get(0);
            relayState.detach();
           
            Header header = new HeaderBuilder().buildObject();
            header.getUnknownXMLObjects().clear();
            header.getUnknownXMLObjects().add(relayState);
            idpAuthResponse.setHeader(header);
           
            clientFlow.sendAuthResponseToSp(clientFlow.convertSoapToString(idpAuthResponse), assertionConsumerUrl);
           
        } catch (ConfigurationException e)
        {
            logger.error("Ecp Client Error", e);
        }
       

    }

}

package edu.example.ecp.client;

import java.io.IOException;

import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.protocol.HttpContext;
import org.opensaml.common.xml.SAMLConstants;


public class EcpRequestInterceptor implements HttpRequestInterceptor,EcpFlowConstants {

    @Override
    public void process(HttpRequest request, HttpContext context)
            throws HttpException, IOException
    {
        request.addHeader(HttpHeaders.ACCEPT, PAOS_CONTENT_TYPE);
        request.addHeader(PAOS_HEADER,"ver="+SAMLConstants.PAOS_NS+";"+SAMLConstants.SAML20ECP_NS);
    }
}


Testing the Code:

 

Create the Source and Resource Directories


mkdir ecp-client/src/main/java -p
mkdir ecp-client/src/main/resources -p

Copy the Java Source files into  ecp-client/src/main/java directory.

Creating Build File



Create a build.gradle File and add the following content in the File and also copy the file to ecp-client directory:


apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'

repositories{
    mavenCentral()
}
dependencies{
    compile 'org.opensaml:opensaml:2.6.4','org.apache.httpcomponents:httpclient:4.4',
        'org.slf4j:log4j-over-slf4j:1.7.10','org.slf4j:slf4j-simple:1.7.10',
        'org.slf4j:slf4j-api:1.7.10'
}
mainClassName='edu.example.ecp.client.EcpClient'

Setting JAVA_HOME, GRADLE_HOME and PATH:


export JAVA_HOME=JavaHOME
export GRADLE_HOME=gradle_home
export PATH=$GRADLE_HOME/bin:$PATH


Setting the Classpath

Run the following command to creating the eclipse project and also setting the classpath

 gradle cleanEclipse eclipse


Running the Code

execute the gradle run and it will execute the ecp client code and display the output in the command line.



Common Errors:

Problem 1. Service Provider not sending the SOAP response with ECP Header while accessing the secure access page using the http client.

   Cause:  The following headers are not available in the request:

    
1. Accept: application/vnd.paos+xml
2. PAOS: ver='url:liberty:paos:2003-08';'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'


Solution: Add the above headers in the request and SP will send the SOAP request with ECP Request Headers and also Authentication Request in the Soap Body.


Problem 2. The Identity Provider throwing 500 error while sending the authentication request from ECP to IDP

   Cause:  The following headers are not available in the request:

    
1. Accept: application/vnd.paos+xml
2. PAOS: ver='url:liberty:paos:2003-08';'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'


Solution: Add the above headers in the request and DP will authenticate the user and send the SAML response in the soap body and also ecp response headers.

Problem 3. The Service Provider throwing 500 error while sending the authentication response from ECP to SP

   Cause:  The following headers are not available in the request:

    
1. Accept: application/vnd.paos+xml
2. PAOS: ver='url:liberty:paos:2003-08';'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'
3. Content-Type:  application/vnd.paos+xml


Solution: Add the above headers in the request and SP will accept the SAML response from the IDP issued assertion and It will redirect to requested target page.


Reference:

SAML 2.0 Profiles