fix: harden the per-user IPC endpoint

This commit is contained in:
2026-08-12 16:08:23 +09:00
parent 4120f90736
commit 432df3730c
9 changed files with 96 additions and 46 deletions

View File

@@ -4,8 +4,6 @@
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "../ipc.h"
typedef struct Key Key;
@@ -81,15 +79,8 @@ loadkeys(char *file)
static void
dial(void)
{
struct sockaddr_un addr;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
fd = ipcconnect();
if(fd < 0)
die("socket");
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
die("connect");
}

View File

@@ -2,8 +2,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <gtk/gtk.h>
#include "ipc.h"
@@ -27,20 +25,9 @@ static GType imtype;
static void
srvconnect(Im *im)
{
struct sockaddr_un addr;
if(im->fd >= 0)
return;
im->fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(im->fd < 0)
return;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
if(connect(im->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
close(im->fd);
im->fd = -1;
}
im->fd = ipcconnect();
}
static void

46
ipc.c
View File

@@ -1,7 +1,10 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "ipc.h"
#if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE)
@@ -21,6 +24,49 @@ getlen(const unsigned char p[Ipclensz])
return p[0] | (p[1] << 8);
}
int
ipcpath(char *dst, size_t cap)
{
const char *dir, *sep;
int n;
if(dst == NULL || cap == 0)
return -1;
dir = getenv("XDG_RUNTIME_DIR");
if(dir != NULL && dir[0] == '/'){
sep = dir[strlen(dir)-1] == '/' ? "" : "/";
n = snprintf(dst, cap, "%s%sstrans.sock", dir, sep);
}else
n = snprintf(dst, cap, "/tmp/strans.%lu",
(unsigned long)getuid());
if(n < 0 || (size_t)n >= cap)
return -1;
return 0;
}
int
ipcconnect(void)
{
struct sockaddr_un addr;
int e, fd;
memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX;
if(ipcpath(addr.sun_path, sizeof addr.sun_path) < 0){
errno = ENAMETOOLONG;
return -1;
}
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(fd < 0)
return -1;
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) == 0)
return fd;
e = errno;
close(fd);
errno = e;
return -1;
}
void
ipcpackreq(unsigned char req[Ipcreqsz], int want, unsigned int mod,
unsigned int key)

4
ipc.h
View File

@@ -3,8 +3,6 @@
#include <stddef.h>
#define IPCPATH "/tmp/strans.%d"
/*
* Request: [flags, modifiers, key byte 0, ..., key byte 3].
* Flags request preedit and distinguish lifecycle reset from physical Escape.
@@ -61,5 +59,7 @@ int ipcpackresp(unsigned char*, size_t, int, const char*, size_t, const char*, s
int ipcreadn(int, void*, size_t);
int ipcsend(int, const void*, size_t);
int ipcreadresp(int, int, char*, size_t, char*, size_t, Ipcresp*);
int ipcpath(char*, size_t);
int ipcconnect(void);
#endif

17
srv.c
View File

@@ -1,7 +1,10 @@
#include "dat.h"
#include "fn.h"
static char adir[40];
#include <sys/stat.h>
#include <sys/un.h>
static char adir[256];
static Channel *clientc;
typedef struct Client Client;
@@ -71,12 +74,16 @@ clientthread(void *arg)
static void
srvinit(void)
{
char addr[64];
char addr[128], path[sizeof(((struct sockaddr_un*)0)->sun_path)];
snprint(addr, sizeof(addr), "unix!" IPCPATH, getuid());
remove(addr + 5);
if(ipcpath(path, sizeof path) < 0)
die("IPC path is too long");
if(snprint(addr, sizeof addr, "unix!%s", path) >= (int)sizeof addr)
die("IPC address is too long");
if(announce(addr, adir) < 0)
die("announce: %r");
die("IPC endpoint is already in use: %r");
if(chmod(path, 0600) < 0)
die("can't protect IPC endpoint: %s", path);
}
void

View File

@@ -1,3 +1,7 @@
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include "test.h"
void
@@ -21,3 +25,29 @@ ipc_masks_modifiers(struct ct *t)
CT_EQ_UINT(t, 0, key);
CT_CHECK(t, ipcreqreset(buf));
}
void
ipc_runtime_path(struct ct *t)
{
char buf[128], fallback[128], *old, *saved;
old = getenv("XDG_RUNTIME_DIR");
saved = old == nil ? nil : strdup(old);
setenv("XDG_RUNTIME_DIR", "/tmp/strans-runtime-test", 1);
CT_EQ_INT(t, 0, ipcpath(buf, sizeof buf));
CT_EQ_STR(t, "/tmp/strans-runtime-test/strans.sock", buf);
setenv("XDG_RUNTIME_DIR", "/tmp/strans-runtime-test/", 1);
CT_EQ_INT(t, 0, ipcpath(buf, sizeof buf));
CT_EQ_STR(t, "/tmp/strans-runtime-test/strans.sock", buf);
setenv("XDG_RUNTIME_DIR", "relative", 1);
CT_EQ_INT(t, 0, ipcpath(buf, sizeof buf));
snprint(fallback, sizeof fallback, "/tmp/strans.%d", getuid());
CT_EQ_STR(t, fallback, buf);
CT_EQ_INT(t, -1, ipcpath(buf, 4));
CT_EQ_INT(t, -1, ipcpath(nil, sizeof buf));
if(saved != nil){
setenv("XDG_RUNTIME_DIR", saved, 1);
free(saved);
}else
unsetenv("XDG_RUNTIME_DIR");
}

View File

@@ -64,5 +64,6 @@ void dictionary_candidates(struct ct*);
void dictionary_misses_clear_result(struct ct*);
void dictionary_emoji_identity(struct ct*);
void ipc_masks_modifiers(struct ct*);
void ipc_runtime_path(struct ct*);
#endif

View File

@@ -113,6 +113,7 @@ static const struct ct_test tests[] = {
{ "dict/misses-clear-result", dictionary_misses_clear_result },
{ "dict/emoji-identity", dictionary_emoji_identity },
{ "ipc/masks-modifiers", ipc_masks_modifiers },
{ "ipc/runtime-path", ipc_runtime_path },
};
void

View File

@@ -2,8 +2,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
#include <xcb-imdkit/imdkit.h>
@@ -119,22 +117,11 @@ srvclose(Ic *state)
static int
srvconnect(Ic *state)
{
struct sockaddr_un addr;
int fd;
if(state->fd >= 0)
return 0;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(fd < 0)
state->fd = ipcconnect();
if(state->fd < 0)
return -1;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), IPCPATH, getuid());
if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
close(fd);
return -1;
}
state->fd = fd;
return 0;
}