Repository: incubator-ranger
Updated Branches:
  refs/heads/master 937e0baba -> b063a9989


RANGER-842: This patch adds PAM auth support to ranger-admin, including the 
remote authentication.

To activate:

* set ranger.authentication.method to PAM .
* create /etc/pam.d/ranger-remote (not configurable)
* create /etc/pam.d/ranger-admin (configurable)
* set ranger.pam.service property to "ranger-admin" (standard) or the name you 
configured above

Signed-off-by: sneethiraj <sneet...@apache.org>
Signed-off-by: rmani <rm...@hortonworks.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/42d8db56
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/42d8db56
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/42d8db56

Branch: refs/heads/master
Commit: 42d8db56d51015c65284014e3c08065de4997046
Parents: 937e0ba
Author: Bolke de Bruin <bo...@xs4all.nl>
Authored: Sun Feb 14 00:39:07 2016 +0100
Committer: rmani <rm...@hortonworks.com>
Committed: Mon Jul 11 15:09:02 2016 -0700

----------------------------------------------------------------------
 .../handler/RangerAuthenticationProvider.java   |  61 +++++
 unixauthclient/pom.xml                          |   6 +
 .../unix/jaas/PamLoginModule.java               | 222 +++++++++++++++++++
 .../authentication/unix/jaas/PamPrincipal.java  |  86 +++++++
 .../jaas/UsernamePasswordCallbackHandler.java   |  38 ++++
 unixauthnative/pom.xml                          |   2 +-
 unixauthnative/src/main/c/credValidator.c       |  89 ++++++--
 7 files changed, 479 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
 
b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
index 8cd4bac..9525612 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
@@ -126,6 +126,12 @@ public class RangerAuthenticationProvider implements 
AuthenticationProvider {
                                        return authentication;
                                }
                        }
