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
|
||||
* 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 <errno.h>
|
||||
#include <unistd.h>
|
||||
@ -29,8 +35,10 @@
|
||||
#include "netty_unix_util.h"
|
||||
|
||||
static jclass datagramSocketAddressClass = NULL;
|
||||
static jclass peerCredentialsClass = NULL;
|
||||
static jmethodID datagramSocketAddrMethodId = NULL;
|
||||
static jmethodID inetSocketAddrMethodId = NULL;
|
||||
static jmethodID peerCredentialsMethodId = NULL;
|
||||
static jclass inetSocketAddressClass = NULL;
|
||||
static jclass netUtilClass = NULL;
|
||||
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
||||
@ -647,6 +655,14 @@ static jint netty_unix_socket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd)
|
||||
}
|
||||
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 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 jint dynamicMethodsTableSize() {
|
||||
return fixed_method_table_size + 2;
|
||||
return fixed_method_table_size + 3;
|
||||
}
|
||||
|
||||
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->fnPtr = (void *) netty_unix_socket_recvFromAddress;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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()");
|
||||
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);
|
||||
if (mem == NULL) {
|
||||
@ -826,4 +868,8 @@ void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
|
||||
(*env)->DeleteGlobalRef(env, netUtilClass);
|
||||
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.DomainSocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.PeerCredentials;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDomain;
|
||||
import java.io.IOException;
|
||||
|
||||
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
|
||||
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
|
||||
@ -132,6 +134,14 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
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 {
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
public PeerCredentials getPeerCredentials() throws IOException {
|
||||
return getPeerCredentials(fd);
|
||||
}
|
||||
|
||||
public void setKeepAlive(boolean keepAlive) throws IOException {
|
||||
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 getTcpDeferAccept(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 setReceiveBufferSize(int fd, int receiveBufferSize) throws IOException;
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.channel.unix;
|
||||
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import java.io.File;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -96,4 +97,30 @@ public class SocketTest {
|
||||
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