Merge pull request #3506 from abstractj/KEYCLOAK-3913
[KEYCLOAK-3913] - Native libraries included within SSSD jar
This commit is contained in:
commit
cf17687b8b
12 changed files with 529 additions and 116 deletions
|
@ -22,6 +22,7 @@
|
|||
|
||||
<resources>
|
||||
<artifact name="${org.keycloak:keycloak-sssd-federation}"/>
|
||||
<resource-root path="/usr/share/java/jna.jar"/>
|
||||
</resources>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
|
@ -70,10 +71,6 @@
|
|||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jnr</groupId>
|
||||
<artifactId>jnr-unixsocket</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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 cx.ath.matthew;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>.
|
||||
*/
|
||||
public class LibraryLoader {
|
||||
|
||||
private static final String[] PATHS = {"/usr/lib/", "/usr/lib64/", "/usr/local/lib/", "/opt/local/lib/"};
|
||||
private static final String LIBRARY_NAME = "libunix_dbus_java";
|
||||
private static final String VERSION = "0.0.8";
|
||||
private static boolean loadSucceeded;
|
||||
|
||||
public static LibraryLoader load() {
|
||||
for (String path : PATHS) {
|
||||
try {
|
||||
System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION));
|
||||
loadSucceeded = true;
|
||||
break;
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
loadSucceeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
return new LibraryLoader();
|
||||
}
|
||||
|
||||
public boolean succeed() {
|
||||
return loadSucceeded;
|
||||
}
|
||||
}
|
|
@ -26,25 +26,25 @@
|
|||
*/
|
||||
package cx.ath.matthew.unix;
|
||||
|
||||
import jnr.unixsocket.UnixSocketChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.Channels;
|
||||
|
||||
public class USInputStream extends InputStream {
|
||||
public static final int MSG_DONTWAIT = 0x40;
|
||||
private UnixSocketChannel channel;
|
||||
|
||||
private native int native_recv(int sock, byte[] b, int off, int len, int flags, int timeout) throws IOException;
|
||||
|
||||
private int sock;
|
||||
boolean closed = false;
|
||||
private byte[] onebuf = new byte[1];
|
||||
private UnixSocket us;
|
||||
private boolean blocking = true;
|
||||
private int flags = 0;
|
||||
private int timeout = 0;
|
||||
|
||||
public USInputStream(UnixSocketChannel channel, UnixSocket us) {
|
||||
public USInputStream(int sock, UnixSocket us) {
|
||||
this.sock = sock;
|
||||
this.us = us;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
|
@ -65,8 +65,7 @@ public class USInputStream extends InputStream {
|
|||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (closed) throw new NotConnectedException();
|
||||
int count = receive(b, off, len);
|
||||
|
||||
int count = native_recv(sock, b, off, len, flags, timeout);
|
||||
/* Yes, I really want to do this. Recv returns 0 for 'connection shut down'.
|
||||
* read() returns -1 for 'end of stream.
|
||||
* Recv returns -1 for 'EAGAIN' (all other errors cause an exception to be raised)
|
||||
|
@ -92,21 +91,4 @@ public class USInputStream extends InputStream {
|
|||
public void setSoTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from JRuby with small modifications
|
||||
* @see <a href="https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/ext/socket/RubyUNIXSocket.java">RubyUNIXSocket.java</a>
|
||||
*/
|
||||
private int receive(byte[] dataBytes, int off, int len) {
|
||||
int recvStatus = -1;
|
||||
try {
|
||||
InputStream inputStream = Channels.newInputStream(channel);
|
||||
recvStatus = inputStream.read(dataBytes, off, len);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return recvStatus;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,31 +26,22 @@
|
|||
*/
|
||||
package cx.ath.matthew.unix;
|
||||
|
||||
import jnr.constants.platform.linux.SocketLevel;
|
||||
import jnr.posix.CmsgHdr;
|
||||
import jnr.posix.MsgHdr;
|
||||
import jnr.posix.POSIX;
|
||||
import jnr.posix.POSIXFactory;
|
||||
import jnr.unixsocket.UnixSocketChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class USOutputStream extends OutputStream {
|
||||
private native int native_send(int sock, byte[] b, int off, int len) throws IOException;
|
||||
|
||||
private UnixSocketChannel channel;
|
||||
private native int native_send(int sock, byte[][] b) throws IOException;
|
||||
|
||||
private int sock;
|
||||
boolean closed = false;
|
||||
private byte[] onebuf = new byte[1];
|
||||
private UnixSocket us;
|
||||
|
||||
public USOutputStream(UnixSocketChannel channel, int sock, UnixSocket us) {
|
||||
public USOutputStream(int sock, UnixSocket us) {
|
||||
this.sock = sock;
|
||||
this.us = us;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
|
@ -61,9 +52,14 @@ public class USOutputStream extends OutputStream {
|
|||
public void flush() {
|
||||
} // no-op, we do not buffer
|
||||
|
||||
public void write(byte[][] b) throws IOException {
|
||||
if (closed) throw new NotConnectedException();
|
||||
native_send(sock, b);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (closed) throw new NotConnectedException();
|
||||
send(sock, b, off, len);
|
||||
native_send(sock, b, off, len);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
|
@ -79,46 +75,4 @@ public class USOutputStream extends OutputStream {
|
|||
public UnixSocket getSocket() {
|
||||
return us;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from JRuby with small modifications
|
||||
* @see <a href="https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/ext/socket/RubyUNIXSocket.java">RubyUNIXSocket.java</a>
|
||||
*/
|
||||
private void send(int sock, ByteBuffer[] outIov) {
|
||||
|
||||
final POSIX posix = POSIXFactory.getNativePOSIX();
|
||||
MsgHdr outMessage = posix.allocateMsgHdr();
|
||||
|
||||
outMessage.setIov(outIov);
|
||||
|
||||
CmsgHdr outControl = outMessage.allocateControl(4);
|
||||
outControl.setLevel(SocketLevel.SOL_SOCKET.intValue());
|
||||
outControl.setType(0x01);
|
||||
|
||||
ByteBuffer fdBuf = ByteBuffer.allocateDirect(4);
|
||||
fdBuf.order(ByteOrder.nativeOrder());
|
||||
fdBuf.putInt(0, channel.getFD());
|
||||
outControl.setData(fdBuf);
|
||||
|
||||
posix.sendmsg(sock, outMessage, 0);
|
||||
|
||||
}
|
||||
|
||||
private void send(int sock, byte[] dataBytes, int off, int len) {
|
||||
ByteBuffer[] outIov = new ByteBuffer[1];
|
||||
outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
|
||||
outIov[0].put(dataBytes, off, len);
|
||||
outIov[0].flip();
|
||||
|
||||
send(sock, outIov);
|
||||
}
|
||||
|
||||
protected void send(int sock, byte[] dataBytes) {
|
||||
ByteBuffer[] outIov = new ByteBuffer[1];
|
||||
outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
|
||||
outIov[0].put(dataBytes);
|
||||
outIov[0].flip();
|
||||
|
||||
send(sock, outIov);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Java Unix Sockets Library
|
||||
*
|
||||
* Copyright (c) Matthew Johnson 2004
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* To Contact the author, please email src@matthew.ath.cx
|
||||
*
|
||||
*/
|
||||
package cx.ath.matthew.unix;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An IO Exception which occurred during UNIX Socket IO
|
||||
*/
|
||||
public class UnixIOException extends IOException {
|
||||
private int no;
|
||||
private String message;
|
||||
|
||||
public UnixIOException(int no, String message) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.no = no;
|
||||
}
|
||||
}
|
|
@ -26,11 +26,9 @@
|
|||
*/
|
||||
package cx.ath.matthew.unix;
|
||||
|
||||
import cx.ath.matthew.LibraryLoader;
|
||||
import cx.ath.matthew.debug.Debug;
|
||||
import jnr.unixsocket.UnixSocketAddress;
|
||||
import jnr.unixsocket.UnixSocketChannel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -39,8 +37,25 @@ import java.io.OutputStream;
|
|||
* Represents a UnixSocket.
|
||||
*/
|
||||
public class UnixSocket {
|
||||
static {
|
||||
LibraryLoader.load();
|
||||
}
|
||||
|
||||
private UnixSocketChannel channel;
|
||||
private native void native_set_pass_cred(int sock, boolean passcred) throws IOException;
|
||||
|
||||
private native int native_connect(String address, boolean abs) throws IOException;
|
||||
|
||||
private native void native_close(int sock) throws IOException;
|
||||
|
||||
private native int native_getPID(int sock);
|
||||
|
||||
private native int native_getUID(int sock);
|
||||
|
||||
private native int native_getGID(int sock);
|
||||
|
||||
private native void native_send_creds(int sock, byte data) throws IOException;
|
||||
|
||||
private native byte native_recv_creds(int sock, int[] creds) throws IOException;
|
||||
|
||||
private UnixSocketAddress address = null;
|
||||
private USOutputStream os = null;
|
||||
|
@ -58,8 +73,8 @@ public class UnixSocket {
|
|||
this.sock = sock;
|
||||
this.address = address;
|
||||
this.connected = true;
|
||||
this.os = new USOutputStream(channel, sock, this);
|
||||
this.is = new USInputStream(channel, this);
|
||||
this.os = new USOutputStream(sock, this);
|
||||
this.is = new USInputStream(sock, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,7 +98,7 @@ public class UnixSocket {
|
|||
* @param address The Unix Socket address to connect to
|
||||
*/
|
||||
public UnixSocket(String address) throws IOException {
|
||||
this(new UnixSocketAddress(new File(address)));
|
||||
this(new UnixSocketAddress(address));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,11 +108,9 @@ public class UnixSocket {
|
|||
*/
|
||||
public void connect(UnixSocketAddress address) throws IOException {
|
||||
if (connected) close();
|
||||
this.channel = UnixSocketChannel.open(address);
|
||||
this.channel = UnixSocketChannel.open(address);
|
||||
this.sock = channel.getFD();
|
||||
this.os = new USOutputStream(channel, sock, this);
|
||||
this.is = new USInputStream(channel, this);
|
||||
this.sock = native_connect(address.path, address.abs);
|
||||
this.os = new USOutputStream(this.sock, this);
|
||||
this.is = new USInputStream(this.sock, this);
|
||||
this.address = address;
|
||||
this.connected = true;
|
||||
this.closed = false;
|
||||
|
@ -110,7 +123,7 @@ public class UnixSocket {
|
|||
* @param address The Unix Socket address to connect to
|
||||
*/
|
||||
public void connect(String address) throws IOException {
|
||||
connect(new UnixSocketAddress(new File(address)));
|
||||
connect(new UnixSocketAddress(address));
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
|
@ -125,7 +138,7 @@ public class UnixSocket {
|
|||
*/
|
||||
public synchronized void close() throws IOException {
|
||||
if (Debug.debug) Debug.print(Debug.INFO, "Closing socket");
|
||||
channel.close();
|
||||
native_close(sock);
|
||||
sock = 0;
|
||||
this.closed = true;
|
||||
this.connected = false;
|
||||
|
@ -169,7 +182,91 @@ public class UnixSocket {
|
|||
*/
|
||||
public void sendCredentialByte(byte data) throws IOException {
|
||||
if (!connected) throw new NotConnectedException();
|
||||
os.send(channel.getFD(), new byte[]{ data });
|
||||
native_send_creds(sock, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a single byte of data, with credentials.
|
||||
* (Works on BSDs)
|
||||
*
|
||||
* @param data The byte of data to send.
|
||||
* @see getPeerUID
|
||||
* @see getPeerPID
|
||||
* @see getPeerGID
|
||||
*/
|
||||
public byte recvCredentialByte() throws IOException {
|
||||
if (!connected) throw new NotConnectedException();
|
||||
int[] creds = new int[]{-1, -1, -1};
|
||||
byte data = native_recv_creds(sock, creds);
|
||||
pid = creds[0];
|
||||
uid = creds[1];
|
||||
gid = creds[2];
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the credential passing status.
|
||||
* (only effective on linux)
|
||||
*
|
||||
* @return The current status of credential passing.
|
||||
* @see setPassCred
|
||||
*/
|
||||
public boolean getPassCred() {
|
||||
return passcred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the uid of the remote process.
|
||||
* Some data must have been received on the socket to do this.
|
||||
* Either setPassCred must be called on Linux first, or recvCredentialByte
|
||||
* on BSD.
|
||||
*
|
||||
* @return the UID or -1 if it is not available
|
||||
*/
|
||||
public int getPeerUID() {
|
||||
if (-1 == uid)
|
||||
uid = native_getUID(sock);
|
||||
return uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the gid of the remote process.
|
||||
* Some data must have been received on the socket to do this.
|
||||
* Either setPassCred must be called on Linux first, or recvCredentialByte
|
||||
* on BSD.
|
||||
*
|
||||
* @return the GID or -1 if it is not available
|
||||
*/
|
||||
public int getPeerGID() {
|
||||
if (-1 == gid)
|
||||
gid = native_getGID(sock);
|
||||
return gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pid of the remote process.
|
||||
* Some data must have been received on the socket to do this.
|
||||
* Either setPassCred must be called on Linux first, or recvCredentialByte
|
||||
* on BSD.
|
||||
*
|
||||
* @return the PID or -1 if it is not available
|
||||
*/
|
||||
public int getPeerPID() {
|
||||
if (-1 == pid)
|
||||
pid = native_getPID(sock);
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the credential passing status.
|
||||
* (Only does anything on linux, for other OS, you need
|
||||
* to use send/recv credentials)
|
||||
*
|
||||
* @param enable Set to true for credentials to be passed.
|
||||
*/
|
||||
public void setPassCred(boolean enable) throws IOException {
|
||||
native_set_pass_cred(sock, enable);
|
||||
passcred = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Java Unix Sockets Library
|
||||
*
|
||||
* Copyright (c) Matthew Johnson 2004
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* To Contact the author, please email src@matthew.ath.cx
|
||||
*
|
||||
*/
|
||||
package cx.ath.matthew.unix;
|
||||
|
||||
/**
|
||||
* Represents an address for a Unix Socket
|
||||
*/
|
||||
public class UnixSocketAddress {
|
||||
String path;
|
||||
boolean abs;
|
||||
|
||||
/**
|
||||
* Create the address.
|
||||
*
|
||||
* @param path The path to the Unix Socket.
|
||||
* @param abs True if this should be an abstract socket.
|
||||
*/
|
||||
public UnixSocketAddress(String path, boolean abs) {
|
||||
this.path = path;
|
||||
this.abs = abs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the address.
|
||||
*
|
||||
* @param path The path to the Unix Socket.
|
||||
*/
|
||||
public UnixSocketAddress(String path) {
|
||||
this.path = path;
|
||||
this.abs = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this an address for an abstract socket.
|
||||
*/
|
||||
public boolean isAbstract() {
|
||||
return abs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Address as a String.
|
||||
*/
|
||||
public String toString() {
|
||||
return "unix" + (abs ? ":abstract" : "") + ":path=" + path;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof UnixSocketAddress)) return false;
|
||||
return ((UnixSocketAddress) o).path.equals(this.path);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path.hashCode();
|
||||
}
|
||||
}
|
|
@ -43,12 +43,20 @@ public class MessageWriter {
|
|||
if (Debug.debug) Debug.print(Debug.WARN, "Message " + m + " wire-data was null!");
|
||||
return;
|
||||
}
|
||||
for (byte[] buf : m.getWireData()) {
|
||||
if (Debug.debug)
|
||||
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
|
||||
if (null == buf) break;
|
||||
out.write(buf);
|
||||
}
|
||||
if (isunix) {
|
||||
if (Debug.debug) {
|
||||
Debug.print(Debug.DEBUG, "Writing all " + m.getWireData().length + " buffers simultaneously to Unix Socket");
|
||||
for (byte[] buf : m.getWireData())
|
||||
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
|
||||
}
|
||||
((USOutputStream) out).write(m.getWireData());
|
||||
} else
|
||||
for (byte[] buf : m.getWireData()) {
|
||||
if (Debug.debug)
|
||||
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
|
||||
if (null == buf) break;
|
||||
out.write(buf);
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ package org.freedesktop.dbus;
|
|||
|
||||
import cx.ath.matthew.debug.Debug;
|
||||
import cx.ath.matthew.unix.UnixSocket;
|
||||
import cx.ath.matthew.unix.UnixSocketAddress;
|
||||
import cx.ath.matthew.utils.Hexdump;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -25,6 +26,7 @@ import java.io.OutputStream;
|
|||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -255,8 +257,10 @@ public class Transport {
|
|||
return new String(res);
|
||||
}
|
||||
|
||||
public static final int MODE_SERVER = 1;
|
||||
public static final int MODE_CLIENT = 2;
|
||||
|
||||
public static final int AUTH_NONE = 0;
|
||||
public static final int AUTH_EXTERNAL = 1;
|
||||
public static final int AUTH_SHA = 2;
|
||||
public static final int AUTH_ANON = 4;
|
||||
|
@ -273,12 +277,15 @@ public class Transport {
|
|||
public static final int WAIT_DATA = 1;
|
||||
public static final int WAIT_OK = 2;
|
||||
public static final int WAIT_REJECT = 3;
|
||||
public static final int WAIT_AUTH = 4;
|
||||
public static final int WAIT_BEGIN = 5;
|
||||
public static final int AUTHENTICATED = 6;
|
||||
public static final int FAILED = 7;
|
||||
|
||||
public static final int OK = 1;
|
||||
public static final int CONTINUE = 2;
|
||||
public static final int ERROR = 3;
|
||||
public static final int REJECT = 4;
|
||||
|
||||
public Command receive(InputStream s) throws IOException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
@ -388,8 +395,89 @@ public class Transport {
|
|||
}
|
||||
}
|
||||
|
||||
public String challenge = "";
|
||||
public String cookie = "";
|
||||
|
||||
public int do_response(int auth, String Uid, String kernelUid, Command c) {
|
||||
MessageDigest md = null;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA");
|
||||
} catch (NoSuchAlgorithmException NSAe) {
|
||||
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe);
|
||||
return ERROR;
|
||||
}
|
||||
switch (auth) {
|
||||
case AUTH_NONE:
|
||||
switch (c.getMechs()) {
|
||||
case AUTH_ANON:
|
||||
return OK;
|
||||
case AUTH_EXTERNAL:
|
||||
if (0 == col.compare(Uid, c.getData()) &&
|
||||
(null == kernelUid || 0 == col.compare(Uid, kernelUid)))
|
||||
return OK;
|
||||
else
|
||||
return ERROR;
|
||||
case AUTH_SHA:
|
||||
String context = COOKIE_CONTEXT;
|
||||
long id = System.currentTimeMillis();
|
||||
byte[] buf = new byte[8];
|
||||
Message.marshallintBig(id, buf, 0, 8);
|
||||
challenge = stupidlyEncode(md.digest(buf));
|
||||
Random r = new Random();
|
||||
r.nextBytes(buf);
|
||||
cookie = stupidlyEncode(md.digest(buf));
|
||||
try {
|
||||
addCookie(context, "" + id, id / 1000, cookie);
|
||||
} catch (IOException IOe) {
|
||||
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, IOe);
|
||||
}
|
||||
if (Debug.debug)
|
||||
Debug.print(Debug.DEBUG, "Sending challenge: " + context + ' ' + id + ' ' + challenge);
|
||||
c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge));
|
||||
return CONTINUE;
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
case AUTH_SHA:
|
||||
String[] response = stupidlyDecode(c.getData()).split(" ");
|
||||
if (response.length < 2) return ERROR;
|
||||
String cchal = response[0];
|
||||
String hash = response[1];
|
||||
String prehash = challenge + ":" + cchal + ":" + cookie;
|
||||
byte[] buf = md.digest(prehash.getBytes());
|
||||
String posthash = stupidlyEncode(buf);
|
||||
if (Debug.debug)
|
||||
Debug.print(Debug.DEBUG, "Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash=" + posthash);
|
||||
if (0 == col.compare(posthash, hash))
|
||||
return OK;
|
||||
else
|
||||
return ERROR;
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getTypes(int types) {
|
||||
switch (types) {
|
||||
case AUTH_EXTERNAL:
|
||||
return new String[]{"EXTERNAL"};
|
||||
case AUTH_SHA:
|
||||
return new String[]{"DBUS_COOKIE_SHA1"};
|
||||
case AUTH_ANON:
|
||||
return new String[]{"ANONYMOUS"};
|
||||
case AUTH_SHA + AUTH_EXTERNAL:
|
||||
return new String[]{"EXTERNAL", "DBUS_COOKIE_SHA1"};
|
||||
case AUTH_SHA + AUTH_ANON:
|
||||
return new String[]{"ANONYMOUS", "DBUS_COOKIE_SHA1"};
|
||||
case AUTH_EXTERNAL + AUTH_ANON:
|
||||
return new String[]{"ANONYMOUS", "EXTERNAL"};
|
||||
case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA:
|
||||
return new String[]{"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"};
|
||||
default:
|
||||
return new String[]{};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* performs SASL auth on the given streams.
|
||||
* Mode selects whether to run as a SASL server or client.
|
||||
|
@ -400,6 +488,7 @@ public class Transport {
|
|||
public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException {
|
||||
String username = System.getProperty("user.name");
|
||||
String Uid = null;
|
||||
String kernelUid = null;
|
||||
try {
|
||||
Class c = Class.forName("com.sun.security.auth.module.UnixSystem");
|
||||
Method m = c.getMethod("getUid");
|
||||
|
@ -529,6 +618,110 @@ public class Transport {
|
|||
state = FAILED;
|
||||
}
|
||||
break;
|
||||
case MODE_SERVER:
|
||||
switch (state) {
|
||||
case INITIAL_STATE:
|
||||
byte[] buf = new byte[1];
|
||||
if (null == us) {
|
||||
in.read(buf);
|
||||
} else {
|
||||
buf[0] = us.recvCredentialByte();
|
||||
int kuid = us.getPeerUID();
|
||||
if (kuid >= 0)
|
||||
kernelUid = stupidlyEncode("" + kuid);
|
||||
}
|
||||
if (0 != buf[0]) state = FAILED;
|
||||
else state = WAIT_AUTH;
|
||||
break;
|
||||
case WAIT_AUTH:
|
||||
c = receive(in);
|
||||
switch (c.getCommand()) {
|
||||
case COMMAND_AUTH:
|
||||
if (null == c.getData()) {
|
||||
send(out, COMMAND_REJECTED, getTypes(types));
|
||||
} else {
|
||||
switch (do_response(current, Uid, kernelUid, c)) {
|
||||
case CONTINUE:
|
||||
send(out, COMMAND_DATA, c.getResponse());
|
||||
current = c.getMechs();
|
||||
state = WAIT_DATA;
|
||||
break;
|
||||
case OK:
|
||||
send(out, COMMAND_OK, guid);
|
||||
state = WAIT_BEGIN;
|
||||
current = 0;
|
||||
break;
|
||||
case REJECT:
|
||||
send(out, COMMAND_REJECTED, getTypes(types));
|
||||
current = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case COMMAND_ERROR:
|
||||
send(out, COMMAND_REJECTED, getTypes(types));
|
||||
break;
|
||||
case COMMAND_BEGIN:
|
||||
state = FAILED;
|
||||
break;
|
||||
default:
|
||||
send(out, COMMAND_ERROR, "Got invalid command");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WAIT_DATA:
|
||||
c = receive(in);
|
||||
switch (c.getCommand()) {
|
||||
case COMMAND_DATA:
|
||||
switch (do_response(current, Uid, kernelUid, c)) {
|
||||
case CONTINUE:
|
||||
send(out, COMMAND_DATA, c.getResponse());
|
||||
state = WAIT_DATA;
|
||||
break;
|
||||
case OK:
|
||||
send(out, COMMAND_OK, guid);
|
||||
state = WAIT_BEGIN;
|
||||
current = 0;
|
||||
break;
|
||||
case REJECT:
|
||||
send(out, COMMAND_REJECTED, getTypes(types));
|
||||
current = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case COMMAND_ERROR:
|
||||
case COMMAND_CANCEL:
|
||||
send(out, COMMAND_REJECTED, getTypes(types));
|
||||
state = WAIT_AUTH;
|
||||
break;
|
||||
case COMMAND_BEGIN:
|
||||
state = FAILED;
|
||||
break;
|
||||
default:
|
||||
send(out, COMMAND_ERROR, "Got invalid command");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WAIT_BEGIN:
|
||||
c = receive(in);
|
||||
switch (c.getCommand()) {
|
||||
case COMMAND_ERROR:
|
||||
case COMMAND_CANCEL:
|
||||
send(out, COMMAND_REJECTED, getTypes(types));
|
||||
state = WAIT_AUTH;
|
||||
break;
|
||||
case COMMAND_BEGIN:
|
||||
state = AUTHENTICATED;
|
||||
break;
|
||||
default:
|
||||
send(out, COMMAND_ERROR, "Got invalid command");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
state = FAILED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -588,15 +781,25 @@ public class Transport {
|
|||
types = SASL.AUTH_EXTERNAL;
|
||||
mode = SASL.MODE_CLIENT;
|
||||
us = new UnixSocket();
|
||||
if (null != address.getParameter("path"))
|
||||
us.connect(new jnr.unixsocket.UnixSocketAddress(new File(address.getParameter("path"))));
|
||||
if (null != address.getParameter("abstract"))
|
||||
us.connect(new UnixSocketAddress(address.getParameter("abstract"), true));
|
||||
else if (null != address.getParameter("path"))
|
||||
us.connect(new UnixSocketAddress(address.getParameter("path"), false));
|
||||
us.setPassCred(true);
|
||||
in = us.getInputStream();
|
||||
out = us.getOutputStream();
|
||||
} else if ("tcp".equals(address.getType())) {
|
||||
types = SASL.AUTH_SHA;
|
||||
mode = SASL.MODE_CLIENT;
|
||||
s = new Socket();
|
||||
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
||||
if (null != address.getParameter("listen")) {
|
||||
mode = SASL.MODE_SERVER;
|
||||
ServerSocket ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
||||
s = ss.accept();
|
||||
} else {
|
||||
mode = SASL.MODE_CLIENT;
|
||||
s = new Socket();
|
||||
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
||||
}
|
||||
in = s.getInputStream();
|
||||
out = s.getOutputStream();
|
||||
} else {
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -98,7 +98,6 @@
|
|||
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
|
||||
<twitter4j.version>4.0.4</twitter4j.version>
|
||||
<jna.version>4.1.0</jna.version>
|
||||
<jnr.version>0.14</jnr.version>
|
||||
|
||||
<!-- Test -->
|
||||
<greenmail.version>1.3.1b</greenmail.version>
|
||||
|
@ -702,11 +701,6 @@
|
|||
<artifactId>jna</artifactId>
|
||||
<version>${jna.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jnr</groupId>
|
||||
<artifactId>jnr-unixsocket</artifactId>
|
||||
<version>${jnr.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-ldap-federation</artifactId>
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.testsuite.sssd;
|
|||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
|
@ -72,6 +73,7 @@ public class SSSDTest extends AbstractKeycloakTest {
|
|||
adminClient.realm(REALM_NAME).userFederation().create(userFederation);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testProviderFactories() {
|
||||
List<UserFederationProviderFactoryRepresentation> providerFactories = adminClient.realm(REALM_NAME).userFederation().getProviderFactories();
|
||||
|
|
Loading…
Reference in a new issue