#StackBounty: #active-directory #ldap Secure LDAP Auth with SSL Cert

Bounty: 50

I’m wanting to setup secure LDAP authentication with an external service provider. The end user currently uses unsecured LDAP to the service provider. The service provider admits they way it had been originally implemented exposes credentials via packet capture.

I’ve reviewed:
https://support.microsoft.com/en-us/help/321051/how-to-enable-ldap-over-ssl-with-a-third-party-certification-authority

Their local domain is a *.local. The product manufacturer requires an SSL certificate that is signed by a valid certificate authority. This is good. I can create an SSL cert for the domain, but it won’t match that the Directory Service has.

I’m not sure exactly how the external auth is presented to the LDAP server, if it’s just passing the user name with domain name appended or if it verifies the LDAP server first.

Questions – Do I need to rename the domain to match? Would adding a UPN suffix allow for a work-around?

Edit:
External access through the Internet is required, thus the desire to secure LDAP.

Update text for clarity


Get this bounty!!!

#StackBounty: #authentication #active-directory #ldap #apache-zeppelin #shiro Zeppelin – LDAP Authentication failed

Bounty: 50

I am trying to configure ldap authentication in Zeppelin notebook. I have specified ldap server and other configurations by following this link. However, when I try to login I got following error:

ERROR [2019-12-23 17:52:12,196] ({qtp1580893732-66} LoginRestApi.java[proceedToLogin]:172) - Exception in login:
org.apache.shiro.authc.AuthenticationException: Authentication failed for token submission [org.apache.shiro.authc.UsernamePasswordToken - user1, rememberMe=false].  Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).
        at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:214)
        at org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106)
        at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:270)
        at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:256)
        at org.apache.zeppelin.rest.LoginRestApi.proceedToLogin(LoginRestApi.java:140)
        at org.apache.zeppelin.rest.LoginRestApi.postLogin(LoginRestApi.java:199)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)
       org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
        at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
      org.apache.shiro.realm.ldap.DefaultLdapRealm.queryForAuthenticationInfo(DefaultLdapRealm.java:371)
        at org.apache.zeppelin.realm.LdapRealm.queryForAuthenticationInfo(LdapRealm.java:268)
        at org.apache.shiro.realm.ldap.DefaultLdapRealm.doGetAuthenticationInfo(DefaultLdapRealm.java:295)
        at org.apache.zeppelin.realm.LdapRealm.doGetAuthenticationInfo(LdapRealm.java:217)
        at org.apache.shiro.realm.AuthenticatingRealm.getAuthenticationInfo(AuthenticatingRealm.java:568)
        at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:180)
        at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267)
        at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
        ... 78 more
 WARN [2019-12-23 17:52:12,197] ({qtp1580893732-66} LoginRestApi.java[postLogin]:206) - {"status":"FORBIDDEN","message":"","body":""}

Here is the shiro.ini file

ldapRealm=org.apache.zeppelin.realm.LdapRealm

ldapRealm.contextFactory.authenticationMechanism=simple
ldapRealm.contextFactory.url=ldap://10.16.0.113:389
ldapRealm.userDnTemplate=uid={0},ou=Users,dc=domain,dc=org,dc=com
# Ability to set ldap paging Size if needed default is 100
#ldapRealm.pagingSize = 200
#ldapRealm.authorizationEnabled=true
#ldapRealm.contextFactory.systemAuthenticationMechanism=simple
ldapRealm.searchBase=dc=domain,dc=org,dc=com
ldapRealm.userSearchBase=dc=domain,dc=org,dc=com
ldapRealm.groupSearchBase=ou=Users,dc=domain,dc=org,dc=com
ldapRealm.groupObjectClass=groupofnames
# Allow userSearchAttribute to be customized
ldapRealm.userSearchAttributeName = sAMAccountName
ldapRealm.memberAttribute=member
# force usernames returned from ldap to lowercase useful for AD
ldapRealm.userLowerCase = true
# ability set searchScopes subtree (default), one, base
ldapRealm.userSearchScope = subtree;
ldapRealm.groupSearchScope = subtree;
ldapRealm.memberAttributeValueTemplate=cn={0},ou=Users,dc=domain,dc=org,dc=com
ldapRealm.contextFactory.systemUsername=uid=domaindigital.banking,ou=Users,dc=domain,dc=org,dc=com
ldapRealm.contextFactory.systemPassword=Password1
securityManager.realms = $ldapRealm

