<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Actually I'm stupid, the stat() call is required and should
      happen after realpath() because it's required for
      selabel_lookup(): the expected context may differ based on the
      type of the inode.<br>
    </p>
    <div class="moz-cite-prefix">On 12/29/21 09:52, Renaud Métrich
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:472d375e-8f3e-ac89-7d5c-372968e5caf0@redhat.com">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <p>See inline<br>
      </p>
      <div class="moz-cite-prefix">On 12/29/21 00:45, Dmitry V. Levin
        wrote:<br>
      </div>
      <blockquote type="cite"
        cite="mid:20211228234553.GA17229@altlinux.org">
        <pre class="moz-quote-pre" wrap="">On Tue, Dec 28, 2021 at 01:12:16PM +0100, Renaud Métrich wrote:
[...]
</pre>
        <blockquote type="cite">
          <pre class="moz-quote-pre" wrap="">diff --git a/m4/st_selinux.m4 b/m4/st_selinux.m4
index 8b524896d..09a15e9d2 100644
--- a/m4/st_selinux.m4
+++ b/m4/st_selinux.m4
@@ -48,6 +48,24 @@ AS_IF([test "x$with_libselinux" != xno],
                     )
                    ]
              )
+             AC_CHECK_LIB([selinux],[selabel_open],
+               [libselinux_LIBS="-lselinux"
+                enable_secontext=yes
+               ],
+               [if test "x$with_libselinux" != xcheck; then
+                  AC_MSG_FAILURE([failed to find selabel_open in libselinux])
+                fi
+               ]
+             )
+             AC_CHECK_LIB([selinux],[selabel_lookup],
+               [libselinux_LIBS="-lselinux"
+                enable_secontext=yes
+               ],
+               [if test "x$with_libselinux" != xcheck; then
+                  AC_MSG_FAILURE([failed to find selabel_lookup in libselinux])
+                fi
+               ]
+             )
              LDFLAGS="$saved_LDFLAGS"
             ],
             [AS_IF([test "x$with_libselinux" != xcheck],
</pre>
        </blockquote>
        <pre class="moz-quote-pre" wrap="">After commit v5.15~18 this could be re-written as follows:

--- a/m4/st_selinux.m4
+++ b/m4/st_selinux.m4
@@ -35,7 +35,7 @@ AS_IF([test "x$with_libselinux" != xno],
             [saved_LDFLAGS="$LDFLAGS"
              LDFLAGS="$LDFLAGS $libselinux_LDFLAGS"
              missing=
-             for func in getpidcon getfilecon; do
+             for func in getpidcon getfilecon selabel_open selabel_lookup; do
                AC_CHECK_LIB([selinux], [$func], [:],
                             [missing="$missing $func"])
              done
</pre>
      </blockquote>
      OK<br>
      <blockquote type="cite"
        cite="mid:20211228234553.GA17229@altlinux.org">
        <pre class="moz-quote-pre" wrap="">[...]
</pre>
        <blockquote type="cite">
          <pre class="moz-quote-pre" wrap="">+static int
+get_expected_filecontext(const char *path, char **result)
+{
+       static struct selabel_handle *hdl = NULL;
+       static bool disabled = false;
+
+       if (disabled)
+               return -1;
+
+       if (!hdl) {
+               hdl = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+               if (!hdl) {
+                       error_msg("Could not open SELinux database, disabling "
+                                 "context mismatch checking: %s",
+                                 strerror(errno));
</pre>
        </blockquote>
        <pre class="moz-quote-pre" wrap="">perror_msg should be used instead of error_msg(..., strerror(errno)).
</pre>
      </blockquote>
      OK<br>
      <blockquote type="cite"
        cite="mid:20211228234553.GA17229@altlinux.org">
        <blockquote type="cite">
          <pre class="moz-quote-pre" wrap="">+                        disabled = true;
+                       return -1;
+               }
+       }
+
+       /*
+        * We need to fully resolve the path, because selabel_lookup() isn't
+        * smart enough to automatically resolve
+        */
+
+       char *resolved = realpath(path, NULL);
+       if (!resolved)
+               return -1;
</pre>
        </blockquote>
        <pre class="moz-quote-pre" wrap="">realpath looks like overkill, it may issue quite a few syscalls while
a simple readlink should be enough to resolve the symlink in /proc/.
</pre>
      </blockquote>
      <p>Unfortunately readlink() is not working here because readlink()
        doesn't resolve fully but selabel_lookup() really requires
        knowing the path, because it just checks in its database for the
        corresponding regex.</p>
      <p>Example:</p>
      <pre>$ cd /tmp
$ ln -s /home/rmetrich symlinkdir
$ touch /home/rmetrich/bar
$ ln -s /tmp/symlinkdir/bar
$ matchpathcon $(readlink bar)
/tmp/symlinkdir/bar     <<none>>

---> WRONG

$ matchpathcon $(realpath bar)
/home/rmetrich/bar      unconfined_u:object_r:user_home_t:s0

---> RIGHT
</pre>
      <blockquote type="cite"
        cite="mid:20211228234553.GA17229@altlinux.org">
        <blockquote type="cite">
          <pre class="moz-quote-pre" wrap="">+        struct stat statbuf;
+       if (stat(resolved, &statbuf) == -1) {
+               free(resolved);
+               return -1;
+       }
</pre>
        </blockquote>
      </blockquote>
      Actually I think stat() is not needed, if realpath() fails because
      the file doesn't exist/isn't accessible, we will return in error
      as well.<br>
      <blockquote type="cite"
        cite="mid:20211228234553.GA17229@altlinux.org">
        <pre class="moz-quote-pre" wrap="">Maybe stat should be called before readlink.


</pre>
      </blockquote>
    </blockquote>
  </body>
</html>