+                       if (rangerAuthenticationMethod.equalsIgnoreCase("PAM")) 
{
+                               authentication = 
getPamAuthentication(authentication);
+                               if(authentication!=null && 
authentication.isAuthenticated()) {
+                                       return authentication;
+                               }
+                       }
                        String encoder="SHA256";
                        try{
                                
authentication=getJDBCAuthentication(authentication,encoder);
@@ -296,6 +302,61 @@ public class RangerAuthenticationProvider implements 
AuthenticationProvider {
                return authentication;
        }
 
+       public Authentication getPamAuthentication(Authentication 
authentication) {
+               try {
+                       String rangerLdapDefaultRole = 
PropertiesUtil.getProperty(
+                                       "ranger.ldap.default.role", 
"ROLE_USER");
+                       DefaultJaasAuthenticationProvider 
jaasAuthenticationProvider = new DefaultJaasAuthenticationProvider();
+                       String loginModuleName = 
"org.apache.ranger.authentication.unix.jaas.PamLoginModule";
+                       LoginModuleControlFlag controlFlag = 
LoginModuleControlFlag.REQUIRED;
+                       Map<String, String> options = 
PropertiesUtil.getPropertiesMap();
+
+                       if (!options.containsKey("ranger.pam.service"))
+                               options.put("ranger.pam.service", 
"ranger-admin");
+
+                       AppConfigurationEntry appConfigurationEntry = new 
AppConfigurationEntry(
+                                       loginModuleName, controlFlag, options);
+                       AppConfigurationEntry[] appConfigurationEntries = new 
AppConfigurationEntry[] { appConfigurationEntry };
+                       Map<String, AppConfigurationEntry[]> 
appConfigurationEntriesOptions = new HashMap<String, AppConfigurationEntry[]>();
+                       appConfigurationEntriesOptions.put("SPRINGSECURITY",
+                                       appConfigurationEntries);
+                       Configuration configuration = new InMemoryConfiguration(
+                                       appConfigurationEntriesOptions);
+                       
jaasAuthenticationProvider.setConfiguration(configuration);
+                       RoleUserAuthorityGranter authorityGranter = new 
RoleUserAuthorityGranter();
+                       RoleUserAuthorityGranter[] authorityGranters = new 
RoleUserAuthorityGranter[] { authorityGranter };
+                       
jaasAuthenticationProvider.setAuthorityGranters(authorityGranters);
+                       jaasAuthenticationProvider.afterPropertiesSet();
+                       String userName = authentication.getName();
+                       String userPassword = "";
+                       if (authentication.getCredentials() != null) {
+                               userPassword = 
authentication.getCredentials().toString();
+                       }
+
+                       // getting user authenticated
+                       if (userName != null && userPassword != null
+                                       && !userName.trim().isEmpty()
+                                       && !userPassword.trim().isEmpty()) {
+                               final List<GrantedAuthority> grantedAuths = new 
ArrayList<>();
+                               grantedAuths.add(new SimpleGrantedAuthority(
+                                               rangerLdapDefaultRole));
+                               final UserDetails principal = new 
User(userName, userPassword,
+                                               grantedAuths);
+                               final Authentication finalAuthentication = new 
UsernamePasswordAuthenticationToken(
+                                               principal, userPassword, 
grantedAuths);
+                               authentication = jaasAuthenticationProvider
+                                               
.authenticate(finalAuthentication);
+                               
authentication=getAuthenticationWithGrantedAuthority(authentication);
+                               return authentication;
+                       } else {
+                               return authentication;
+                       }
+               } catch (Exception e) {
+                       logger.debug("Pam Authentication Failed:", e);
+               }
+               return authentication;
+       }
+
        public Authentication getUnixAuthentication(Authentication 
authentication) {
 
                try {

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/unixauthclient/pom.xml
----------------------------------------------------------------------
diff --git a/unixauthclient/pom.xml b/unixauthclient/pom.xml
index a4513e6..b748dcd 100644
--- a/unixauthclient/pom.xml
+++ b/unixauthclient/pom.xml
@@ -82,5 +82,11 @@
             <artifactId>slf4j-api</artifactId>
             <version>${slf4j-api.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.kohsuke</groupId>
+            <artifactId>libpam4j</artifactId>
+            <version>${libpam4j.version}</version>
+        </dependency>
+
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
----------------------------------------------------------------------
diff --git 
a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
 
b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
new file mode 100644
index 0000000..d1107ef
--- /dev/null
+++ 
b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.authentication.unix.jaas;
+
+import org.jvnet.libpam.PAM;
+import org.jvnet.libpam.PAMException;
+import org.jvnet.libpam.UnixUser;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.*;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class PamLoginModule extends Object implements LoginModule
+{
+    public static final String SERVICE_KEY = "ranger.pam.service";
+
+    private PAM _pam;
+    private Subject _subject;
+    private CallbackHandler _callbackHandler;
+    private Map<String, ?> _options;
+
+    private String _username;
+    private String _password;
+
+    private boolean _authSucceeded;
+    private PamPrincipal _principal;
+
+    public PamLoginModule()
+    {
+        super();
+        _authSucceeded = false;
+    }
+
+    @Override
+    public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> sharedState, Map<String, ?> options)
+    {
+        _subject = subject;
+        _callbackHandler = callbackHandler;
+        _options = new HashMap<>(options);
+    }
+
+    @Override
+    public boolean login() throws LoginException
+    {
+        initializePam();
+        obtainUserAndPassword();
+        return performLogin();
+    }
+
+    private void initializePam() throws LoginException
+    {
+        String service = (String)_options.get(SERVICE_KEY);
+        if (service == null)
+        {
+            throw new LoginException("Error: PAM service was not defined");
+        }
+        createPam(service);
+    }
+
+    private void createPam(String service) throws LoginException
+    {
+        try
+        {
+            _pam = new PAM(service);
+        }
+        catch (PAMException ex)
+        {
+            LoginException le = new LoginException("Error initializing PAM");
+            le.initCause(ex);
+            throw le;
+        }
+    }
+
+    private void obtainUserAndPassword() throws LoginException
+    {
+        if (_callbackHandler == null)
+        {
+            throw new LoginException("Error: no CallbackHandler available  to 
gather authentication information from the user");
+        }
+
+        try
+        {
+            NameCallback nameCallback = new NameCallback("username");
+            PasswordCallback passwordCallback = new 
PasswordCallback("password", false);
+
+            invokeCallbackHandler(nameCallback, passwordCallback);
+
+            initUserName(nameCallback);
+            initPassword(passwordCallback);
+        }
+        catch (IOException | UnsupportedCallbackException ex)
+        {
+            LoginException le = new LoginException("Error in callbacks");
+            le.initCause(ex);
+            throw le;
+        }
+    }
+
+    private void invokeCallbackHandler(NameCallback nameCallback, 
PasswordCallback passwordCallback) throws IOException, 
UnsupportedCallbackException
+    {
+        Callback[] callbacks = new Callback[2];
+        callbacks[0] = nameCallback;
+        callbacks[1] = passwordCallback;
+
+        _callbackHandler.handle(callbacks);
+    }
+
+    private void initUserName(NameCallback nameCallback)
+    {
+        _username = nameCallback.getName();
+    }
+
+    private void initPassword(PasswordCallback passwordCallback)
+    {
+        char[] password = passwordCallback.getPassword();
+        _password = new String(password);
+
+        passwordCallback.clearPassword();
+    }
+
+    private boolean performLogin() throws LoginException
+    {
+        try
+        {
+            UnixUser user = _pam.authenticate(_username, _password);
+            _principal = new PamPrincipal(user);
+            _authSucceeded = true;
+
+            return true;
+        }
+        catch (PAMException ex)
+        {
+            LoginException le = new FailedLoginException("Invalid username or 
password");
+            le.initCause(ex);
+            throw le;
+        }
+    }
+
+    @Override
+    public boolean commit() throws LoginException
+    {
+        if (_authSucceeded == false)
+        {
+            return false;
+        }
+
+        if (_subject.isReadOnly())
+        {
+            cleanup();
+            throw new LoginException("Subject is read-only");
+        }
+
+        Set<Principal> principals = _subject.getPrincipals();
+        if (principals.contains(_principal) == false)
+        {
+            principals.add(_principal);
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean abort() throws LoginException
+    {
+        if (_authSucceeded == false)
+        {
+            return false;
+        }
+
+        cleanup();
+        return true;
+    }
+
+    @Override
+    public boolean logout() throws LoginException
+    {
+        if (_subject.isReadOnly())
+        {
+            cleanup();
+            throw new LoginException("Subject is read-only");
+        }
+
+        _subject.getPrincipals().remove(_principal);
+
+        cleanup();
+        return true;
+    }
+
+    private void cleanup()
+    {
+        _authSucceeded = false;
+        _username = null;
+        _password = null;
+        _principal = null;
+        _pam.dispose();
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
----------------------------------------------------------------------
diff --git 
a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
 
b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
new file mode 100644
index 0000000..8379f3f
--- /dev/null
+++ 
b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.authentication.unix.jaas;
+
+import org.jvnet.libpam.UnixUser;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
+
+
+public class PamPrincipal extends Object implements Principal {
+    private String _userName;
+    private String _gecos;
+    private String _homeDir;
+    private String _shell;
+    private int _uid;
+    private int _gid;
+    private Set<String> _groups;
+
+    public PamPrincipal(UnixUser user)
+    {
+        super();
+        _userName = user.getUserName();
+        _gecos = user.getGecos();
+        _homeDir = user.getDir();
+        _shell = user.getShell();
+        _uid = user.getUID();
+        _gid = user.getGID();
+        _groups = Collections.unmodifiableSet(user.getGroups());
+    }
+
+    @Override
+    public String getName()
+    {
+        return _userName;
+    }
+
+    public String getGecos()
+    {
+        return _gecos;
+    }
+
+    public String getHomeDir()
+    {
+        return _homeDir;
+    }
+
+    public String getShell()
+    {
+        return _shell;
+    }
+
+    public int getUid()
+    {
+        return _uid;
+    }
+
+    public int getGid()
+    {
+        return _gid;
+    }
+
+    public Set<String> getGroups()
+    {
+        return _groups;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
----------------------------------------------------------------------
diff --git 
a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
 
b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
new file mode 100644
index 0000000..a73f653
--- /dev/null
+++ 
b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
@@ -0,0 +1,38 @@
+package org.apache.ranger.authentication.unix.jaas;
+
+import javax.security.auth.callback.*;
+import java.io.IOException;
+
+public class UsernamePasswordCallbackHandler extends Object implements 
CallbackHandler {
+    private String _user;
+    private String _password;
+
+    public UsernamePasswordCallbackHandler(String user, String password) {
+        super();
+        _user = user;
+        _password = password;
+    }
+
+    @Override
+    public void handle(Callback[] callbacks) throws IOException, 
UnsupportedCallbackException {
+        for (Callback callback : callbacks) {
+            if (callback instanceof NameCallback) {
+                handleName((NameCallback) callback);
+            } else if (callback instanceof PasswordCallback) {
+                handlePassword((PasswordCallback) callback);
+            } else {
+                throw new UnsupportedCallbackException(callback);
+            }
+        }
+    }
+
+    private void handleName(NameCallback callback) {
+        callback.setName(_user);
+    }
+
+    private void handlePassword(PasswordCallback callback) {
+        char[] passwordChars = _password.toCharArray();
+        callback.setPassword(passwordChars);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/unixauthnative/pom.xml
----------------------------------------------------------------------
diff --git a/unixauthnative/pom.xml b/unixauthnative/pom.xml
index 62aa4e2..70d1469 100644
--- a/unixauthnative/pom.xml
+++ b/unixauthnative/pom.xml
@@ -46,7 +46,7 @@
                         </source>
                     </sources>
                     <linkerEndOptions>
-                        <linkerEndOption>-lcrypt</linkerEndOption>
+                        <linkerEndOption>-lpam</linkerEndOption>
                     </linkerEndOptions>
                 </configuration>
             </plugin>

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/42d8db56/unixauthnative/src/main/c/credValidator.c
----------------------------------------------------------------------
diff --git a/unixauthnative/src/main/c/credValidator.c 
b/unixauthnative/src/main/c/credValidator.c
index d706a93..ab19080 100644
--- a/unixauthnative/src/main/c/credValidator.c
+++ b/unixauthnative/src/main/c/credValidator.c
@@ -14,50 +14,91 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+ /*
+  You need to add the following (or equivalent) to the
+  /etc/pam.d/ranger-remote file:
+  # check authorization
+  auth       required     pam_unix.so
+  account    required     pam_unix.so
+ */
+
 #include <stdio.h>
+#include <stdarg.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <pwd.h>
-#include <shadow.h>
 #include <string.h>
 #include <sys/types.h>
-#include <crypt.h>
+#include <security/pam_appl.h>
+
+int pamconv(int num_msg, const struct pam_message **msg, struct pam_response 
**resp, void *appdata_ptr) {
+  if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
+               fprintf(stderr, "ERROR: Unexpected PAM conversation '%d/%s'\n", 
msg[0]->msg_style, msg[0]->msg);
+               return PAM_CONV_ERR;
+  }
+  if (!appdata_ptr) {
+               fprintf(stderr, "ERROR: No password available to 
conversation!\n");
+               return PAM_CONV_ERR;
+  }
+       *resp = calloc(num_msg, sizeof(struct pam_response));
+       if (!*resp) {
+               fprintf(stderr, "ERROR: Out of memory!\n");
+               return PAM_CONV_ERR;
+  }
+  (*resp)[0].resp = strdup((char *) appdata_ptr);
+  (*resp)[0].resp_retcode = 0;
+
+       return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
+}
+
+struct pam_conv conv = { pamconv, NULL };
 
 int main(int ac, char **av, char **ev)
 {
        char username[64] ;
        char password[64] ;
        char line[512] ;
-       struct passwd *pwp;
-       struct spwd *spwd ; 
 
-       fgets(line,512,stdin) ;
+       int retval;
+       pam_handle_t *pamh = NULL;
 
+       fgets(line,512,stdin) ;
        sscanf(line, "LOGIN:%s %s",username,password) ;
+       conv.appdata_ptr = (char *) password;
 
-       pwp = getpwnam(username) ;
-
-       if (pwp == (struct passwd *)NULL) {
+       retval = pam_start("ranger-remote", username, &conv, &pamh);
+       if (retval != PAM_SUCCESS) {
+               /* why expose this? */
                fprintf(stdout, "FAILED: [%s] does not exists.\n", username) ;
-               exit(1) ;
+               exit(1);
+       }
+
+       retval = pam_authenticate(pamh, 0);
+       if (retval != PAM_SUCCESS) {
+               fprintf(stdout, "FAILED: Password did not match.\n") ;
+               exit(1);
+       }
+
+       /* authorize */
+       retval = pam_acct_mgmt(pamh, 0);
+       if (retval != PAM_SUCCESS) {
+               fprintf(stdout, "FAILED: [%s] is not authorized.\n", username) ;
+               exit(1);
        }
-       
-       spwd = getspnam(pwp->pw_name) ;
 
-       if (spwd == (struct spwd *)NULL) {
-               fprintf(stdout, "FAILED: unable to get (shadow) password for 
%s\n", username) ;
-               exit(1) ;
+       /* establish the requested credentials */
+       if ((retval = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+                       fprintf(stdout, "FAILED: Error setting credentials for 
[%s].\n", username) ;
+               exit(1);
        }
-       else {
-               char *gen = crypt(password,spwd->sp_pwdp) ;
-               if (strcmp(spwd->sp_pwdp,gen) == 0) {
-                       fprintf(stdout, "OK:\n") ;
-                       exit(0);
-               }
-               else {
-                       fprintf(stdout, "FAILED: Password did not match.\n") ;
-                       exit(1) ;
-               }
+
+       /* not opening a session, as logout has not been implemented as a 
remote service */
+       fprintf(stdout, "OK:\n") ;
+
+       if (pamh) {
+               pam_end(pamh, retval);
        }
+
        exit(0) ;
 }

Reply via email to