Where am I going wrong. I need some assistance


Get this bounty!!!

#StackBounty: #ldap #kerberos #389-ds Seed Kerberos with existing LDAP users

Bounty: 50

My organization was using 389 Directory Server LDAP up until now to manage authentication. I was tasked with switching to Kerberos for that purpose but I still want to keep LDAP for non-auth relevant data.

My problem lies in seeding the existing users.

As I understand it, LDAP keeps the hashed password but Kerberos generates a key based on a plain text password when a user is created. Is there a way to use the hashed password instead? I know it’s possible in LDAP.

A friend ran into a similar problem and his solution was to capture the user/plain-text password request on every auth attempt and use that information to seed his Kerberos database for a few months (to get all ‘active’ users) but that poses some security risks.

Another option would be to send a massive email to all users forcing them to reset their password but I’d rather avoid that.

Is there a better way?


Get this bounty!!!

#StackBounty: #ldap #authentication #unix #pam #authorization PAM dynamic LDAP Authorization with groups

Bounty: 50

At the moment my PAM is integrated through LDAP with a custom authentication stack in the /etc/pam.d/systhem-auth:

auth        required      pam_env.so
auth        required      pam_faildelay.so delay=2000000
auth        sufficient    pam_unix.so nullok try_first_pass
auth required pam_listfile.so onerr=fail item=group sense=allow file=/etc/login.netgroup.allowed
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        sufficient    pam_ldap.so use_first_pass
auth        required      pam_deny.so

account     required      pam_unix.so broken_shadow
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet
account     [default=bad success=ok user_unknown=ignore] pam_ldap.so
account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass retry=3
password    sufficient    pam_unix.so sha512 nullok try_first_pass use_authtok
password    sufficient    pam_ldap.so use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session     optional      pam_systemd.so
session     optional      pam_mkhomedir.so skel=/etc/skel umask=077
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     optional      pam_ldap.so

As you can see the authorization is made by a lookup on the /etc/login.netgroup.allow file, which contains a list of LDAP groups. So, an user can login or not on this server if he belongs at least to one or more groups.

This check is made statically. I mean, the login.netgroup.allow file is immutable and it contains only a list of groups. Is there a way or any suggestion to made this check dynamically through an LDAP check? I mean, suppose I have an LDAP branch which contains an entry with the hostname of my server and a multivalue attribute containing the list of the groups associated to this server. Is it possible to made the check not to a file but directly on LDAP?


Get this bounty!!!

#StackBounty: #oauth #api #oauth2 #ldap Invalidate API tokens when LDAP user is invalid

Bounty: 50

I’m using oauth2 and LDAP for authorization and authentication of an API. Authentication flow is given below.

  1. User sends LDAP username and password (with password grand type) to
    the server.
  2. Server validates the password communicating with the LDAP server If valid, server returns an access token and a refresh token.

User keeps accessing APIs with access token until expired. When expired, the refresh token can be used to get a new access token and a new refresh token. The user can keep accessing the APIs forever updating the tokens.

Another requirement is making tokens invalid when user password is changed or when the user is disabled in the LDAP server. Is there anyway to do this?

So far I tried following approach:

Store the LDAP password hash in the server when initially user sends them.
When refreshing the token, fetch password hash of the user from the LDAP server and compare them to the one saved in the server.
But it looks like it’s not always possible to fetch the hash. It depends on the LDAP configuration,directory and Permissions


Get this bounty!!!

#StackBounty: #password #authentication #ldap #shadow #getent getent shadow shows password hashes for some users

Bounty: 400

If I execute

sudo getent shadow

I see password hashes for all the local users who have them. For most of the LDAP accounts I only see * in the password field. However, for a few LDAP users, I see password hashes. I have verified that they don’t have local accounts (i.e. no entry in /etc/passwd but getent passwd username returns a line).

In fact, logged in as myself, my user entry is one of the ones with an *.

There is a single LDAP server running OpenLDAP with a single group of accounts (one branch in the tree).

The LDAP-client servers are running nscd and libnss-ldap.

This is a portion of /etc/nsswitch.conf on the LDAP-client servers:

passwd:         files ldap
group:          files ldap
shadow:         files ldap

LDAP is used to authenticate the user for login to the LDAP-client server in this case and at some point the authentication mechanism, PAM in this case, must check a hash of the password provided by the user at login against the stored hash which is contained in the LDAP database.

Each user’s LDAP account stores their password hash and has the objectClasses of posixAccount and shadowAccount (among other values and settings).

