summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAlejandro Colomar <alx.manpages@gmail.com>2022-07-28 16:10:32 +0200
committerAlejandro Colomar <alx.manpages@gmail.com>2022-08-18 18:55:14 +0200
commitd8e0768a5bae991b96328f469b8e27d8a51da9d6 (patch)
tree37e220c8e125da6eb683a0bae546cfe3c3b25ece
parent1c2f070ee226256a05ced1eaae08196a375a53b6 (diff)
downloadunit-d8e0768a5bae991b96328f469b8e27d8a51da9d6.tar.gz
unit-d8e0768a5bae991b96328f469b8e27d8a51da9d6.tar.bz2
Fixed support for abstract Unix sockets.
Unix domain sockets are normally backed by files in the filesystem. This has historically been problematic when closing and opening again such sockets, since SO_REUSEADDR is ignored for Unix sockets (POSIX left the behavior of SO_REUSEADDR as implementation-defined, and most --if not all-- implementations decided to just ignore this flag). Many solutions are available for this problem, but all of them have important caveats: - unlink(2) the file when it's not needed anymore. This is not easy, because the process that controls the fd may not be the same process that created the file, and may not have file permissions to remove it. Further solutions can be applied to that caveat: - unlink(2) the file right after creation. This will remove the pathname from the filesystem without closing the socket (it will continue to live until the last fd is closed). This is not useful for us, since we need the pathname of the socket as its interface. - chown(2) or chmod(2) the directory that contains the socket. For removing a file from the filesystem, a process needs write permissions in the containing directory. We could put sockets in dummy directories that can be chown(2)ed to nobody. This could be dangerous, though, as we don't control the socket names. It is our users who configure the socket name in their configuration, and so it's easy that they don't understand the many implications of not chosing an appropriate socket pathname. A user could unknowingly put the socket in a directory that is not supposed to be owned by user nobody, and if we blindly chown(2) or chmod(2) the directory, we could be creating a big security hole. - Ask the main process to remove the socket. This would require a very complex communication mechanism with the main process, which is not impossible, but let's avoid it if there are simpler solutions. - Give the child process the CAP_DAC_OVERRIDE capability. That is one of the most powerful capabilities. A process with that capability can be considered root for most practical aspects. Even if the capability is disabled for most of the lifetime of the process, there's a slight chance that a malicious actor could activate it and then easily do serious damage to the system. - unlink(2) the file right before calling bind(2). This is dangerous because another process (for example, another running instance of unitd(8)), could be using the socket, and removing the pathname from the filesystem would be problematic. To do this correctly, a lot of checks should be added before the actual unlink(2), which is error-prone, and difficult to do correctly, and atomically. - Use abstract-namespace Unix domain sockets. This is the simplest solution, as it only requires accepting a slightly different syntax (basically a @ prefix) for the socket name, to transform it into a string starting with a null byte ('\0') that the kernel can understand. The patch is minimal. Since abstract sockets live in an abstract namespace, they don't create files in the filesystem, so there's no need to remove them later. The kernel removes the name when the last fd to it has been closed. One caveat is that only Linux currently supports this kind of Unix sockets. Of course, a solution to that could be to ask other kernels to implement such a feature. Another caveat is that filesystem permissions can't be used to control access to the socket file (since, of course, there's no file). Anyone knowing the socket name can access to it. The only method to control access to it is by using network_namespaces(7). Since in unitd(8) we're using 0666 file sockets, abstract sockets should be no more insecure than that (anyone can already read/write to the listener sockets). - Ask the kernel to implement a simpler way to unlink(2) socket files when they are not needed anymore. I've suggested that to the <linux-fsdevel@vger.kernel.org> mailing list, in: <lore.kernel.org/linux-fsdevel/0bc5f919-bcfd-8fd0-a16b-9f060088158a@gmail.com/T> In this commit, I decided to go for the easiest/simplest solution, which is abstract sockets. In fact, we already had partial support. This commit only fixes some small bug in the existing code so that abstract Unix sockets work: - Don't chmod(2) the socket if it's an abstract one. This fixes the creation of abstract sockets, but doesn't make them usable, since we produce them with a trailing '\0' in their name. That will be fixed in the following commit. This closes #669 issue on GitHub.
-rw-r--r--docs/changes.xml6
-rw-r--r--src/nxt_main_process.c4
2 files changed, 9 insertions, 1 deletions
diff --git a/docs/changes.xml b/docs/changes.xml
index f4f5dbf0..27fffc4c 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -39,6 +39,12 @@ increased the applications' startup timeout.
<change type="feature">
<para>
+supporting abstract UNIX sockets.
+</para>
+</change>
+
+<change type="feature">
+<para>
supporting UNIX sockets in address matching.
</para>
</change>
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index 03761a10..39a8e112 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -1187,7 +1187,9 @@ nxt_main_listening_socket(nxt_sockaddr_t *sa, nxt_listening_socket_t *ls)
#if (NXT_HAVE_UNIX_DOMAIN)
- if (sa->u.sockaddr.sa_family == AF_UNIX) {
+ if (sa->u.sockaddr.sa_family == AF_UNIX
+ && sa->u.sockaddr_un.sun_path[0] != '\0')
+ {
char *filename;
mode_t access;