IBM Security Verify

 View Only
Expand all | Collapse all

User Mapping for LMI Certificate authentication

  • 1.  User Mapping for LMI Certificate authentication

    Posted Fri March 10, 2023 07:47 AM

    Hello,

    To address one of the STIG findings, we need to enable PIV authentication for Security Verify Access LMI. We need to map one of the attributes from the certificate to the user id in LDAP. Has anyone written a user map function for this? The documentation gives a simple example of extracting 'cn' attribute and combining it with the baseDN. I want to list all available attributes and then determine which one can be used to generate the resultant LDAP dn. Also, I tried to place a System.out.println in the code,  but that did not work-any thoughts on what functions I can use to log some messages?

    Thanks,

    Rakesh 



    ------------------------------
    Rakesh Vohra
    Great Falls VA
    2405683495
    ------------------------------


  • 2.  RE: User Mapping for LMI Certificate authentication

    Posted Mon March 13, 2023 04:57 AM
    Edited by Peter Volckaert Mon March 13, 2023 04:57 AM

    Hi Rakesh,

    In Infomaps you can use the traceString() function. In this 'vintage' article traceString() and its use is explained: https://philipnye.com/2014/04/03/isam-for-mobile-trace-statements-in-mapping-rules/

    traceString() is a method of the Java class IDMappingExtUtils. Detailed documentation on Java is contained in the ISAM-javadoc.zip that can be downloaded from the appliance; see for example this link on how to download that .zip file: https://www.ibm.com/docs/en/sva/9.0.5?topic=policies-access-policy-development

    You can find lots of examples using Google or searching in the ISVA Documentation

    Cheers,

      - Peter.



    ------------------------------
    Peter Volckaert
    Senior Sales Engineer
    Authentication and Access
    IBM Security
    ------------------------------



  • 3.  RE: User Mapping for LMI Certificate authentication

    Posted Tue March 14, 2023 02:19 AM

    I have done this several times using an InfoMap AAC mechanism as the "certificate EAI" implementation, including for some very bespoke ASN.1 encoded extensions, using Javascript libraries to parse the x509 certificate data. The KJUR (actually jsrsasign - https://github.com/kjur/jsrsasign) library is your friend here - it's very easy to import that into your infomap and parse standard extensions with that. 

    Here's a sample InfoMap that acts as a certificate EAI where the username to login as can be found in a very specific OID encoded as an otherName part of the SAN extension. You can most likely dumb this down a lot for your own use case....

    importClass(Packages.com.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils);
    
    // must use jsrsasign 10.5.3 or later for otherName supoprt (see https://kjur.github.io/jsrsasign/api/symbols/KJUR.asn1.x509.GeneralName.html)
    importMappingRule("jsrsasign")
    
    function getRequestHeader(h) {
    	return context.get(Scope.REQUEST, "urn:ibm:security:asf:request:header", h);
    }
    
    function addOptionalResponseAttribute(attrName, attrValue) {
    	if (attrValue != null) {
    		context.set(Scope.SESSION, "urn:ibm:security:asf:response:token:attributes", attrName, attrValue);
    	}
    }
    
    function getPrincipalNameFromSAN(san) {
    	let result = null;
    	if (san != null && 
    		san["array"] != null &&
    		san["array"].length > 0) {
    		for (let i = 0; i < san["array"].length && result == null; i++) {
    			let aObj = san["array"][i];
    			if (aObj["other"] != null && 
    				aObj["other"]["oid"] == "1.3.6.1.4.1.311.20.2.3" &&
    				aObj["other"]["value"] != null &&
    				aObj["other"]["value"]["utf8str"] != null &&
    				aObj["other"]["value"]["utf8str"]["str"] != null) {
    				result = aObj["other"]["value"]["utf8str"]["str"];		
    			}
    		}		
    	}
    	return result;
    }
    
    let headerToAttribute = {
    	"cert": "cert",
    	"subjectcn": "SubjectCN",
    	"fingerprint": "fingerprint",
    	"subjectdn": "subjectDN",
    	"issuerdn": "issuerDN",
    	"subjectorganizationalunit": "subjectOU",
    	"alternativednsname": "alternativeDNSName",
    	"alternativeipaddress": "alternativeIPAddress",
    	"alternativeuri": "alternativeURI",
    	"alternativeemail": "alternativeEmail"
    };
    
    let headerMap = {};
    
    Object.keys(headerToAttribute).forEach((x) => {
    	let val = getRequestHeader(x);
    	if (val != null) {
    		headerMap[x] = ''+val;
    	}
    });
    
    IDMappingExtUtils.traceString("Entering CertEAI Infomap");
    
    // useful trace
    IDMappingExtUtils.traceString("CertEAI headers: " + JSON.stringify(headerMap));
    let eaiResult = false;
    
    if (headerMap["cert"] != null) {
    
    	// use the jsrsasign library to parse the x509 cert and extract SAN details
    	let mycert = new X509();
    	mycert.readCertHex(b64tohex(headerMap["cert"]));
    
    	let san = mycert.getExtSubjectAltName();
    	if (san != null) {
    		IDMappingExtUtils.traceString("SAN: " + JSON.stringify(san));
    	
    		let sanUPN = getPrincipalNameFromSAN(san);
    		if (sanUPN != null) {
    			// login as the username from UPN
    			IDMappingExtUtils.traceString("logging in as: " + sanUPN);
    			addOptionalResponseAttribute("username", sanUPN);
    			addOptionalResponseAttribute("AUTHENTICATION_LEVEL", "1");	
    			addOptionalResponseAttribute("san", JSON.stringify(san));
    			eaiResult = true;	
    		} else {
    			IDMappingExtUtils.traceString("Certificate SAN did not contain valid UPN");
    		}
    	}
    } else {
    	IDMappingExtUtils.traceString("Certificate information unavailable");
    }
    success.setValue(eaiResult);
    

    The configuration of it in the web reverse proxy is something like this:

    [certificate]
    accept-client-certs = required 
    eai-uri = /mga/sps/authsvc/policy/certeai
    eai-data = Base64Certificate:cert
    

    That should be plenty to get you started.

    Regards,
    Shane.



    ------------------------------
    Shane Weeden
    IBM
    ------------------------------



  • 4.  RE: User Mapping for LMI Certificate authentication

    Posted Tue March 14, 2023 07:22 AM
    Hello, Shane,

    Thanks for your reply and the information.

    In my case, it is the certificate authentication for the ISVA LMI. I am not sure if I could use a similar code there. The management authentication configuration portion of the LMI setup provides an option to use javascript to do user mapping. I am not sure what all functions and libraries are available when writing code for that user mapping. 

    Thanks,

    Rakesh





  • 5.  RE: User Mapping for LMI Certificate authentication

    Posted Tue March 14, 2023 10:46 PM
    Sorry about that - I missed "LMI" :)

    Anyway, this is not an area of ISVA I am intimately familiar with, but I have experiemented a bit and here's something to get started.

    First, I believe you will need to have System -> Administrator Settings -> Validate Client Certificate Identity turned ON before the user mapping function can work. 

    In my experimentation with the user mapping function, it appears it can only contain just one function "mapUser" (documentation here: https://www.ibm.com/docs/en/sva/10.0.5?topic=settings-configuring-management-authentication) and it should return the *username* (not the DN) of the user to authenticate into the LMI.

    You can access the X509Certificate as its *Java class* (java.security.cert.X509Certificate) via:
    var cert = props.get("cert");

    You can also use java.lang.System.out.println("some string") to print to the LMI message and trace logs (which you can monitor from the command line using isam logs monitor).
    Finally you can also turn on LMI trace (System -> Administrator Settings -> LMI Tracing) for the component com.ibm.mesa.security.authentication.modules=all to get trace of the authentication process (then monitor the LMI trace.log).

    Here's a more elaborate example that will print out the PEM version of the client certificate, then just maps everything to "admin". You should be able to use other standard Java APIs available from X509Certificate to parse other parts of the data.
    function mapUser(props) {
        // dump all available properties for debugging
        java.lang.System.out.println("props: " + props.toString());
    
        // this is java.security.cert.X509Certificate
        var cert = props.get("cert");
    
        // build the PEM version
        var b64encoder = java.util.Base64.getMimeEncoder(64, [ 0x0A ]);
        var pemcert = "-----BEGIN CERTIFICATE-----"
            + java.lang.System.lineSeparator();
            + b64encoder.encodeToString(cert.getEncoded());
            + java.lang.System.lineSeparator();
            + "-----END CERTIFICATE-----"
            + java.lang.System.lineSeparator();
    
        // and log it
        java.lang.System.out.println(pemcert);
    
        return "admin";
    }
    



    ------------------------------
    Shane Weeden
    IBM
    ------------------------------