socket-util: Properly set socket permissions in make_unix_socket().
authorBen Pfaff <blp@nicira.com>
Mon, 18 Apr 2011 18:24:50 +0000 (11:24 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 29 Apr 2011 21:31:59 +0000 (14:31 -0700)
Under Linux, at least, bind and fchmod interact for Unix sockets in a way
that surprised me.  Calling fchmod() on a Unix socket successfully sets the
permissions for the socket's own inode.  But that has no effect on any
inode that has already been created in the file system by bind(), because
that inode is not the same as the one for the Unix socket itself.

However, if you bind() *after* calling fchmod(), then the bind() takes the
permissions for the new inode from the Unix socket inode, which has the
desired effect.

This also adds a more portable fallback for non-Linux systems.

Reported-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
lib/socket-util.c

index 12bbc71..7e4b8be 100644 (file)
@@ -302,6 +302,24 @@ make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len,
     }
 }
 
+/* Binds Unix domain socket 'fd' to a file with permissions 0700. */
+static int
+bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len)
+{
+#ifdef __linux__
+    /* On Linux, calling fchmod() *before* bind() sets permissions for the file
+     * about to be created.  Calling fchmod() *after* bind has no effect on the
+     * file that was created.) */
+    return fchmod(fd, 0700) || bind(fd, sun, sun_len) ? errno : 0;
+#else
+    /* According to _Unix Network Programming_, umask should affect bind(). */
+    mode_t old_umask = umask(0077);
+    int error = bind(fd, sun, sun_len) ? errno : 0;
+    umask(old_umask);
+    return error;
+#endif
+}
+
 /* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or
  * SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and
  * connected to '*connect_path' (if 'connect_path' is non-null).  If 'nonblock'
@@ -348,9 +366,8 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED,
         fatal_signal_add_file_to_unlink(bind_path);
 
         error = make_sockaddr_un(bind_path, &un, &un_len, &dirfd);
-        if (!error && (bind(fd, (struct sockaddr*) &un, un_len)
-                       || fchmod(fd, S_IRWXU))) {
-            error = errno;
+        if (!error) {
+            error = bind_unix_socket(fd, (struct sockaddr *) &un, un_len);
         }
         if (dirfd >= 0) {
             close(dirfd);