You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2016/08/17 13:39:03 UTC
knox git commit: KNOX-537 Linux PAM Authentication Provider
(jeffreyr97/hkropp via lmccay)
Repository: knox
Updated Branches:
refs/heads/master d64d848dc -> b37e4293d
KNOX-537 Linux PAM Authentication Provider (jeffreyr97/hkropp via lmccay)
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/b37e4293
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/b37e4293
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/b37e4293
Branch: refs/heads/master
Commit: b37e4293d2abfb2db0b2a63a571ffe7d54866242
Parents: d64d848
Author: Larry McCay <lm...@hortonworks.com>
Authored: Wed Aug 17 09:38:45 2016 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Wed Aug 17 09:38:45 2016 -0400
----------------------------------------------------------------------
gateway-provider-security-shiro/pom.xml | 6 +
.../filter/ShiroSubjectIdentityAdapter.java | 21 ++-
.../hadoop/gateway/shirorealm/KnoxPamRealm.java | 144 +++++++++++++++++++
.../gateway/shirorealm/UnixUserPrincipal.java | 46 ++++++
.../gateway/shirorealm/KnoxPamRealmTest.java | 70 +++++++++
pom.xml | 12 ++
6 files changed, 294 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/b37e4293/gateway-provider-security-shiro/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/pom.xml b/gateway-provider-security-shiro/pom.xml
index 40614b9..1ed7302 100644
--- a/gateway-provider-security-shiro/pom.xml
+++ b/gateway-provider-security-shiro/pom.xml
@@ -65,6 +65,12 @@
</dependency>
<dependency>
+ <groupId>org.kohsuke</groupId>
+ <artifactId>libpam4j</artifactId>
+ <version>1.8</version>
+ </dependency>
+
+ <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/knox/blob/b37e4293/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
index 11a0780..692cf8d 100644
--- a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
+++ b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
@@ -68,7 +68,7 @@ public class ShiroSubjectIdentityAdapter implements Filter {
// we use shiro authorization realm to look up groups
subject.hasRole("authenticatedUser");
- final String principalName = (String) subject.getPrincipal();
+ final String principalName = (String) subject.getPrincipal().toString();
CallableChain callableChain = new CallableChain(request, response, chain);
SecurityUtils.getSubject().execute(callableChain);
@@ -95,7 +95,7 @@ public class ShiroSubjectIdentityAdapter implements Filter {
}
};
Subject shiroSubject = SecurityUtils.getSubject();
- final String principal = (String) shiroSubject.getPrincipal();
+ final String principal = (String) shiroSubject.getPrincipal().toString();
HashSet emptySet = new HashSet();
Set<Principal> principals = new HashSet<Principal>();
Principal p = new PrimaryPrincipal(principal);
@@ -108,9 +108,20 @@ public class ShiroSubjectIdentityAdapter implements Filter {
// map ldap groups saved in session to Java Subject GroupPrincipal(s)
if (SecurityUtils.getSubject().getSession().getAttribute(SUBJECT_USER_GROUPS) != null) {
userGroups = (Set<String>)SecurityUtils.getSubject().getSession().getAttribute(SUBJECT_USER_GROUPS);
- } else {
- userGroups = new HashSet<String>(shiroSubject.getPrincipals().asSet());
- userGroups.remove(principal);
+ } else { // KnoxLdapRealm case
+ if( shiroSubject.getPrincipal() instanceof String ) {
+ userGroups = new HashSet<String>(shiroSubject.getPrincipals().asSet());
+ userGroups.remove(principal);
+ } else { // KnoxPamRealm case
+ Set<Principal> shiroPrincipals = new HashSet<Principal>(shiroSubject.getPrincipals().asSet());
+ userGroups = new HashSet<String>(); // Here we are creating a new UserGroup
+ // so we don't need to remove a Principal
+ // In the case of LDAP the userGroup may have already Principal
+ // added to the list of groups, so it is not needed.
+ for( Principal shiroPrincipal: shiroPrincipals ) {
+ userGroups.add(shiroPrincipal.toString() );
+ }
+ }
}
for (String userGroup : userGroups) {
Principal gp = new GroupPrincipal(userGroup);
http://git-wip-us.apache.org/repos/asf/knox/blob/b37e4293/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealm.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealm.java b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealm.java
new file mode 100644
index 0000000..84121a7
--- /dev/null
+++ b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealm.java
@@ -0,0 +1,144 @@
+/*
+ * 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.hadoop.gateway.shirorealm;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.apache.hadoop.gateway.GatewayMessages;
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.api.AuditService;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
+import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.shirorealm.impl.i18n.KnoxShiroMessages;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.crypto.hash.*;
+import org.jvnet.libpam.PAM;
+import org.jvnet.libpam.PAMException;
+import org.jvnet.libpam.UnixUser;
+
+/**
+ * A Unix-style
+ * <a href="http://www.kernel.org/pub/linux/libs/pam/index.html">PAM</a>
+ * {@link org.apache.shiro.realm.Realm Realm} that uses
+ * <a href="https://github.com/kohsuke/libpam4j">libpam4j</a> to interface with
+ * the PAM system libraries.
+ * <p>
+ * This is a single Shiro {@code Realm} that interfaces with the OS's
+ * {@code PAM} subsystem which itself can be connected to several authentication
+ * methods (unix-crypt,Samba, LDAP, etc.)
+ * <p>
+ * This {@code Realm} can also take part in Shiro's Pluggable Realms concept.
+ * <p>
+ * Using a {@code KnoxPamRealm} requires a PAM {@code service} name. This is the
+ * name of the file under {@code /etc/pam.d} that is used to initialise and
+ * configure the PAM subsytem. Normally, this file reflects the application
+ * using it. For example {@code gdm}, {@code su}, etc. There is no default value
+ * for this propery.
+ * <p>
+ * For example, defining this realm in Shiro .ini:
+ *
+ * <pre>
+ * [main]
+ * pamRealm = org.apache.shiro.realm.libpam4j.KnoxPamRealm
+ * pamRealm.service = [ knox-pam-ldap-service | knox-pam-os-service | knox-pam-winbind-service ]
+ * [urls]
+ * **=authcBasic
+ * </pre>
+ *
+ */
+
+public class KnoxPamRealm extends AuthorizingRealm {
+ private static final String HASHING_ALGORITHM = "SHA-1";
+ private final static String SUBJECT_USER_ROLES = "subject.userRoles";
+ private final static String SUBJECT_USER_GROUPS = "subject.userGroups";
+ private static GatewayMessages LOG = MessagesFactory.get(GatewayMessages.class);
+ private HashService hashService = new DefaultHashService();
+ KnoxShiroMessages ShiroLog = MessagesFactory.get(KnoxShiroMessages.class);
+ GatewayMessages GatewayLog = MessagesFactory.get(GatewayMessages.class);
+ private static AuditService auditService = AuditServiceFactory.getAuditService();
+ private static Auditor auditor = auditService.getAuditor(AuditConstants.DEFAULT_AUDITOR_NAME,
+ AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME);
+
+ private String service;
+
+ public KnoxPamRealm() {
+ HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(HASHING_ALGORITHM);
+ setCredentialsMatcher(credentialsMatcher);
+ }
+
+ public void setService(String service) {
+ this.service = service;
+ }
+
+ public String getService() {
+ return this.service;
+ }
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ Set<String> roles = new LinkedHashSet<String>();
+
+ UnixUserPrincipal user = principals.oneByType(UnixUserPrincipal.class);
+ if (user != null) {
+ roles.addAll(user.getUnixUser().getGroups());
+ }
+ SecurityUtils.getSubject().getSession().setAttribute(SUBJECT_USER_ROLES, roles);
+ SecurityUtils.getSubject().getSession().setAttribute(SUBJECT_USER_GROUPS, roles);
+ GatewayLog.lookedUpUserRoles(roles, user.getName());
+ return new SimpleAuthorizationInfo(roles);
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+ UsernamePasswordToken upToken = (UsernamePasswordToken) token;
+ UnixUser user=null;
+ try {
+ user = (new PAM(this.getService())).authenticate(upToken.getUsername(),
+ new String(upToken.getPassword()));
+ } catch (PAMException e) {
+ auditor.audit(Action.AUTHENTICATION, token.getPrincipal().toString(), ResourceType.PRINCIPAL,
+ ActionOutcome.FAILURE, e.getMessage());
+ ShiroLog.failedLoginInfo(token);
+ ShiroLog.failedLoginAttempt(e.getCause());
+ throw new AuthenticationException(e);
+ }
+ HashRequest.Builder builder = new HashRequest.Builder();
+ Hash credentialsHash = hashService
+ .computeHash(builder.setSource(token.getCredentials()).setAlgorithmName(HASHING_ALGORITHM).build());
+ return new SimpleAuthenticationInfo(new UnixUserPrincipal(user) , credentialsHash.toHex(), credentialsHash.getSalt(),
+ getName());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/b37e4293/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/UnixUserPrincipal.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/UnixUserPrincipal.java b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/UnixUserPrincipal.java
new file mode 100644
index 0000000..a659888
--- /dev/null
+++ b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/UnixUserPrincipal.java
@@ -0,0 +1,46 @@
+/*
+ * 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.hadoop.gateway.shirorealm;
+
+import java.security.Principal;
+import org.jvnet.libpam.UnixUser;
+
+public class UnixUserPrincipal implements Principal {
+ private final UnixUser userName;
+
+ public UnixUserPrincipal(UnixUser userName) {
+ this.userName = userName;
+ }
+
+ @Override
+ public String getName() {
+ return userName.getUserName();
+ }
+
+ public UnixUser getUnixUser() {
+ return userName;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(userName.getUserName());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/b37e4293/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealmTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealmTest.java b/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealmTest.java
new file mode 100644
index 0000000..1ada3c6
--- /dev/null
+++ b/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxPamRealmTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.hadoop.gateway.shirorealm;
+
+import java.util.Scanner;
+
+import org.junit.Test;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.assumeTrue;
+import static org.easymock.EasyMock.*;
+
+public class KnoxPamRealmTest {
+ @Test
+ public void setService() {
+ KnoxPamRealm realm = new KnoxPamRealm();
+ realm.setService("knox-pam-os-service");
+ //assertEquals(realm.getService(), "knox-pam-os-service");
+ }
+
+ @Test
+ public void testDoGetAuthenticationInfo() {
+ KnoxPamRealm realm = new KnoxPamRealm();
+ realm.setService("sshd"); // pam settings being used: /etc/pam.d/sshd
+
+ // use environment variables and skip the test if not set.
+ String pamuser = System.getenv("PAMUSER");
+ String pampass = System.getenv("PAMPASS");
+ assumeTrue(pamuser != null);
+ assumeTrue(pampass != null);
+
+ // mock shiro auth token
+ UsernamePasswordToken authToken = createMock(UsernamePasswordToken.class);
+ expect(authToken.getUsername()).andReturn(pamuser);
+ expect(authToken.getPassword()).andReturn(pampass.toCharArray());
+ expect(authToken.getCredentials()).andReturn(pampass);
+ replay(authToken);
+
+ // login
+ AuthenticationInfo authInfo = realm.doGetAuthenticationInfo(authToken);
+
+ // verify success
+ assertTrue(authInfo.getCredentials() != null);
+ }
+
+ public static void main(String[] args) throws Exception {
+ KnoxPamRealmTest pamTest = new KnoxPamRealmTest();
+ pamTest.testDoGetAuthenticationInfo();
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/b37e4293/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2310417..8894a2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1053,6 +1053,18 @@
</dependency>
<dependency>
+ <groupId>org.kohsuke</groupId>
+ <artifactId>libpam4j</artifactId>
+ <version>1.8</version>
+ </dependency>
+
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>4.1.0</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>