Skip to content

Commit e89f511

Browse files
committed
server server UPDATE add API to set unix sock basedir
1 parent c90b82d commit e89f511

File tree

8 files changed

+187
-23
lines changed

8 files changed

+187
-23
lines changed

modules/[email protected]

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,10 +328,10 @@ module libnetconf2-netconf-server {
328328
length "1..107";
329329
}
330330
description
331-
"The explicit filesystem path where the UNIX socket will be bonded.
332-
The parent directory must exist and be writable by the server process.
331+
"Relative filesystem path where the UNIX socket will be bound.
332+
The parent directory must be set by an internal server API setting.
333333
334-
Example: /var/run/netconf.sock";
334+
Example: netconf.sock";
335335
}
336336
}
337337
case hidden-path {

src/server_config.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ nc_server_config_free(struct nc_server_config *config)
360360
struct nc_ch_client *ch_client;
361361
struct nc_ch_endpt *ch_endpt;
362362
LY_ARRAY_COUNT_TYPE i, j;
363-
const char *socket_path = NULL;
363+
char *socket_path = NULL;
364364

365365
if (!config) {
366366
return;
@@ -397,6 +397,11 @@ nc_server_config_free(struct nc_server_config *config)
397397
}
398398
LY_ARRAY_FREE(endpt->binds);
399399

400+
if (endpt->ti == NC_TI_UNIX) {
401+
free(socket_path);
402+
socket_path = NULL;
403+
}
404+
400405
/* free transport specific options */
401406
switch (endpt->ti) {
402407
#ifdef NC_ENABLED_SSH_TLS
@@ -5035,7 +5040,8 @@ static int
50355040
nc_server_config_bindings_match(const struct nc_endpt *e1, const struct nc_bind *b1,
50365041
const struct nc_endpt *e2, const struct nc_bind *b2)
50375042
{
5038-
const char *addr1, *addr2;
5043+
int rc = 1;
5044+
char *addr1 = NULL, *addr2 = NULL;
50395045

50405046
if (e1->ti != e2->ti) {
50415047
/* different transport protocols */
@@ -5052,15 +5058,22 @@ nc_server_config_bindings_match(const struct nc_endpt *e1, const struct nc_bind
50525058
}
50535059
if (!addr1 || !addr2) {
50545060
/* unable to get the address */
5055-
return 0;
5061+
rc = 0;
5062+
goto cleanup;
50565063
}
50575064

50585065
if (strcmp(addr1, addr2) || (b1->port != b2->port)) {
50595066
/* different addresses or ports */
5060-
return 0;
5067+
rc = 0;
5068+
goto cleanup;
50615069
}
50625070

5063-
return 1;
5071+
cleanup:
5072+
if (e1->ti == NC_TI_UNIX) {
5073+
free(addr1);
5074+
free(addr2);
5075+
}
5076+
return rc;
50645077
}
50655078

50665079
/**

src/session_client.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,8 +1485,11 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx)
14851485
session->ti.unixsock.sock = sock;
14861486
sock = -1;
14871487

1488-
/* socket path */
14891488
session->path = strdup(address);
1489+
if (!session->path) {
1490+
ERRMEM;
1491+
goto fail;
1492+
}
14901493

14911494
/* NETCONF username */
14921495
if (unix_opts.username) {

src/session_p.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ struct nc_server_opts {
755755
char *endpt_name; /**< Name of the endpoint using this path. */
756756
char *path; /**< The actual filesystem path for the socket. */
757757
} *unix_paths; /**< Array of UNIX socket paths set via API (sized-array, see libyang docs). */
758+
char *unix_socket_dir; /**< Base directory for UNIX sockets created by the server. */
758759

759760
/* ACCESS unlocked */
760761
ATOMIC_T new_session_id;
@@ -980,7 +981,7 @@ void *nc_realloc(void *ptr, size_t size);
980981
* @param[in] endpt Endpoint to get the socket path for.
981982
* @return Socket path, NULL on error.
982983
*/
983-
const char *nc_server_unix_get_socket_path(const struct nc_endpt *endpt);
984+
char *nc_server_unix_get_socket_path(const struct nc_endpt *endpt);
984985

985986
/**
986987
* @brief Bind and listen on a socket for the given endpoint and its bind.

src/session_server.c

Lines changed: 137 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -358,31 +358,136 @@ nc_sock_listen_inet(const char *address, uint16_t port)
358358
return -1;
359359
}
360360

361-
const char *
361+
/**
362+
* @brief Construct the full path to the UNIX socket.
363+
*
364+
* @param[in] filename Name of the socket file.
365+
* @param[out] path Constructed full path to the UNIX socket (must be freed by the caller).
366+
* @return 0 on success, 1 on error.
367+
*/
368+
static int
369+
nc_session_unix_construct_socket_path(const char *filename, char **path)
370+
{
371+
int rc = 0, is_prefix, is_subdir, is_exact;
372+
char *full_path = NULL, *real_base_dir = NULL, *last_slash = NULL, *sock_dir_path = NULL;
373+
char *real_target_dir = NULL;
374+
struct sockaddr_un sun;
375+
size_t dir_len, base_len;
376+
const char *dir = server_opts.unix_socket_dir;
377+
378+
if (!dir) {
379+
ERR(NULL, "Cannot construct UNIX socket path \"%s\""
380+
" (no base directory set, see nc_set_unix_socket_dir()).", filename);
381+
return 1;
382+
}
383+
384+
if (filename[0] == '/') {
385+
ERR(NULL, "Cannot construct UNIX socket path \"%s\" (absolute path not allowed).", filename);
386+
return 1;
387+
}
388+
389+
/* construct the path to the UNIX socket */
390+
if (asprintf(&full_path, "%s/%s", dir, filename) == -1) {
391+
ERRMEM;
392+
rc = 1;
393+
goto cleanup;
394+
}
395+
396+
if (strlen(full_path) > sizeof(sun.sun_path) - 1) {
397+
ERR(NULL, "Socket path \"%s\" is too long.", full_path);
398+
goto cleanup;
399+
}
400+
401+
/* ensure the socket path is within the base directory */
402+
if (!(real_base_dir = realpath(dir, NULL))) {
403+
ERR(NULL, "realpath() failed for UNIX socket base directory \"%s\" (%s).", dir, strerror(errno));
404+
rc = 1;
405+
goto cleanup;
406+
}
407+
408+
/* find the last slash in the constructed path */
409+
last_slash = strrchr(full_path, '/');
410+
if (last_slash) {
411+
/* extract the directory part of the socket path */
412+
dir_len = last_slash - full_path;
413+
sock_dir_path = strndup(full_path, dir_len);
414+
NC_CHECK_ERRMEM_GOTO(!sock_dir_path, rc = 1, cleanup);
415+
416+
if (!(real_target_dir = realpath(sock_dir_path, NULL))) {
417+
ERR(NULL, "realpath() failed for UNIX socket path directory \"%s\" (%s).", sock_dir_path,
418+
strerror(errno));
419+
rc = 1;
420+
goto cleanup;
421+
}
422+
} else {
423+
/* should not happen as we always add dir/filename */
424+
real_target_dir = strdup(real_base_dir);
425+
NC_CHECK_ERRMEM_GOTO(!real_target_dir, rc = 1, cleanup);
426+
}
427+
428+
base_len = strlen(real_base_dir);
429+
430+
/* check the relationship between both paths */
431+
is_prefix = (strncmp(real_base_dir, real_target_dir, base_len) == 0);
432+
433+
is_exact = (real_target_dir[base_len] == '\0');
434+
435+
is_subdir = (real_target_dir[base_len] == '/');
436+
437+
/* special case if base is '/' */
438+
if ((base_len == 1) && (real_base_dir[0] == '/')) {
439+
is_subdir = 1;
440+
}
441+
442+
if (!is_prefix || (!is_exact && !is_subdir)) {
443+
ERR(NULL, "UNIX socket path \"%s\" escapes the base directory \"%s\".", full_path, dir);
444+
rc = 1;
445+
goto cleanup;
446+
}
447+
448+
/* transfer ownership */
449+
*path = full_path;
450+
full_path = NULL;
451+
452+
cleanup:
453+
free(real_base_dir);
454+
free(real_target_dir);
455+
free(sock_dir_path);
456+
free(full_path);
457+
return rc;
458+
}
459+
460+
char *
362461
nc_server_unix_get_socket_path(const struct nc_endpt *endpt)
363462
{
364463
LY_ARRAY_COUNT_TYPE i;
365-
const char *path = NULL;
464+
const char *filename = NULL;
465+
char *path = NULL;
366466

367467
/* check the endpoints options for type of socket path */
368468
if (endpt->opts.unix->path_type == NC_UNIX_SOCKET_PATH_FILE) {
369469
/* UNIX socket endpoints always have only one bind, get its address */
370-
path = endpt->binds[0].address;
470+
filename = endpt->binds[0].address;
371471
} else if (endpt->opts.unix->path_type == NC_UNIX_SOCKET_PATH_HIDDEN) {
372-
/* serach the mappings */
472+
/* search the mappings */
373473
LY_ARRAY_FOR(server_opts.unix_paths, i) {
374474
if (!strcmp(server_opts.unix_paths[i].endpt_name, endpt->name)) {
375-
path = server_opts.unix_paths[i].path;
475+
filename = server_opts.unix_paths[i].path;
376476
break;
377477
}
378478
}
379-
if (!path) {
479+
if (!filename) {
380480
ERR(NULL, "UNIX socket path mapping for endpoint \"%s\" not found.", endpt->name);
381481
}
382482
} else {
383483
ERRINT;
384484
}
385485

486+
/* construct the full socket path */
487+
if (nc_session_unix_construct_socket_path(filename, &path)) {
488+
return NULL;
489+
}
490+
386491
return path;
387492
}
388493

@@ -543,7 +648,7 @@ nc_sock_accept_binds(struct nc_endpt *endpt, struct nc_bind *binds, uint16_t bin
543648
pthread_mutex_t *bind_lock, int timeout, char **host, uint16_t *port, uint16_t *idx, int *sock)
544649
{
545650
uint16_t i, j, pfd_count, client_port;
546-
char *client_address;
651+
char *client_address, *sockpath = NULL;
547652
struct pollfd *pfd;
548653
struct sockaddr_storage saddr;
549654
socklen_t saddr_len = sizeof(saddr);
@@ -648,7 +753,11 @@ nc_sock_accept_binds(struct nc_endpt *endpt, struct nc_bind *binds, uint16_t bin
648753
}
649754

650755
if (saddr.ss_family == AF_UNIX) {
651-
VRB(NULL, "Accepted a connection on %s.", endpt ? nc_server_unix_get_socket_path(endpt) : "UNIX socket");
756+
if (endpt) {
757+
sockpath = nc_server_unix_get_socket_path(endpt);
758+
}
759+
VRB(NULL, "Accepted a connection on %s.", sockpath ? sockpath : "UNIX socket");
760+
free(sockpath);
652761
} else {
653762
VRB(NULL, "Accepted a connection on %s:%u from %s:%u.", binds[i].address, binds[i].port, client_address, client_port);
654763
}
@@ -2335,7 +2444,7 @@ nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
23352444
int
23362445
nc_server_bind_and_listen(struct nc_endpt *endpt, struct nc_bind *bind)
23372446
{
2338-
const char *unix_path = NULL;
2447+
char *unix_path = NULL;
23392448
int sock = -1, rc = 0;
23402449

23412450
/* start listening on the endpoint */
@@ -2378,6 +2487,7 @@ nc_server_bind_and_listen(struct nc_endpt *endpt, struct nc_bind *bind)
23782487
}
23792488

23802489
cleanup:
2490+
free(unix_path);
23812491
return rc;
23822492
}
23832493

@@ -4382,3 +4492,21 @@ nc_server_get_unix_socket_path(const char *endpoint_name)
43824492
pthread_rwlock_unlock(&server_opts.config_lock);
43834493
return socket_path;
43844494
}
4495+
4496+
API int
4497+
nc_server_set_unix_socket_dir(const char *dir)
4498+
{
4499+
int rc = 0;
4500+
4501+
/* CONFIG WRITE LOCK */
4502+
pthread_rwlock_wrlock(&server_opts.config_lock);
4503+
4504+
free(server_opts.unix_socket_dir);
4505+
server_opts.unix_socket_dir = strdup(dir);
4506+
NC_CHECK_ERRMEM_GOTO(!server_opts.unix_socket_dir, rc = 1, cleanup);
4507+
4508+
cleanup:
4509+
/* CONFIG WRITE UNLOCK */
4510+
pthread_rwlock_unlock(&server_opts.config_lock);
4511+
return rc;
4512+
}

src/session_server.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,17 @@ int nc_server_set_unix_socket_path(const char *endpoint_name, const char *socket
467467
*/
468468
const char *nc_server_get_unix_socket_path(const char *endpoint_name);
469469

470+
/**
471+
* @brief Set the base directory for UNIX socket paths.
472+
*
473+
* All UNIX socket paths will be relative to this directory.
474+
* It must exist and be writable by the server process.
475+
*
476+
* @param[in] dir Base directory for UNIX socket paths.
477+
* @return 0 on success, 1 on error.
478+
*/
479+
int nc_server_set_unix_socket_dir(const char *dir);
480+
470481
/** @} Server Session */
471482

472483
/**

tests/test_config.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,8 +961,12 @@ setup_f(void **state)
961961
ret = nc_server_config_setup_data(tree);
962962
assert_int_equal(ret, 0);
963963

964+
/* set the base directory for UNIX sockets */
965+
ret = nc_server_set_unix_socket_dir("/tmp");
966+
assert_int_equal(ret, 0);
967+
964968
/* set hidden path for UNIX endpoint */
965-
ret = nc_server_set_unix_socket_path("unix", "/tmp/netconf-test-server.sock");
969+
ret = nc_server_set_unix_socket_path("unix", "netconf-test-server.sock");
966970
assert_int_equal(ret, 0);
967971

968972
lyd_free_all(tree);

tests/test_unix_socket.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ setup_glob_f(void **state)
5454
assert_non_null(test_ctx->test_data);
5555
test_ctx->free_test_data = ln2_glob_test_free_test_data;
5656

57+
/* set the base directory for UNIX sockets */
58+
ret = nc_server_set_unix_socket_dir("/tmp");
59+
assert_int_equal(ret, 0);
60+
5761
/* set two hidden paths for UNIX sockets */
58-
ret = nc_server_set_unix_socket_path("unix", "/tmp/nc2_test_unix_sock");
62+
ret = nc_server_set_unix_socket_path("unix", "nc2_test_unix_sock");
5963
assert_int_equal(ret, 0);
60-
ret = nc_server_set_unix_socket_path("unix2", "/tmp/nc2_test_unix_sock2");
64+
ret = nc_server_set_unix_socket_path("unix2", "nc2_test_unix_sock2");
6165
assert_int_equal(ret, 0);
6266

6367
return 0;
@@ -424,7 +428,7 @@ test_cleartext_path(void **state)
424428

425429
/* create the UNIX socket with a different cleartext path */
426430
ret = nc_server_config_add_unix_socket(test_ctx->ctx,
427-
"unix2", "/tmp/nc2_test_cleartext_unix_sock", "0666", NULL, NULL, &config);
431+
"unix2", "nc2_test_cleartext_unix_sock", "0666", NULL, NULL, &config);
428432
assert_int_equal(ret, 0);
429433

430434
ret = nc_server_config_setup_data(config);

0 commit comments

Comments
 (0)