Added support to fetch the UID, GID, and PID of the connected unix domain socket (EG: SO_PEERCREDS)
Motivation: I had a need to know the user credentials of a connected unix domain socket. Modifications: Added a class to encapsulate user credentials (UID, GID, and the PID). Augemented the Socket class to provide the JNI native interface to return this new class Augemented the c code to call getSockOpts passing <a href=http://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a> Then surfaced the ability to get user credentials in the EpollDomainSocketChannel Result: The EpollDomainSocketChannel now has a the following function signature: public PeerCredentials peerCredentials() throws IOException allowing a caller to get the UID, GID, and PID of the linux process connected to the unix domain socket.
This commit is contained in:
parent
e3cb9935c0
commit
b3fa976028
@ -13,6 +13,12 @@
|
|||||||
* License for the specific language governing permissions and limitations
|
* License for the specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
* Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined
|
||||||
|
* (before including any header files) in order to obtain the
|
||||||
|
* definition of this structure. See <a href=https://linux.die.net/man/7/unix>
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -29,8 +35,10 @@
|
|||||||
#include "netty_unix_util.h"
|
#include "netty_unix_util.h"
|
||||||
|
|
||||||
static jclass datagramSocketAddressClass = NULL;
|
static jclass datagramSocketAddressClass = NULL;
|
||||||
|
static jclass peerCredentialsClass = NULL;
|
||||||
static jmethodID datagramSocketAddrMethodId = NULL;
|
static jmethodID datagramSocketAddrMethodId = NULL;
|
||||||
static jmethodID inetSocketAddrMethodId = NULL;
|
static jmethodID inetSocketAddrMethodId = NULL;
|
||||||
|
static jmethodID peerCredentialsMethodId = NULL;
|
||||||
static jclass inetSocketAddressClass = NULL;
|
static jclass inetSocketAddressClass = NULL;
|
||||||
static jclass netUtilClass = NULL;
|
static jclass netUtilClass = NULL;
|
||||||
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
||||||
@ -647,6 +655,14 @@ static jint netty_unix_socket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd)
|
|||||||
}
|
}
|
||||||
return optval;
|
return optval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jobject netty_channel_unix_socket_getPeerCredentials(JNIEnv *env, jclass clazz, jint fd) {
|
||||||
|
struct ucred credentials;
|
||||||
|
if(netty_unix_socket_getOption(env,fd, SOL_SOCKET, SO_PEERCRED, &credentials, sizeof (credentials))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (*env)->NewObject(env, peerCredentialsClass, peerCredentialsMethodId, credentials.pid, credentials.uid, credentials.gid);
|
||||||
|
}
|
||||||
// JNI Registered Methods End
|
// JNI Registered Methods End
|
||||||
|
|
||||||
// JNI Method Registration Table Begin
|
// JNI Method Registration Table Begin
|
||||||
@ -690,7 +706,7 @@ static const JNINativeMethod fixed_method_table[] = {
|
|||||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||||
|
|
||||||
static jint dynamicMethodsTableSize() {
|
static jint dynamicMethodsTableSize() {
|
||||||
return fixed_method_table_size + 2;
|
return fixed_method_table_size + 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
||||||
@ -708,6 +724,12 @@ static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
|
|||||||
dynamicMethod->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName);
|
dynamicMethod->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName);
|
||||||
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress;
|
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress;
|
||||||
free(dynamicTypeName);
|
free(dynamicTypeName);
|
||||||
|
++dynamicMethod;
|
||||||
|
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;");
|
||||||
|
dynamicMethod->name = "getPeerCredentials";
|
||||||
|
dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
|
||||||
|
dynamicMethod->fnPtr = (void *) netty_channel_unix_socket_getPeerCredentials;
|
||||||
|
free(dynamicTypeName);
|
||||||
return dynamicMethods;
|
return dynamicMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,6 +810,26 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
|||||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()");
|
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()");
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials");
|
||||||
|
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName);
|
||||||
|
free(nettyClassName);
|
||||||
|
nettyClassName = NULL;
|
||||||
|
if (localPeerCredsClass == NULL) {
|
||||||
|
// pending exception...
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass);
|
||||||
|
if (peerCredentialsClass == NULL) {
|
||||||
|
// out-of-memory!
|
||||||
|
netty_unix_errors_throwOutOfMemoryError(env);
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "<init>", "(III)V");
|
||||||
|
if (peerCredentialsMethodId == NULL) {
|
||||||
|
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.<init>(int, int, int)");
|
||||||
|
return JNI_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
void* mem = malloc(1);
|
void* mem = malloc(1);
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
@ -826,4 +868,8 @@ void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
|
|||||||
(*env)->DeleteGlobalRef(env, netUtilClass);
|
(*env)->DeleteGlobalRef(env, netUtilClass);
|
||||||
netUtilClass = NULL;
|
netUtilClass = NULL;
|
||||||
}
|
}
|
||||||
|
if (peerCredentialsClass != NULL) {
|
||||||
|
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
|
||||||
|
peerCredentialsClass = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,13 @@ import io.netty.channel.ChannelPipeline;
|
|||||||
import io.netty.channel.unix.DomainSocketAddress;
|
import io.netty.channel.unix.DomainSocketAddress;
|
||||||
import io.netty.channel.unix.DomainSocketChannel;
|
import io.netty.channel.unix.DomainSocketChannel;
|
||||||
import io.netty.channel.unix.FileDescriptor;
|
import io.netty.channel.unix.FileDescriptor;
|
||||||
|
import io.netty.channel.unix.PeerCredentials;
|
||||||
import io.netty.channel.unix.Socket;
|
import io.netty.channel.unix.Socket;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
import static io.netty.channel.unix.Socket.newSocketDomain;
|
import static io.netty.channel.unix.Socket.newSocketDomain;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
|
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
|
||||||
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
|
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
|
||||||
@ -132,6 +134,14 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
|||||||
return super.filterOutboundMessage(msg);
|
return super.filterOutboundMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unix credentials (uid, gid, pid) of the peer
|
||||||
|
* <a href=http://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a>
|
||||||
|
*/
|
||||||
|
public PeerCredentials peerCredentials() throws IOException {
|
||||||
|
return fd().getPeerCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
private final class EpollDomainUnsafe extends EpollStreamUnsafe {
|
private final class EpollDomainUnsafe extends EpollStreamUnsafe {
|
||||||
@Override
|
@Override
|
||||||
void epollInReady() {
|
void epollInReady() {
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project 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 io.netty.channel.unix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User credentials discovered for the peer unix domain socket.
|
||||||
|
*
|
||||||
|
* The PID, UID and GID of the user connected on the other side of the unix domain socket
|
||||||
|
* For details see:
|
||||||
|
* <a href=http://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a>
|
||||||
|
*/
|
||||||
|
public final class PeerCredentials {
|
||||||
|
private final int pid;
|
||||||
|
private final int uid;
|
||||||
|
private final int gid;
|
||||||
|
|
||||||
|
// These values are set by JNI via Socket.peerCredentials()
|
||||||
|
PeerCredentials(int p, int u, int g) {
|
||||||
|
pid = p;
|
||||||
|
uid = u;
|
||||||
|
gid = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int pid() {
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int uid() {
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int gid() {
|
||||||
|
return gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserCredentials["
|
||||||
|
+ "pid=" + pid
|
||||||
|
+ "; uid=" + uid
|
||||||
|
+ "; gid=" + gid
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
}
|
@ -319,6 +319,10 @@ public final class Socket extends FileDescriptor {
|
|||||||
return getSoError(fd);
|
return getSoError(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PeerCredentials getPeerCredentials() throws IOException {
|
||||||
|
return getPeerCredentials(fd);
|
||||||
|
}
|
||||||
|
|
||||||
public void setKeepAlive(boolean keepAlive) throws IOException {
|
public void setKeepAlive(boolean keepAlive) throws IOException {
|
||||||
setKeepAlive(fd, keepAlive ? 1 : 0);
|
setKeepAlive(fd, keepAlive ? 1 : 0);
|
||||||
}
|
}
|
||||||
@ -419,6 +423,7 @@ public final class Socket extends FileDescriptor {
|
|||||||
private static native int getSoError(int fd) throws IOException;
|
private static native int getSoError(int fd) throws IOException;
|
||||||
private static native int getTcpDeferAccept(int fd) throws IOException;
|
private static native int getTcpDeferAccept(int fd) throws IOException;
|
||||||
private static native int isTcpQuickAck(int fd) throws IOException;
|
private static native int isTcpQuickAck(int fd) throws IOException;
|
||||||
|
private static native PeerCredentials getPeerCredentials(int fd) throws IOException;
|
||||||
|
|
||||||
private static native void setKeepAlive(int fd, int keepAlive) throws IOException;
|
private static native void setKeepAlive(int fd, int keepAlive) throws IOException;
|
||||||
private static native void setReceiveBufferSize(int fd, int receiveBufferSize) throws IOException;
|
private static native void setReceiveBufferSize(int fd, int receiveBufferSize) throws IOException;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package io.netty.channel.unix;
|
package io.netty.channel.unix;
|
||||||
|
|
||||||
import io.netty.channel.epoll.Epoll;
|
import io.netty.channel.epoll.Epoll;
|
||||||
|
import java.io.File;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -96,4 +97,30 @@ public class SocketTest {
|
|||||||
socket.close();
|
socket.close();
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeerCreds() throws IOException {
|
||||||
|
Socket s1 = Socket.newSocketDomain();
|
||||||
|
Socket s2 = Socket.newSocketDomain();
|
||||||
|
File domainSocketFile = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
domainSocketFile = File.createTempFile("netty-test", "sckt");
|
||||||
|
DomainSocketAddress dsa = new DomainSocketAddress(domainSocketFile);
|
||||||
|
s1.bind(dsa);
|
||||||
|
s1.listen(1);
|
||||||
|
|
||||||
|
assertTrue(s2.connect(dsa));
|
||||||
|
byte [] addr = new byte[64];
|
||||||
|
s1.accept(addr);
|
||||||
|
PeerCredentials pc = s1.getPeerCredentials();
|
||||||
|
assertNotEquals(pc.uid(), -1);
|
||||||
|
} finally {
|
||||||
|
s1.close();
|
||||||
|
s2.close();
|
||||||
|
if (domainSocketFile != null) {
|
||||||
|
domainSocketFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user