/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb;
import static org.voltdb.common.Constants.AUTH_HANDSHAKE;
import static org.voltdb.common.Constants.AUTH_HANDSHAKE_VERSION;
import static org.voltdb.common.Constants.AUTH_SERVICE_NAME;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.security.auth.Subject;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.CredentialExpiredException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.MessageProp;
import org.mindrot.BCrypt;
import org.voltcore.logging.Level;
import org.voltcore.logging.VoltLogger;
import org.voltcore.utils.RateLimitedLogger;
import org.voltdb.catalog.Connector;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Procedure;
import org.voltdb.client.ClientAuthScheme;
import org.voltdb.client.DelegatePrincipal;
import org.voltdb.common.Permission;
import org.voltdb.security.AuthenticationRequest;
import org.voltdb.utils.Encoder;
import org.voltdb.utils.LogKeys;
import com.google_voltpatches.common.base.Throwables;
import com.google_voltpatches.common.collect.ImmutableList;
import com.google_voltpatches.common.collect.ImmutableMap;
import com.google_voltpatches.common.collect.ImmutableSet;
/**
* The AuthSystem parses authentication and permission information from the catalog and uses it to generate a representation
* of the permissions assigned to users and groups.
*
*/
public class AuthSystem {
private static final VoltLogger authLogger = new VoltLogger("AUTH");
/**
* JASS Login configuration entry designator
*/
public static final String VOLTDB_SERVICE_LOGIN_MODULE =
System.getProperty("VOLTDB_SERVICE_LOGIN_MODULE", "VoltDBService");
/**
* Authentication provider enumeration. It serves also as mapping mechanism
* for providers, which are configured in the deployment file, and the login
* packet service field.
*/
public enum AuthProvider {
HASH("hash","database"),
KERBEROS("kerberos","kerberos");
private final static Map<String,AuthProvider> providerMap;
private final static Map<String,AuthProvider> serviceMap;
static {
ImmutableMap.Builder<String, AuthProvider> pbldr = ImmutableMap.builder();
ImmutableMap.Builder<String, AuthProvider> sbldr = ImmutableMap.builder();
for (AuthProvider ap: values()) {
pbldr.put(ap.provider,ap);
sbldr.put(ap.service,ap);
}
providerMap = pbldr.build();
serviceMap = sbldr.build();
}
final String provider;
final String service;
AuthProvider(String provider, String service) {
this.provider = provider;
this.service = service;
}
/**
* @return its security provider equivalent
*/
public String provider() {
return provider;
}
/**
* @return its login packet service equivalent
*/
public String service() {
return service;
}
public static AuthProvider fromProvider(String provider) {
AuthProvider ap = providerMap.get(provider);
if (ap == null) {
throw new IllegalArgumentException("No provider mapping for " + provider);
}
return ap;
}
public static AuthProvider fromService(String service) {
AuthProvider ap = serviceMap.get(service);
if (ap == null) {
throw new IllegalArgumentException("No service mapping for " + service);
}
return ap;
}
}
/**
* Representation of a permission group.
*
*/
class AuthGroup {
/**
* Name of the group
*/
private final String m_name;
/**
* Set of users that are a member of this group
*/
private Set<AuthUser> m_users = new HashSet<AuthUser>();
private final EnumSet<Permission> m_permissions = EnumSet.noneOf(Permission.class);
/**
*
* @param name Name of the group
* @param sysproc Whether membership in this group grants permission to invoke system procedures
* @param defaultproc Whether membership in this group grants permission to invoke default procedures
* @param defaultprocread Whether membership in this group grants permission to invoke only read default procedures
* @param adhoc Whether membership in this group grants permission to invoke adhoc queries
*/
private AuthGroup(String name, EnumSet<Permission> permissions) {
m_name = name.intern();
m_permissions.addAll(permissions);
}
private void finish() {
m_users = ImmutableSet.copyOf(m_users);
}
}
/**
* Representation of the permissions associated with a specific user along with a SHA-1 double hashed copy of the users
* clear text password.
*
*/
public static class AuthUser {
/**
* SHA-1 double hashed copy of the users clear text password
*/
private final byte[] m_sha1ShadowPassword;
/**
* SHA-2 double hashed copy of the users clear text password
*/
private final byte[] m_sha2ShadowPassword;
/**
* SHA-1 hashed and then bcrypted copy of the users clear text password
*/
private final String m_bcryptShadowPassword;
/**
* SHA-2 hashed and then bcrypted copy of the users clear text password
*/
private final String m_bcryptSha2ShadowPassword;
/**
* Name of the user
*/
public final String m_name;
/**
* Fast iterable list of groups this user is a member of.
*/
private List<AuthGroup> m_groups = new ArrayList<AuthGroup>();
private EnumSet<Permission> m_permissions = EnumSet.noneOf(Permission.class);
private String[] m_permissions_list;
/**
* Fast membership check set of stored procedures this user has permission to invoke.
* This is generated when the catalog is parsed and it includes procedures the user has permission
* to invoke by virtue of group membership. The catalog entry for the stored procedure is used here.
*/
private Set<Procedure> m_authorizedProcedures = new HashSet<Procedure>();
/**
* Set of export connectors this user is authorized to access.
*/
private Set<Connector> m_authorizedConnectors = new HashSet<Connector>();
/**
* The constructor accepts the password as either sha1 or bcrypt. In practice
* there will be only one passed in depending on the format of the password in the catalog.
* The other will be null and that is used to determine how to hash the supplied password
* for auth
* @param shadowPassword SHA-1 double hashed copy of the users clear text password
* @param name Name of the user
*/
private AuthUser(byte[] sha1ShadowPassword, byte[] sha2ShadowPassword, String bcryptShadowPassword, String bCryptSha2ShadowPassword, String name) {
m_sha1ShadowPassword = sha1ShadowPassword;
m_sha2ShadowPassword = sha2ShadowPassword;
m_bcryptShadowPassword = bcryptShadowPassword;
m_bcryptSha2ShadowPassword = bCryptSha2ShadowPassword;
if (name != null) {
m_name = name.intern();
} else {
m_name = null;
}
}
/**
* Check if a user has permission to invoke the specified stored procedure
* Handle both user-written procedures and default auto-generated ones.
* @param proc Catalog entry for the stored procedure to check
* @return true if the user has permission and false otherwise
*/
public boolean hasUserDefinedProcedurePermission(Procedure proc) {
if (proc == null) {
return false;
}
return hasPermission(Permission.ALLPROC) || m_authorizedProcedures.contains(proc);
}
/**
* Check if a user has any one of given permission.
* @return true if the user has permission and false otherwise
*/
public boolean hasPermission(Permission... perms) {
for (int i = 0; i < perms.length;i++) {
if (m_permissions.contains(perms[i])) {
return true;
}
}
return false;
}
/**
* Get group names.
* @return group name array
*/
public final String[] getGroupNames() {
String[] groupNames = new String[m_groups.size()];
for (int i = 0; i < m_groups.size(); ++i) {
groupNames[i] = m_groups.get(i).m_name;
}
return groupNames;
}
public boolean authorizeConnector(String connectorClass) {
if (connectorClass == null) {
return false;
}
for (Connector c : m_authorizedConnectors) {
if (c.getLoaderclass().equals(connectorClass)) {
return true;
}
}
return false;
}
public boolean isAuthEnabled() {
return true;
}
private void finish() {
m_groups = ImmutableList.copyOf(m_groups);
m_authorizedProcedures = ImmutableSet.copyOf(m_authorizedProcedures);
m_authorizedConnectors = ImmutableSet.copyOf(m_authorizedConnectors);
}
}
/**
* Storage for user permissions keyed on the username
*/
private Map<String, AuthUser> m_users = new HashMap<String, AuthUser>();
/**
* Storage for group permissions keyed on group name.
*/
private Map<String, AuthGroup> m_groups = new HashMap<String, AuthGroup>();
/**
* Indicates whether security is enabled. If security is disabled all authentications will succede and all returned
* AuthUsers will allow everything.
*/
private final boolean m_enabled;
/**
* The configured authentication provider
*/
private final AuthProvider m_authProvider;
/**
* VoltDB Kerberos service login context
*/
private final LoginContext m_loginCtx;
/**
* VoltDB service principal name
*/
private final byte [] m_principalName;
private final GSSManager m_gssManager;
private final InternalImporterUser m_internalImporterUser;
private final InternalAdminUser m_internalAdminUser;
//Auth system keeps a array of all perms used for auth disabled user not for checking permissions.
private static String[] m_perm_list;
AuthSystem(final Database db, boolean enabled) {
AuthProvider ap = null;
LoginContext loginContext = null;
GSSManager gssManager = null;
String principal = null;
//Build static list of perms auth system knows.
m_perm_list = new String[Permission.values().length];
int idx = 0;
for (Permission p : Permission.values()) {
m_perm_list[idx++] = p.name();
}
m_internalImporterUser = new InternalImporterUser(enabled);
m_internalAdminUser = new InternalAdminUser(enabled);
m_enabled = enabled;
if (!m_enabled) {
m_authProvider = ap;
m_loginCtx = loginContext;
m_principalName = null;
m_gssManager = null;
return;
}
m_authProvider = AuthProvider.fromProvider(db.getSecurityprovider());
if (m_authProvider == AuthProvider.KERBEROS) {
try {
loginContext = new LoginContext(VOLTDB_SERVICE_LOGIN_MODULE);
} catch (LoginException|SecurityException ex) {
VoltDB.crashGlobalVoltDB(
"Cannot initialize JAAS LoginContext", true, ex);
}
try {
loginContext.login();
principal = loginContext
.getSubject()
.getPrincipals()
.iterator().next()
.getName();
gssManager = GSSManager.getInstance();
} catch (AccountExpiredException ex) {
VoltDB.crashGlobalVoltDB(
"VoltDB assigned service principal has expired", true, ex);
} catch(CredentialExpiredException ex) {
VoltDB.crashGlobalVoltDB(
"VoltDB assigned service principal credentials have expired", true, ex);
} catch(FailedLoginException ex) {
VoltDB.crashGlobalVoltDB(
"VoltDB failed to authenticate against kerberos", true, ex);
}
catch (LoginException ex) {
VoltDB.crashGlobalVoltDB(
"VoltDB service principal failed to login", true, ex);
}
catch (Exception ex) {
VoltDB.crashGlobalVoltDB(
"Unexpected exception occured during service authentication", true, ex);
}
}
m_loginCtx = loginContext;
m_principalName = principal != null ? principal.getBytes(StandardCharsets.UTF_8) : null;
m_gssManager = gssManager;
/*
* First associate all users with groups and vice versa
*/
for (org.voltdb.catalog.User catalogUser : db.getUsers()) {
//shadow are bcrypt of sha-?
String shadowPassword = catalogUser.getShadowpassword();
String sha256shadowPassword = catalogUser.getSha256shadowpassword();
byte sha1ShadowPassword[] = null;
byte sha2ShadowPassword[] = null;
if (shadowPassword.length() == 40) {
/*
* This is an old catalog with a SHA-1 password
* Need to hex decode it
*/
sha1ShadowPassword = Encoder.hexDecode(shadowPassword);
sha2ShadowPassword = Encoder.hexDecode(sha256shadowPassword);
} else if (shadowPassword.length() != 60) {
/*
* If not 40 should be 60 since it is bcrypt
*/
VoltDB.crashGlobalVoltDB(
"Found a shadowPassword in the catalog that was in an unrecogized format", true, null);
}
final AuthUser user = new AuthUser( sha1ShadowPassword, sha2ShadowPassword, shadowPassword, sha256shadowPassword, catalogUser.getTypeName());
m_users.put(user.m_name, user);
for (org.voltdb.catalog.GroupRef catalogGroupRef : catalogUser.getGroups()) {
final org.voltdb.catalog.Group catalogGroup = catalogGroupRef.getGroup();
AuthGroup group = null;
if (!m_groups.containsKey(catalogGroup.getTypeName())) {
group = new AuthGroup(catalogGroup.getTypeName(), Permission.getPermissionSetForGroup(catalogGroup));
m_groups.put(group.m_name, group);
} else {
group = m_groups.get(catalogGroup.getTypeName());
}
user.m_permissions.addAll(group.m_permissions);
group.m_users.add(user);
user.m_groups.add(group);
}
//Cache the list so we dont rebuild everytime this is asked.
user.m_permissions_list = new String[user.m_permissions.size()];
idx = 0;
for (Permission p : user.m_permissions) {
user.m_permissions_list[idx++] = p.toString();
}
}
for (org.voltdb.catalog.Group catalogGroup : db.getGroups()) {
AuthGroup group = null;
if (!m_groups.containsKey(catalogGroup.getTypeName())) {
group = new AuthGroup(catalogGroup.getTypeName(), Permission.getPermissionSetForGroup(catalogGroup));
m_groups.put(group.m_name, group);
//A group not associated with any users? Weird stuff.
} else {
group = m_groups.get(catalogGroup.getTypeName());
}
}
/*
* Then iterate through each procedure and and add it
* to the set of procedures for each specified user and for the members of
* each specified group.
*/
for (org.voltdb.catalog.Procedure catalogProcedure : db.getProcedures()) {
for (org.voltdb.catalog.UserRef catalogUserRef : catalogProcedure.getAuthusers()) {
final org.voltdb.catalog.User catalogUser = catalogUserRef.getUser();
final AuthUser user = m_users.get(catalogUser.getTypeName());
if (user == null) {
//Error case. Procedure has a user listed as authorized but no such user exists
} else {
user.m_authorizedProcedures.add(catalogProcedure);
}
}
for (org.voltdb.catalog.GroupRef catalogGroupRef : catalogProcedure.getAuthgroups()) {
final org.voltdb.catalog.Group catalogGroup = catalogGroupRef.getGroup();
final AuthGroup group = m_groups.get(catalogGroup.getTypeName());
if (group == null) {
//Error case. Procedure has a group listed as authorized but no such user exists
} else {
for (AuthUser user : group.m_users) {
user.m_authorizedProcedures.add(catalogProcedure);
}
}
}
}
m_users = ImmutableMap.copyOf(m_users);
m_groups = ImmutableMap.copyOf(m_groups);
for (AuthUser user : m_users.values()) {
user.finish();
}
for (AuthGroup group : m_groups.values()) {
group.finish();
}
if (principal != null && m_users.containsKey(principal)) {
VoltDB.crashGlobalVoltDB("Kerberos service principal " + principal + " must not correspond to a database user", true, null);
}
}
//Is security enabled?
public boolean isSecurityEnabled() {
return m_enabled;
}
public LoginContext getLoginContext() {
return m_loginCtx;
}
public String getServicePrincipal() {
return m_principalName == null ? null : new String(m_principalName, StandardCharsets.UTF_8);
}
public InternalImporterUser getImporterUser() {
return m_internalImporterUser;
}
public InternalAdminUser getInternalAdminUser() {
return m_internalAdminUser;
}
public static class AuthDisabledUser extends AuthUser {
public AuthDisabledUser() {
super(null, null, null, null, null);
}
@Override
public boolean hasUserDefinedProcedurePermission(Procedure proc) {
return true;
}
@Override
public boolean hasPermission(Permission... p) {
return true;
}
@Override
public boolean authorizeConnector(String connectorName) {
return true;
}
@Override
public boolean isAuthEnabled() {
return false;
}
}
public static class InternalImporterUser extends AuthUser {
final static private EnumSet<Permission> PERMS = EnumSet.<Permission>of(
Permission.ALLPROC, Permission.DEFAULTPROC
);
private final boolean m_authEnabled;
private InternalImporterUser(boolean authEnabled) {
super(null, null, null, null, null);
m_authEnabled = authEnabled;
}
@Override
public boolean hasUserDefinedProcedurePermission(Procedure proc) {
return true;
}
@Override
public boolean hasPermission(Permission... p) {
if (!m_authEnabled) {
return true;
} else if (p != null && p.length == 1) {
return PERMS.contains(p[0]);
} else if (p == null || p.length == 0) {
return false;
} else {
return PERMS.containsAll(Arrays.asList(p));
}
}
@Override
public boolean authorizeConnector(String connectorName) {
return true;
}
@Override
public boolean isAuthEnabled() {
return m_authEnabled;
}
}
public static class InternalAdminUser extends AuthUser {
final static private EnumSet<Permission> PERMS =
EnumSet.<Permission>allOf(Permission.class);
private final boolean m_authEnabled;
private InternalAdminUser(boolean authEnabled) {
super(null, null, null, null, null);
m_authEnabled = authEnabled;
}
@Override
public boolean hasUserDefinedProcedurePermission(Procedure proc) {
return true;
}
@Override
public boolean hasPermission(Permission... p) {
if (!m_authEnabled) {
return true;
} else if (p != null && p.length == 1) {
return PERMS.contains(p[0]);
} else if (p == null || p.length == 0) {
return false;
} else {
return PERMS.containsAll(Arrays.asList(p));
}
}
@Override
public boolean authorizeConnector(String connectorName) {
return true;
}
@Override
public boolean isAuthEnabled() {
return m_authEnabled;
}
}
private final AuthUser m_authDisabledUser = new AuthDisabledUser();
public AuthUser getUser(String name) {
if (!m_enabled) {
return m_authDisabledUser;
}
return m_users.get(name);
}
public String[] getGroupNamesForUser(String userName) {
if (userName == null) {
return new String[] {};
}
AuthUser user = getUser(userName);
if (user == null) {
return new String[] {};
}
return user.getGroupNames();
}
//Get users permission list not god for permission checking.
public String[] getUserPermissionList(String userName) {
if (!m_enabled) {
return m_perm_list;
}
if (userName == null) {
return new String[] {};
}
AuthUser user = getUser(userName);
if (user == null) {
return new String[] {};
}
return user.m_permissions_list;
}
public class HashAuthenticationRequest extends AuthenticationRequest {
private final String m_user;
private final byte [] m_password;
public HashAuthenticationRequest(final String user, final byte [] hash) {
m_user = user;
m_password = hash;
}
@Override
protected boolean authenticateImpl(ClientAuthScheme scheme, String fromAddress) throws Exception {
if (!m_enabled) {
m_authenticatedUser = m_user;
return true;
}
else if (m_authProvider != AuthProvider.HASH) {
return false;
}
final AuthUser user = m_users.get(m_user);
if (user == null) {
logAuthFails(LogKeys.auth_AuthSystem_NoSuchUser.name(), m_user, fromAddress);
return false;
}
boolean matched = true;
if (user.m_sha1ShadowPassword != null || user.m_sha2ShadowPassword != null) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance(ClientAuthScheme.getDigestScheme(scheme));
} catch (NoSuchAlgorithmException e) {
VoltDB.crashLocalVoltDB(e.getMessage(), true, e);
}
byte passwordHash[] = md.digest(m_password);
/*
* A n00bs attempt at constant time comparison
*/
byte shaShadowPassword[] = (scheme == ClientAuthScheme.HASH_SHA1 ? user.m_sha1ShadowPassword : user.m_sha2ShadowPassword);
for (int ii = 0; ii < passwordHash.length; ii++) {
if (passwordHash[ii] != shaShadowPassword[ii]){
matched = false;
}
}
} else {
String pwToCheck = (scheme == ClientAuthScheme.HASH_SHA1 ? user.m_bcryptShadowPassword : user.m_bcryptSha2ShadowPassword);
matched = BCrypt.checkpw(Encoder.hexEncode(m_password), pwToCheck);
}
if (matched) {
m_authenticatedUser = m_user;
logAuthSuccess(m_authenticatedUser, fromAddress);
return true;
}
logAuthFails(LogKeys.auth_AuthSystem_AuthFailedPasswordMistmatch.name(), m_user, fromAddress);
return false;
}
}
private static void logAuthSuccess(String user, String fromAddress) {
//Make sure its logged per user
if (fromAddress == null) {
fromAddress = "NULL";
}
String authenticationLogMessage = String.format(
"Authenticated user %s from %s. This message is rate limited to once every 60 seconds.",
user, fromAddress);
RateLimitedLogger.tryLogForMessage(System.currentTimeMillis(), 60, TimeUnit.SECONDS,
authLogger, Level.INFO, authenticationLogMessage);
}
private static void logAuthFails(String key, String user, String fromAddress) {
authLogger.l7dlog(Level.INFO, key, new String[] {user, fromAddress}, null);
}
public class SpnegoPassthroughRequest extends AuthenticationRequest {
private final String m_authenticatedPrincipal;
public SpnegoPassthroughRequest(final String authenticatedPrincipal) {
m_authenticatedPrincipal = authenticatedPrincipal;
}
@Override
protected boolean authenticateImpl(ClientAuthScheme scheme, String fromAddress) throws Exception {
final AuthUser user = m_users.get(m_authenticatedPrincipal);
if (user == null) {
logAuthFails(LogKeys.auth_AuthSystem_NoSuchUser.name(), m_authenticatedPrincipal, fromAddress);
return false;
}
m_authenticatedUser = m_authenticatedPrincipal;
logAuthSuccess(m_authenticatedUser, fromAddress);
return true;
}
}
public class KerberosAuthenticationRequest extends AuthenticationRequest {
private SocketChannel m_socket;
public KerberosAuthenticationRequest(final SocketChannel socket) {
m_socket = socket;
}
@Override
protected boolean authenticateImpl(ClientAuthScheme scheme, String fromAddress) throws Exception {
if (!m_enabled) {
m_authenticatedUser = "_^_pinco_pallo_^_";
return true;
}
else if (m_authProvider != AuthProvider.KERBEROS) {
return false;
}
int msgSize =
4 // message size header
+ 1 // protocol version
+ 1 // result code
+ 4 // service name length
+ m_principalName.length;
final ByteBuffer bb = ByteBuffer.allocate(4096);
/*
* write the service principal response. This gives the connecting client
* the service principal name form which it constructs the GSS context
* used in the client/service authentication handshake
*/
bb.putInt(msgSize-4).put(AUTH_HANDSHAKE_VERSION).put(AUTH_SERVICE_NAME);
bb.putInt(m_principalName.length);
bb.put(m_principalName);
bb.flip();
while (bb.hasRemaining()) {
m_socket.write(bb);
}
String authenticatedUser = Subject.doAs(m_loginCtx.getSubject(), new PrivilegedAction<String>() {
/**
* Establish an authenticated GSS security context
* For further information on GSS please refer to
* <a href="http://en.wikipedia.org/wiki/Generic_Security_Services_Application_Program_Interface">this</a>
* article on Generic Security Services Application Program Interface
*/
@Override
public String run() {
GSSContext context = null;
try {
// derive the credentials from the authenticated service subject
context = m_gssManager.createContext((GSSCredential)null);
byte [] token;
while (!context.isEstablished()) {
// read in the next packet size
bb.clear().limit(4);
while (bb.hasRemaining()) {
if (m_socket.read(bb) == -1) throw new EOFException();
}
bb.flip();
int msgSize = bb.getInt();
if (msgSize > bb.capacity() || msgSize <= 0) {
authLogger.warn("Authentication packet not within alloted size");
return null;
}
// read the initiator (client) context token
bb.clear().limit(msgSize);
while (bb.hasRemaining()) {
if (m_socket.read(bb) == -1) throw new EOFException();
}
bb.flip();
byte version = bb.get();
if (version != AUTH_HANDSHAKE_VERSION) {
authLogger.warn("Encountered unexpected authentication protocol version " + version);
return null;
}
byte tag = bb.get();
if (tag != AUTH_HANDSHAKE) {
authLogger.warn("Encountered unexpected authentication protocol tag " + tag);
return null;
}
// process the initiator (client) context token. If it returns a non empty token
// transmit it to the initiator
token = context.acceptSecContext(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining());
if (token != null) {
msgSize = 4 + 1 + 1 + token.length;
bb.clear().limit(msgSize);
bb.putInt(msgSize-4).put(AUTH_HANDSHAKE_VERSION).put(AUTH_HANDSHAKE);
bb.put(token);
bb.flip();
while (bb.hasRemaining()) {
m_socket.write(bb);
}
}
}
// at this juncture we an established security context between
// the client and this service
String authenticateUserName = context.getSrcName().toString();
// check if both ends are authenticated
if (!context.getMutualAuthState()) {
return null;
}
// read the delegate user if the Volt's accepting service principal is the
// same as the one that initiated,
if ( context.getTargName() != null
&& context.getSrcName().equals(context.getTargName())
) {
// read in the next packet size
bb.clear().limit(4);
while (bb.hasRemaining()) {
if (m_socket.read(bb) == -1) throw new EOFException();
}
bb.flip();
int msgSize = bb.getInt();
if (msgSize > bb.capacity() || msgSize <= 0) {
authLogger.warn("Authentication packet not within alloted size");
return null;
}
// read the initiator (client) context token
bb.clear().limit(msgSize);
while (bb.hasRemaining()) {
if (m_socket.read(bb) == -1) throw new EOFException();
}
bb.flip();
byte version = bb.get();
if (version != AUTH_HANDSHAKE_VERSION) {
authLogger.warn("Encountered unexpected authentication protocol version " + version);
return null;
}
byte tag = bb.get();
if (tag != AUTH_HANDSHAKE) {
authLogger.warn("Encountered unexpected authentication protocol tag " + tag);
return null;
}
MessageProp mprop = new MessageProp(0, true);
DelegatePrincipal delegate = new DelegatePrincipal(
context.unwrap(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining(), mprop)
);
if (delegate.getId() != System.identityHashCode(AuthSystem.this)) {
return null;
}
authenticateUserName = delegate.getName();
}
context.dispose();
context = null;
return authenticateUserName;
} catch (IOException|GSSException ex) {
Throwables.propagate(ex);
} finally {
if (context != null) try { context.dispose(); } catch (Exception ignoreIt) {}
}
return null;
}
});
if (authenticatedUser == null)
return false;
final AuthUser user = m_users.get(authenticatedUser);
if (user == null) {
logAuthFails(LogKeys.auth_AuthSystem_NoSuchUser.name(), authenticatedUser, fromAddress);
return false;
}
m_authenticatedUser = authenticatedUser;
logAuthSuccess(m_authenticatedUser, fromAddress);
return true;
}
}
}