/* * 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-256"; private static final String SUBJECT_USER_ROLES = "subject.userRoles"; private static final String SUBJECT_USER_GROUPS = "subject.userGroups"; 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); /* Coverity Scan CID 1361682 */ String userName = null; if (user != null) { userName = user.getName(); } GatewayLog.lookedUpUserRoles(roles, userName); 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) { handleAuthFailure(token, e.getMessage(), e); } HashRequest.Builder builder = new HashRequest.Builder(); Hash credentialsHash = hashService .computeHash(builder.setSource(token.getCredentials()).setAlgorithmName(HASHING_ALGORITHM).build()); /* Coverity Scan CID 1361684 */ if (credentialsHash == null) { handleAuthFailure(token, "Failed to compute hash", null); } return new SimpleAuthenticationInfo(new UnixUserPrincipal(user), credentialsHash.toHex(), credentialsHash.getSalt(), getName()); } private void handleAuthFailure(AuthenticationToken token, String errorMessage, Exception e) { auditor.audit(Action.AUTHENTICATION, token.getPrincipal().toString(), ResourceType.PRINCIPAL, ActionOutcome.FAILURE, errorMessage); ShiroLog.failedLoginInfo(token); if (e != null) { ShiroLog.failedLoginAttempt(e.getCause()); throw new AuthenticationException(e); } throw new AuthenticationException(errorMessage); } }