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) ; }