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:
Demetrius 2016-10-22 00:19:37 +00:00 committed by Norman Maurer
parent e3cb9935c0
commit b3fa976028
5 changed files with 147 additions and 1 deletions

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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
+ "]";
}
}

View File

@ -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;

View File

@ -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();
}
}
}
}