Only system accounts have local entries in /etc/password and /etc/shadow. User accounts are all on LDAP (and only there). All users in LDAP have userPassword entries.

What might cause this or how can I run down the cause of it? Why doesn’t getent shadow return password hashes for all LDAP users?


Get this bounty!!!

LDAP Connector

Below is a sample code to perform LDAP Queries. Just modify the configuration information and then provide any valid query to get the search results.

You can also modify the code to get custom business logic as required.

 

How to do an LDAP Search of Various fields

Requirement: retrieving various values such as Description, Office, etc. from LDAP after authentication.

An LDAP client retrieves attribute values (referred to as “fields” in the question) by transmitting a search request to the server and then reading the server’s response. A search request consists of at a minimum the following components:

  • base DN – the object at which to begin the search. No objects above the base DN are returned
  • scope – the scope of the search; this is base, one, or subtree
  • filter – a filter which limits the entries that are returned by the server

Additionally, a list of requested attributes can be transmitted with the search request. Many LDAP SDKs will simply return all user attributes and no operational attributes if no requested attributes list is provided. In this case, request the attributes description and office and any others that are required.

LDAP-compliant servers enforce an access control scheme which might cause the server to not return certain attributes. Consult with the LDAP administrators to determine if the authentication state of the LDAP client connections have permission to access the attributes desired.

see also

  • LDAP: Using ldapsearch: this article refers to the ldapsearch command line tool, but the concepts are the same as for programmatic access.

Code Ref:

public boolean authenticate(String userid, String pass, String domain) {
        boolean retval = false;
        String searchFilter ="(&(objectClass=user)(" + LDAP_UID_ATTR + "=" + userid + "))";


        try {
            System.out.println("Start: getLDAPAttrs");
            NamingEnumeration answer =
                getLDAPAttrs(userid, pass, searchFilter, domain);
            String uid = "";

            while (answer.hasMoreElements()) {
                SearchResult sr = (SearchResult)answer.next();

                Attributes attrs = sr.getAttributes();

                try {
                    uid = attrs.get(LDAP_UID_ATTR).toString();
                    System.out.println("uid: " + uid);
                    System.out.println(attrs.get("mail"));
                    uid = uid.substring(uid.indexOf(':') + 2);
                } catch (Exception err) {
//                    uid = "";
                    System.out.println(err.getMessage());
                    err.printStackTrace();
                }

                // verify userid
                if (userid.equalsIgnoreCase(uid)) {
                    retval = true;

                    break;
                }
            }
        } catch (NamingException ne) {
            System.out.println("In authenticateWithLDAP, LDAP Authentication NamingException : " +
                               ne.getMessage());
        } catch (Exception ex) {
            System.out.println("In authenticateWithLDAP, LDAP Authentication Exception : " +
                               ex.getMessage());
        }

        return retval;
        //        return retval;
    }

    private NamingEnumeration getLDAPAttrs(String userid, String pass,
                                           String searchFilter,
                                           String domain) throws NamingException,
                                                                 Exception {
        String host = getServerName();
        String port = getIP_Port();
        String dcPart1 = getDcPart1();
        String dcPart2 = getDcPart2();
//        String attrUserID = getLDAP_UID_ATTR();
//        String attrUserName = getLDAP_UNAME_ATTR();

        // set attribute names to obtain value of
        String[] returnedAtts = { "sAMAccountName", "cn","mail" };
        SearchControls searchCtls = new SearchControls();
        searchCtls.setReturningAttributes(returnedAtts);

        // specify the search scope
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        // set search base
        String searchBase = "DC=" + dcPart1 + ",DC=" + dcPart2;

        // set ldap env values
        Hashtable environment = new Hashtable();
        environment.put(Context.INITIAL_CONTEXT_FACTORY,
                        "com.sun.jndi.ldap.LdapCtxFactory");
        environment.put(Context.PROVIDER_URL, "ldap://" + host + ":" + port);
        environment.put(Context.SECURITY_AUTHENTICATION, "simple");
        environment.put(Context.SECURITY_PRINCIPAL, userid + "@" + domain);
        environment.put(Context.SECURITY_CREDENTIALS, pass);

        // set ldap context
        DirContext ctxGC = new InitialDirContext(environment);

        // perform search to obtain values
        NamingEnumeration answer =
            ctxGC.search(searchBase, searchFilter, searchCtls);
        return answer;
    }